feat(api): add /info and /stream/* endpoints
This commit is contained in:
parent
5bf62dd9cb
commit
12d2d4b15a
|
@ -8,6 +8,9 @@ class ApiClient(AbstractApiClient):
|
|||
super().__init__(base_url=base_url)
|
||||
self.session.headers.update({"Authorization": f"Api-Key {api_key}"})
|
||||
|
||||
def get_info(self, **kwargs) -> Response:
|
||||
return self._request("GET", "/api/v2/info", **kwargs)
|
||||
|
||||
def get_version(self, **kwargs) -> Response:
|
||||
return self._request("GET", "/api/v2/version", **kwargs)
|
||||
|
||||
|
@ -31,3 +34,9 @@ class ApiClient(AbstractApiClient):
|
|||
|
||||
def download_file(self, item_id: int, **kwargs) -> Response:
|
||||
return self._request("GET", f"/api/v2/files/{item_id}/download", **kwargs)
|
||||
|
||||
def get_stream_preferences(self, **kwargs) -> Response:
|
||||
return self._request("GET", "/api/v2/stream/preferences", **kwargs)
|
||||
|
||||
def get_stream_state(self, **kwargs) -> Response:
|
||||
return self._request("GET", "/api/v2/stream/state", **kwargs)
|
||||
|
|
|
@ -1,4 +1,45 @@
|
|||
from enum import Enum
|
||||
|
||||
from django.db import models
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SitePreferences(BaseModel):
|
||||
station_name: str
|
||||
|
||||
|
||||
class MessageFormatKind(int, Enum):
|
||||
ARTIST_TITLE = 0
|
||||
SHOW_ARTIST_TITLE = 1
|
||||
RADIO_SHOW = 2
|
||||
|
||||
|
||||
class StreamPreferences(BaseModel):
|
||||
input_fade_transition: float
|
||||
message_format: MessageFormatKind
|
||||
message_offline: str
|
||||
# input_auto_switch_off: bool
|
||||
# input_auto_switch_on: bool
|
||||
# input_main_user: str
|
||||
# input_main_password: str
|
||||
# replay_gain_enabled: bool
|
||||
# replay_gain_offset: float
|
||||
# track_fade_in: float
|
||||
# track_fade_out: float
|
||||
# track_fade_transition: float
|
||||
|
||||
|
||||
class StreamState(BaseModel):
|
||||
input_main_connected: bool
|
||||
input_main_streaming: bool
|
||||
input_show_connected: bool
|
||||
input_show_streaming: bool
|
||||
schedule_streaming: bool
|
||||
|
||||
|
||||
class SitePreferenceManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(user__isnull=True)
|
||||
|
||||
|
||||
class Preference(models.Model):
|
||||
|
@ -22,6 +63,36 @@ class Preference(models.Model):
|
|||
db_column="valstr",
|
||||
)
|
||||
|
||||
objects = models.Manager()
|
||||
site = SitePreferenceManager()
|
||||
|
||||
@classmethod
|
||||
def get_site_preferences(cls) -> SitePreferences:
|
||||
entries = dict(cls.site.values_list("key", "value"))
|
||||
return SitePreferences(
|
||||
station_name=entries.get("station_name") or "LibreTime",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_stream_preferences(cls) -> StreamPreferences:
|
||||
entries = dict(cls.site.values_list("key", "value"))
|
||||
return StreamPreferences(
|
||||
input_fade_transition=float(entries.get("default_transition_fade") or 0.0),
|
||||
message_format=int(entries.get("stream_label_format") or 0),
|
||||
message_offline=entries.get("off_air_meta") or "Offline",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_stream_state(cls) -> StreamState:
|
||||
entries = dict(cls.site.values_list("key", "value"))
|
||||
return StreamState(
|
||||
input_main_connected=entries.get("master_dj") == "true",
|
||||
input_main_streaming=entries.get("master_dj_switch") == "on",
|
||||
input_show_connected=entries.get("live_dj") == "true",
|
||||
input_show_streaming=entries.get("live_dj_switch") == "on",
|
||||
schedule_streaming=entries.get("scheduled_play_switch") == "on",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = "cc_pref"
|
||||
|
|
|
@ -3,10 +3,12 @@ from rest_framework import routers
|
|||
|
||||
from .views import (
|
||||
CeleryTaskViewSet,
|
||||
InfoView,
|
||||
LoginAttemptViewSet,
|
||||
PreferenceViewSet,
|
||||
ServiceRegisterViewSet,
|
||||
StreamSettingViewSet,
|
||||
StreamPreferencesView,
|
||||
StreamStateView,
|
||||
ThirdPartyTrackReferenceViewSet,
|
||||
UserTokenViewSet,
|
||||
UserViewSet,
|
||||
|
@ -17,7 +19,6 @@ router = routers.DefaultRouter(trailing_slash=False)
|
|||
router.register("login-attempts", LoginAttemptViewSet)
|
||||
router.register("preferences", PreferenceViewSet)
|
||||
router.register("service-registers", ServiceRegisterViewSet)
|
||||
router.register("stream-settings", StreamSettingViewSet)
|
||||
router.register("users", UserViewSet)
|
||||
router.register("user-tokens", UserTokenViewSet)
|
||||
router.register("celery-tasks", CeleryTaskViewSet)
|
||||
|
@ -25,5 +26,8 @@ router.register("third-party-track-references", ThirdPartyTrackReferenceViewSet)
|
|||
|
||||
urls = [
|
||||
*router.urls,
|
||||
path("info", InfoView.as_view()),
|
||||
path("version", VersionView.as_view()),
|
||||
path("stream/preferences", StreamPreferencesView.as_view()),
|
||||
path("stream/state", StreamStateView.as_view()),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from .auth import LoginAttemptSerializer, UserTokenSerializer
|
||||
from .info import VersionSerializer
|
||||
from .preference import PreferenceSerializer, StreamSettingSerializer
|
||||
from .info import InfoSerializer, VersionSerializer
|
||||
from .preference import PreferenceSerializer
|
||||
from .service import ServiceRegisterSerializer
|
||||
from .stream import StreamPreferencesSerializer, StreamStateSerializer
|
||||
from .user import UserSerializer
|
||||
from .worker import CeleryTaskSerializer, ThirdPartyTrackReferenceSerializer
|
||||
|
|
|
@ -3,4 +3,9 @@ from rest_framework import serializers
|
|||
|
||||
# pylint: disable=abstract-method
|
||||
class VersionSerializer(serializers.Serializer):
|
||||
api_version = serializers.CharField()
|
||||
api_version = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class InfoSerializer(serializers.Serializer):
|
||||
station_name = serializers.CharField(read_only=True)
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from ..models import Preference, StreamSetting
|
||||
from ..models import Preference
|
||||
|
||||
|
||||
class PreferenceSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Preference
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class StreamSettingSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = StreamSetting
|
||||
fields = "__all__"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class StreamPreferencesSerializer(serializers.Serializer):
|
||||
input_fade_transition = serializers.FloatField(read_only=True)
|
||||
message_format = serializers.IntegerField(read_only=True)
|
||||
message_offline = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
class StreamStateSerializer(serializers.Serializer):
|
||||
input_main_connected = serializers.BooleanField(read_only=True)
|
||||
input_main_streaming = serializers.BooleanField(read_only=True)
|
||||
input_show_connected = serializers.BooleanField(read_only=True)
|
||||
input_show_streaming = serializers.BooleanField(read_only=True)
|
||||
schedule_streaming = serializers.BooleanField(read_only=True)
|
|
@ -0,0 +1,31 @@
|
|||
from libretime_api.core.models.preference import Preference
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_site_preferences(db):
|
||||
result = Preference.get_site_preferences()
|
||||
assert result.dict() == {
|
||||
"station_name": "LibreTime",
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_stream_preferences(db):
|
||||
result = Preference.get_stream_preferences()
|
||||
assert result.dict() == {
|
||||
"input_fade_transition": 0.0,
|
||||
"message_format": 0,
|
||||
"message_offline": "LibreTime - offline",
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_stream_state(db):
|
||||
result = Preference.get_stream_state()
|
||||
assert result.dict() == {
|
||||
"input_main_connected": False,
|
||||
"input_main_streaming": False,
|
||||
"input_show_connected": False,
|
||||
"input_show_streaming": False,
|
||||
"schedule_streaming": True,
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_version_get(db, api_client: APIClient):
|
||||
response = api_client.get("/api/v2/version")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"api_version": "2.0.0",
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_info_get(db, api_client: APIClient):
|
||||
response = api_client.get("/api/v2/info")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"station_name": "LibreTime",
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_stream_preferences_get(db, api_client: APIClient):
|
||||
response = api_client.get("/api/v2/stream/preferences")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"input_fade_transition": 0.0,
|
||||
"message_format": 0,
|
||||
"message_offline": "LibreTime - offline",
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_stream_state_get(db, api_client: APIClient):
|
||||
response = api_client.get("/api/v2/stream/state")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"input_main_connected": False,
|
||||
"input_main_streaming": False,
|
||||
"input_show_connected": False,
|
||||
"input_show_streaming": False,
|
||||
"schedule_streaming": True,
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
from .auth import LoginAttemptViewSet, UserTokenViewSet
|
||||
from .info import VersionView
|
||||
from .preference import PreferenceViewSet, StreamSettingViewSet
|
||||
from .info import InfoView, VersionView
|
||||
from .preference import PreferenceViewSet
|
||||
from .service import ServiceRegisterViewSet
|
||||
from .stream import StreamPreferencesView, StreamStateView
|
||||
from .user import UserViewSet
|
||||
from .worker import CeleryTaskViewSet, ThirdPartyTrackReferenceViewSet
|
||||
|
|
|
@ -3,7 +3,8 @@ from rest_framework.permissions import AllowAny
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..serializers import VersionSerializer
|
||||
from ..models import Preference
|
||||
from ..serializers import InfoSerializer, VersionSerializer
|
||||
|
||||
|
||||
class VersionView(APIView):
|
||||
|
@ -12,3 +13,18 @@ class VersionView(APIView):
|
|||
|
||||
def get(self, request):
|
||||
return Response({"api_version": settings.API_VERSION})
|
||||
|
||||
|
||||
class InfoView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
serializer_class = InfoSerializer
|
||||
|
||||
def get(self, request):
|
||||
data = Preference.get_site_preferences()
|
||||
return Response(
|
||||
data.dict(
|
||||
include={
|
||||
"station_name",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from ..models import Preference, StreamSetting
|
||||
from ..serializers import PreferenceSerializer, StreamSettingSerializer
|
||||
from ..models import Preference
|
||||
from ..serializers import PreferenceSerializer
|
||||
|
||||
|
||||
class PreferenceViewSet(viewsets.ModelViewSet):
|
||||
queryset = Preference.objects.all()
|
||||
serializer_class = PreferenceSerializer
|
||||
model_permission_name = "preference"
|
||||
|
||||
|
||||
class StreamSettingViewSet(viewsets.ModelViewSet):
|
||||
queryset = StreamSetting.objects.all()
|
||||
serializer_class = StreamSettingSerializer
|
||||
model_permission_name = "streamsetting"
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
from rest_framework import views
|
||||
from rest_framework.response import Response
|
||||
|
||||
from ...permissions import IsSystemTokenOrUser
|
||||
from ..models import Preference
|
||||
from ..serializers import StreamPreferencesSerializer, StreamStateSerializer
|
||||
|
||||
|
||||
class StreamPreferencesView(views.APIView):
|
||||
permission_classes = [IsSystemTokenOrUser]
|
||||
serializer_class = StreamPreferencesSerializer
|
||||
model_permission_name = "streamsetting"
|
||||
|
||||
def get(self, request):
|
||||
data = Preference.get_stream_preferences()
|
||||
return Response(
|
||||
data.dict(
|
||||
include={
|
||||
"input_fade_transition",
|
||||
"message_format",
|
||||
"message_offline",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class StreamStateView(views.APIView):
|
||||
permission_classes = [IsSystemTokenOrUser]
|
||||
serializer_class = StreamStateSerializer
|
||||
model_permission_name = "streamsetting"
|
||||
|
||||
def get(self, request):
|
||||
data = Preference.get_stream_state()
|
||||
return Response(
|
||||
data.dict(
|
||||
include={
|
||||
"input_main_connected",
|
||||
"input_main_streaming",
|
||||
"input_show_connected",
|
||||
"input_show_streaming",
|
||||
"schedule_streaming",
|
||||
}
|
||||
)
|
||||
)
|
220
api/schema.yml
220
api/schema.yml
|
@ -467,6 +467,22 @@ paths:
|
|||
responses:
|
||||
"204":
|
||||
description: No response body
|
||||
/api/v2/info:
|
||||
get:
|
||||
operationId: info_retrieve
|
||||
tags:
|
||||
- info
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
- {}
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Info"
|
||||
description: ""
|
||||
/api/v2/libraries:
|
||||
get:
|
||||
operationId: libraries_list
|
||||
|
@ -4326,11 +4342,11 @@ paths:
|
|||
responses:
|
||||
"204":
|
||||
description: No response body
|
||||
/api/v2/stream-settings:
|
||||
/api/v2/stream/preferences:
|
||||
get:
|
||||
operationId: stream_settings_list
|
||||
operationId: stream_preferences_retrieve
|
||||
tags:
|
||||
- stream-settings
|
||||
- stream
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
|
@ -4339,48 +4355,13 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
$ref: "#/components/schemas/StreamPreferences"
|
||||
description: ""
|
||||
post:
|
||||
operationId: stream_settings_create
|
||||
tags:
|
||||
- stream-settings
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
"201":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
description: ""
|
||||
/api/v2/stream-settings/{key}:
|
||||
/api/v2/stream/state:
|
||||
get:
|
||||
operationId: stream_settings_retrieve
|
||||
parameters:
|
||||
- in: path
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
description: A unique value identifying this stream setting.
|
||||
required: true
|
||||
operationId: stream_state_retrieve
|
||||
tags:
|
||||
- stream-settings
|
||||
- stream
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
|
@ -4389,90 +4370,8 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
$ref: "#/components/schemas/StreamState"
|
||||
description: ""
|
||||
put:
|
||||
operationId: stream_settings_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
description: A unique value identifying this stream setting.
|
||||
required: true
|
||||
tags:
|
||||
- stream-settings
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
description: ""
|
||||
patch:
|
||||
operationId: stream_settings_partial_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
description: A unique value identifying this stream setting.
|
||||
required: true
|
||||
tags:
|
||||
- stream-settings
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedStreamSetting"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedStreamSetting"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedStreamSetting"
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StreamSetting"
|
||||
description: ""
|
||||
delete:
|
||||
operationId: stream_settings_destroy
|
||||
parameters:
|
||||
- in: path
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
description: A unique value identifying this stream setting.
|
||||
required: true
|
||||
tags:
|
||||
- stream-settings
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
"204":
|
||||
description: No response body
|
||||
/api/v2/third-party-track-references:
|
||||
get:
|
||||
operationId: third_party_track_references_list
|
||||
|
@ -5684,6 +5583,14 @@ components:
|
|||
- id
|
||||
- override_album
|
||||
- podcast
|
||||
Info:
|
||||
type: object
|
||||
properties:
|
||||
station_name:
|
||||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- station_name
|
||||
Library:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -6708,19 +6615,6 @@ components:
|
|||
readOnly: true
|
||||
podcast:
|
||||
type: integer
|
||||
PatchedStreamSetting:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
maxLength: 64
|
||||
raw_value:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 255
|
||||
type:
|
||||
type: string
|
||||
maxLength: 16
|
||||
PatchedThirdPartyTrackReference:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7579,22 +7473,47 @@ components:
|
|||
required:
|
||||
- id
|
||||
- podcast
|
||||
StreamSetting:
|
||||
StreamPreferences:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
input_fade_transition:
|
||||
type: number
|
||||
format: double
|
||||
readOnly: true
|
||||
message_format:
|
||||
type: integer
|
||||
readOnly: true
|
||||
message_offline:
|
||||
type: string
|
||||
maxLength: 64
|
||||
raw_value:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 255
|
||||
type:
|
||||
type: string
|
||||
maxLength: 16
|
||||
readOnly: true
|
||||
required:
|
||||
- key
|
||||
- type
|
||||
- input_fade_transition
|
||||
- message_format
|
||||
- message_offline
|
||||
StreamState:
|
||||
type: object
|
||||
properties:
|
||||
input_main_connected:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
input_main_streaming:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
input_show_connected:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
input_show_streaming:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
schedule_streaming:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
required:
|
||||
- input_main_connected
|
||||
- input_main_streaming
|
||||
- input_show_connected
|
||||
- input_show_streaming
|
||||
- schedule_streaming
|
||||
ThirdPartyTrackReference:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7714,6 +7633,7 @@ components:
|
|||
properties:
|
||||
api_version:
|
||||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- api_version
|
||||
Webstream:
|
||||
|
|
Loading…
Reference in New Issue