streamer: integrate, fix, ui change
This commit is contained in:
parent
a64c850efa
commit
87692c860b
|
@ -24,6 +24,7 @@ Usefull context:
|
|||
{
|
||||
"imports": {
|
||||
"vue": "{{vue_url}}"
|
||||
{% block assets-import-map %}{% endblock %}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -70,14 +70,14 @@ class Connector:
|
|||
data = bytes("".join([str(d) for d in data]) + "\n", encoding="utf-8")
|
||||
try:
|
||||
self.socket.sendall(data)
|
||||
data = ""
|
||||
while not response_re.search(data):
|
||||
data += self.socket.recv(1024).decode("utf-8")
|
||||
resp = ""
|
||||
while not response_re.search(resp):
|
||||
resp += self.socket.recv(1024).decode("utf-8")
|
||||
|
||||
if data:
|
||||
data = response_re.sub(r"\1", data).strip()
|
||||
data = self.parse(data) if parse else self.parse_json(data) if parse_json else data
|
||||
return data
|
||||
if resp:
|
||||
resp = response_re.sub(r"\1", resp).strip()
|
||||
resp = self.parse(resp) if parse else self.parse_json(resp) if parse_json else resp
|
||||
return resp
|
||||
except Exception:
|
||||
self.close()
|
||||
if try_count > 0:
|
||||
|
|
|
@ -131,7 +131,7 @@ class QueueSource(Source):
|
|||
def push(self, *paths):
|
||||
"""Add the provided paths to source's play queue."""
|
||||
for path in paths:
|
||||
self.controller.send(f"{self.id}_queue.push {path}")
|
||||
print(self.controller.send(f"{self.id}_queue.push {path}"))
|
||||
|
||||
def fetch(self):
|
||||
super().fetch()
|
||||
|
|
|
@ -19,7 +19,7 @@ end
|
|||
|
||||
{# Transition to live sources #}
|
||||
def to_live(stream, live)
|
||||
stream = fade.final(duration=2., type='log', stream)
|
||||
stream = fade.out(duration=2., type='log', stream)
|
||||
live = fade.initial(duration=2., type='log', live)
|
||||
add(normalize=false, [stream,live])
|
||||
end
|
||||
|
@ -31,12 +31,12 @@ def to_stream(live, stream)
|
|||
end
|
||||
|
||||
{# Skip command #}
|
||||
def add_skip_command(s) =
|
||||
def add_skip_command(id, s) =
|
||||
def skip(_) =
|
||||
source.skip(s)
|
||||
"Done!"
|
||||
end
|
||||
server.register(namespace="#{source.id(s)}",
|
||||
server.register(namespace=id,
|
||||
usage="skip",
|
||||
description="Skip the current song.",
|
||||
"skip",skip)
|
||||
|
@ -58,9 +58,8 @@ def interactive (id, s) =
|
|||
usage="remaining",
|
||||
"remaining", fun (_) -> begin json.stringify(source.remaining(s)) end)
|
||||
|
||||
add_skip_command(s)
|
||||
add_skip_command(id, s)
|
||||
|
||||
{# metadata: create an interactive variable as "{id}_meta" #}
|
||||
s_meta = interactive.string("#{id}_meta", "")
|
||||
s = source.on_metadata(s, fun(meta) -> s_meta.set(json.stringify(meta)))
|
||||
|
||||
|
@ -82,10 +81,7 @@ set("server.socket", true)
|
|||
set("server.socket.path", "{{ streamer.socket_path }}")
|
||||
set("log.file.path", "{{ log_file }}")
|
||||
{% endblock %}
|
||||
|
||||
{% block config_extras %}
|
||||
{% endblock %}
|
||||
|
||||
{% block config_extras %}{% endblock %}
|
||||
|
||||
{% block sources %}
|
||||
{% with source=streamer.dealer %}
|
||||
|
@ -94,7 +90,6 @@ live = audio_to_stereo(interactive('{{ source.id }}',
|
|||
))
|
||||
{% endwith %}
|
||||
|
||||
|
||||
streams = rotate(id="streams", [
|
||||
{% for source in streamer.sources %}
|
||||
{% if source != streamer.dealer %}
|
||||
|
|
107
aircox_streamer/templates/aircox_streamer/source_item.html
Normal file → Executable file
107
aircox_streamer/templates/aircox_streamer/source_item.html
Normal file → Executable file
|
@ -1,8 +1,9 @@
|
|||
{% comment %}List item for a source.{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
<section class="box"><div class="columns is-desktop">
|
||||
<div class="column">
|
||||
<section class="box"><div class="flex-row gap-3">
|
||||
|
||||
<div class="flex-grow-1">
|
||||
<h5 class='title is-5' :class="{'has-text-danger': source.isPlaying, 'has-text-warning': source.isPaused}">
|
||||
<span>
|
||||
<span v-if="source.isPlaying" class="fas fa-play"></span>
|
||||
|
@ -21,28 +22,70 @@
|
|||
</a>
|
||||
</h5>
|
||||
|
||||
<table class="table bg-transparent">
|
||||
<tbody>
|
||||
<tr><th class="has-text-right ws-nowrap">
|
||||
{% translate "Status" %}
|
||||
</th>
|
||||
<td :class="{'has-text-danger': source.isPlaying, 'has-text-warning': source.isPaused}">
|
||||
<span v-if="source.isPlaying" class="fas fa-play"></span>
|
||||
<span v-else-if="source.data.status" class="fas fa-pause"></span>
|
||||
[[ source.data.status_verbose || "—" ]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="source.data.air_time">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Air time" %}
|
||||
</th><td>
|
||||
<span class="far fa-clock"></span>
|
||||
<time :datetime="source.date">
|
||||
[[ source.data.air_time.toLocaleDateString() ]],
|
||||
[[ source.data.air_time.toLocaleTimeString() ]]
|
||||
</time>
|
||||
</td>
|
||||
<tr v-if="source.remaining">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Time left" %}
|
||||
</th><td>
|
||||
<span class="far fa-hourglass"></span>
|
||||
[[ source.remainingString ]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="source.data.uri">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Data source" %}
|
||||
</th><td>
|
||||
<span class="far fa-play-circle"></span>
|
||||
<template v-if="source.data.uri.length > 64">...</template>[[ (source.data.uri && source.data.uri.slice(-64)) || '—' ]]
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<button class="button" @click="source.sync()"
|
||||
title="{% translate "Synchronize source with Liquidsoap" %}">
|
||||
<span class="icon is-small">
|
||||
<span class="fas fa-sync"></span>
|
||||
</span>
|
||||
<span>{% translate "Synchronise" %}</span>
|
||||
</button>
|
||||
<button class="button" @click="source.restart()"
|
||||
<button class="button smaller mr-2 mb-2" @click="source.restart()"
|
||||
title="{% translate "Restart current track" %}">
|
||||
<span class="icon is-small">
|
||||
<span class="fas fa-step-backward"></span>
|
||||
</span>
|
||||
<span>{% translate "Restart" %}</span>
|
||||
</button>
|
||||
<button class="button" @click="source.skip()"
|
||||
<button class="button smaller mr-2 mb-2" @click="source.skip()"
|
||||
title="{% translate "Skip current file" %}">
|
||||
<span>{% translate "Skip" %}</span>
|
||||
<span class="icon is-small">
|
||||
<span class="fas fa-step-forward"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="button smaller mr-2 mb-2" @click="source.sync()"
|
||||
title="{% translate "Synchronize source with Liquidsoap" %}">
|
||||
<span class="icon is-small">
|
||||
<span class="fas fa-sync"></span>
|
||||
</span>
|
||||
<span>{% translate "Synchronise" %}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="source.isQueue">
|
||||
|
@ -89,46 +132,4 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-two-fifths">
|
||||
<h6 class="subtitle is-6 is-marginless">Metadata</h6>
|
||||
<table class="table bg-transparent">
|
||||
<tbody>
|
||||
<tr><th class="has-text-right ws-nowrap">
|
||||
{% translate "Status" %}
|
||||
</th>
|
||||
<td :class="{'has-text-danger': source.isPlaying, 'has-text-warning': source.isPaused}">
|
||||
<span v-if="source.isPlaying" class="fas fa-play"></span>
|
||||
<span v-else-if="source.data.status" class="fas fa-pause"></span>
|
||||
[[ source.data.status_verbose || "—" ]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="source.data.air_time">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Air time" %}
|
||||
</th><td>
|
||||
<span class="far fa-clock"></span>
|
||||
<time :datetime="source.date">
|
||||
[[ source.data.air_time.toLocaleDateString() ]],
|
||||
[[ source.data.air_time.toLocaleTimeString() ]]
|
||||
</time>
|
||||
</td>
|
||||
<tr v-if="source.remaining">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Time left" %}
|
||||
</th><td>
|
||||
<span class="far fa-hourglass"></span>
|
||||
[[ source.remainingString ]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="source.data.uri">
|
||||
<th class="has-text-right ws-nowrap">
|
||||
{% translate "Data source" %}
|
||||
</th><td>
|
||||
<span class="far fa-play-circle"></span>
|
||||
<template v-if="source.data.uri.length > 64">...</template>[[ (source.data.uri && source.data.uri.slice(-64)) || '—' ]]
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></section>
|
||||
|
|
4
aircox_streamer/urls.py
Normal file → Executable file
4
aircox_streamer/urls.py
Normal file → Executable file
|
@ -9,10 +9,10 @@ from . import views, viewsets
|
|||
__all__ = ("api", "urls")
|
||||
|
||||
|
||||
prefix = "(?P<station_pk>[0-9]+)/"
|
||||
prefix = "<int:station_pk>/"
|
||||
|
||||
|
||||
router = DefaultRouter()
|
||||
router = DefaultRouter(use_regex_path=False)
|
||||
router.register(prefix + "playlist", viewsets.PlaylistSourceViewSet, basename="streamer-playlist")
|
||||
router.register(prefix + "queue", viewsets.QueueSourceViewSet, basename="streamer-queue")
|
||||
router.register("streamer", viewsets.StreamerViewSet, basename="streamer")
|
||||
|
|
12
aircox_streamer/viewsets.py
Normal file → Executable file
12
aircox_streamer/viewsets.py
Normal file → Executable file
|
@ -46,7 +46,7 @@ class ControllerViewSet(viewsets.ViewSet):
|
|||
if station_pk is None:
|
||||
return None
|
||||
if station_pk not in self.streamers:
|
||||
raise Http404("station not found")
|
||||
raise Http404(f"station not found: {station_pk}")
|
||||
return self.streamers[station_pk]
|
||||
|
||||
def get_serializer(self, **kwargs):
|
||||
|
@ -60,7 +60,8 @@ class ControllerViewSet(viewsets.ViewSet):
|
|||
return serializer.data
|
||||
|
||||
def dispatch(self, request, *args, station_pk=None, **kwargs):
|
||||
self.streamer = self.get_streamer(station_pk)
|
||||
if not self.streamer:
|
||||
self.streamer = self.get_streamer(station_pk)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
@ -80,6 +81,9 @@ class StreamerViewSet(ControllerViewSet):
|
|||
def dispatch(self, request, *args, pk=None, **kwargs):
|
||||
if pk is not None:
|
||||
kwargs.setdefault("station_pk", pk)
|
||||
if pk := kwargs.get("station_pk"):
|
||||
kwargs["station_pk"] = int(pk)
|
||||
|
||||
self.streamer = self.get_streamer(**kwargs)
|
||||
self.object = self.streamer
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
@ -88,6 +92,8 @@ class StreamerViewSet(ControllerViewSet):
|
|||
class SourceViewSet(ControllerViewSet):
|
||||
serializer_class = SourceSerializer
|
||||
model = controllers.Source
|
||||
lookup_field = "pk"
|
||||
lookup_value_converter = "str"
|
||||
|
||||
def get_sources(self):
|
||||
return (s for s in self.streamer.sources if isinstance(s, self.model))
|
||||
|
@ -139,7 +145,7 @@ class QueueSourceViewSet(SourceViewSet):
|
|||
model = controllers.QueueSource
|
||||
|
||||
def get_sound_queryset(self, request):
|
||||
return Sound.objects.station(request.station).broadcast()
|
||||
return Sound.objects.station(request.station)
|
||||
|
||||
@action(detail=True, methods=["POST"])
|
||||
def push(self, request, pk):
|
||||
|
|
Loading…
Reference in New Issue
Block a user