migration of Sounds; Liquidsoap 2.4 compatibility
This commit is contained in:
parent
6d0f2a7395
commit
52fc161d98
|
@ -5,12 +5,69 @@ from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
sounds_info = {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_sounds_info(apps, schema_editor):
|
||||||
|
global sounds
|
||||||
|
|
||||||
|
Sound = apps.get_model("aircox", "Sound")
|
||||||
|
objs = Sound.objects.filter(episode__isnull=False).values(
|
||||||
|
"pk",
|
||||||
|
"episode_id",
|
||||||
|
"position",
|
||||||
|
"type",
|
||||||
|
)
|
||||||
|
sounds_info.update({obj["pk"]: obj for obj in objs})
|
||||||
|
|
||||||
|
|
||||||
|
def restore_sounds_info(apps, schema_editor):
|
||||||
|
global sounds
|
||||||
|
|
||||||
|
try:
|
||||||
|
Sound = apps.get_model("aircox", "Sound")
|
||||||
|
EpisodeSound = apps.get_model("aircox", "EpisodeSound")
|
||||||
|
TYPE_ARCHIVE = 0x01
|
||||||
|
TYPE_REMOVED = 0x03
|
||||||
|
|
||||||
|
episode_sounds = []
|
||||||
|
sounds = []
|
||||||
|
for sound in Sound.objects.all():
|
||||||
|
info = sounds_info.get(sound.pk)
|
||||||
|
if not info:
|
||||||
|
continue
|
||||||
|
|
||||||
|
sound.broadcast = info["type"] == TYPE_ARCHIVE
|
||||||
|
sound.is_removed = info["type"] == TYPE_REMOVED
|
||||||
|
sounds.append(sound)
|
||||||
|
if not sound.is_removed:
|
||||||
|
obj = EpisodeSound(
|
||||||
|
sound=sound,
|
||||||
|
episode_id=info["episode_id"],
|
||||||
|
position=info["position"],
|
||||||
|
broadcast=sound.broadcast,
|
||||||
|
)
|
||||||
|
episode_sounds.append(obj)
|
||||||
|
|
||||||
|
Sound.objects.bulk_update(sounds, ("broadcast", "is_removed"))
|
||||||
|
EpisodeSound.objects.bulk_create(episode_sounds)
|
||||||
|
|
||||||
|
print(f"\n>>> {len(sounds)} Sound have been updated.")
|
||||||
|
print(f">>> {len(episode_sounds)} EpisodeSound have been created.")
|
||||||
|
except Exception as err:
|
||||||
|
print(err)
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("aircox", "0025_sound_is_removed_alter_sound_is_downloadable_and_more"),
|
("aircox", "0025_sound_is_removed_alter_sound_is_downloadable_and_more"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.RunPython(get_sounds_info),
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name="sound",
|
name="sound",
|
||||||
options={"verbose_name": "Sound file", "verbose_name_plural": "Sound files"},
|
options={"verbose_name": "Sound file", "verbose_name_plural": "Sound files"},
|
||||||
|
@ -105,4 +162,5 @@ class Migration(migrations.Migration):
|
||||||
"verbose_name_plural": "Episode Sounds",
|
"verbose_name_plural": "Episode Sounds",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.RunPython(restore_sounds_info),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import os
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from filer.fields.image import FilerImageField
|
from filer.fields.image import FilerImageField
|
||||||
|
|
||||||
from aircox.conf import settings
|
|
||||||
|
|
||||||
__all__ = ("Station", "StationQuerySet", "Port")
|
__all__ = ("Station", "StationQuerySet", "Port")
|
||||||
|
|
||||||
|
@ -32,13 +29,6 @@ class Station(models.Model):
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=64)
|
name = models.CharField(_("name"), max_length=64)
|
||||||
slug = models.SlugField(_("slug"), max_length=64, unique=True)
|
slug = models.SlugField(_("slug"), max_length=64, unique=True)
|
||||||
# FIXME: remove - should be decided only by Streamer controller + settings
|
|
||||||
path = models.CharField(
|
|
||||||
_("path"),
|
|
||||||
help_text=_("path to the working directory"),
|
|
||||||
max_length=256,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
default = models.BooleanField(
|
default = models.BooleanField(
|
||||||
_("default station"),
|
_("default station"),
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -96,12 +86,6 @@ class Station(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def save(self, make_sources=True, *args, **kwargs):
|
def save(self, make_sources=True, *args, **kwargs):
|
||||||
if not self.path:
|
|
||||||
self.path = os.path.join(
|
|
||||||
settings.CONTROLLERS_WORKING_DIR,
|
|
||||||
self.slug.replace("-", "_"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.default:
|
if self.default:
|
||||||
qs = Station.objects.filter(default=True)
|
qs = Station.objects.filter(default=True)
|
||||||
if self.pk is not None:
|
if self.pk is not None:
|
||||||
|
|
|
@ -19,7 +19,7 @@ class AdminMixin(LoginRequiredMixin, UserPassesTestMixin):
|
||||||
return self.request.station
|
return self.request.station
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
return self.request.user.is_staff
|
return self.request.user.is_admin
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs.update(admin.site.each_context(self.request))
|
kwargs.update(admin.site.each_context(self.request))
|
||||||
|
|
|
@ -77,9 +77,12 @@ class Metadata:
|
||||||
air_time = tz.datetime.strptime(air_time, "%Y/%m/%d %H:%M:%S")
|
air_time = tz.datetime.strptime(air_time, "%Y/%m/%d %H:%M:%S")
|
||||||
return local_tz.localize(air_time)
|
return local_tz.localize(air_time)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data, as_dict=False):
|
||||||
"""Validate provided data and set as attribute (must already be
|
"""Validate provided data and set as attribute (must already be
|
||||||
declared)"""
|
declared)"""
|
||||||
|
if as_dict and isinstance(data, list):
|
||||||
|
data = {v[0]: v[1] for v in data}
|
||||||
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if hasattr(self, key) and not callable(getattr(self, key)):
|
if hasattr(self, key) and not callable(getattr(self, key)):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
|
@ -43,9 +43,9 @@ class Source(Metadata):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.remaining = None
|
self.remaining = None
|
||||||
|
|
||||||
data = self.controller.send(self.id, ".get", parse=True)
|
data = self.controller.send(f"var.get {self.id}_meta", parse_json=True)
|
||||||
if data:
|
if data:
|
||||||
self.validate(data if data and isinstance(data, dict) else {})
|
self.validate(data if data and isinstance(data, (dict, list)) else {}, as_dict=True)
|
||||||
|
|
||||||
def skip(self):
|
def skip(self):
|
||||||
"""Skip the current source sound."""
|
"""Skip the current source sound."""
|
||||||
|
|
|
@ -8,8 +8,7 @@ import subprocess
|
||||||
import psutil
|
import psutil
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
from aircox.conf import settings
|
from ..conf import settings
|
||||||
|
|
||||||
from ..connector import Connector
|
from ..connector import Connector
|
||||||
from .sources import PlaylistSource, QueueSource
|
from .sources import PlaylistSource, QueueSource
|
||||||
|
|
||||||
|
@ -46,8 +45,8 @@ class Streamer:
|
||||||
self.outputs = self.station.port_set.active().output()
|
self.outputs = self.station.port_set.active().output()
|
||||||
|
|
||||||
self.id = self.station.slug.replace("-", "_")
|
self.id = self.station.slug.replace("-", "_")
|
||||||
self.path = os.path.join(station.path, "station.liq")
|
self.path = settings.get_dir(station, "station.liq")
|
||||||
self.connector = connector or Connector(os.path.join(station.path, "station.sock"))
|
self.connector = connector or Connector(settings.get_dir(station, "station.sock"))
|
||||||
self.init_sources()
|
self.init_sources()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -98,7 +97,6 @@ class Streamer:
|
||||||
{
|
{
|
||||||
"station": self.station,
|
"station": self.station,
|
||||||
"streamer": self,
|
"streamer": self,
|
||||||
"settings": settings,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
data = re.sub("[\t ]+\n", "\n", data)
|
data = re.sub("[\t ]+\n", "\n", data)
|
||||||
|
|
|
@ -19,7 +19,7 @@ from aircox_streamer.controllers import Monitor, Streamer
|
||||||
|
|
||||||
|
|
||||||
# force using UTC
|
# force using UTC
|
||||||
tz.activate(timezone.UTC)
|
tz.activate(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -10,9 +10,9 @@ Base liquidsoap station configuration.
|
||||||
|
|
||||||
{% block functions %}
|
{% block functions %}
|
||||||
{# Seek function #}
|
{# Seek function #}
|
||||||
def seek(source, t) =
|
def seek(s, t) =
|
||||||
t = float_of_string(default=0.,t)
|
t = float_of_string(default=0.,t)
|
||||||
ret = source.seek(source,t)
|
ret = source.seek(s,t)
|
||||||
log("seek #{ret} seconds.")
|
log("seek #{ret} seconds.")
|
||||||
"#{ret}"
|
"#{ret}"
|
||||||
end
|
end
|
||||||
|
@ -30,6 +30,17 @@ def to_stream(live, stream)
|
||||||
add(normalize=false, [live,stream])
|
add(normalize=false, [live,stream])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{# Skip command #}
|
||||||
|
def add_skip_command(s) =
|
||||||
|
def skip(_) =
|
||||||
|
source.skip(s)
|
||||||
|
"Done!"
|
||||||
|
end
|
||||||
|
server.register(namespace="#{source.id(s)}",
|
||||||
|
usage="skip",
|
||||||
|
description="Skip the current song.",
|
||||||
|
"skip",skip)
|
||||||
|
end
|
||||||
|
|
||||||
{% comment %}
|
{% comment %}
|
||||||
An interactive source is a source that:
|
An interactive source is a source that:
|
||||||
|
@ -45,10 +56,14 @@ def interactive (id, s) =
|
||||||
server.register(namespace=id,
|
server.register(namespace=id,
|
||||||
description="Get source's track remaining time",
|
description="Get source's track remaining time",
|
||||||
usage="remaining",
|
usage="remaining",
|
||||||
"remaining", fun (_) -> begin json_of(source.remaining(s)) end)
|
"remaining", fun (_) -> begin json.stringify(source.remaining(s)) end)
|
||||||
|
|
||||||
s = store_metadata(id=id, size=1, s)
|
|
||||||
add_skip_command(s)
|
add_skip_command(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)))
|
||||||
|
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -66,9 +81,6 @@ end
|
||||||
set("server.socket", true)
|
set("server.socket", true)
|
||||||
set("server.socket.path", "{{ streamer.socket_path }}")
|
set("server.socket.path", "{{ streamer.socket_path }}")
|
||||||
set("log.file.path", "{{ station.path }}/liquidsoap.log")
|
set("log.file.path", "{{ station.path }}/liquidsoap.log")
|
||||||
{% for key, value in settings.AIRCOX_LIQUIDSOAP_SET.items %}
|
|
||||||
set("{{ key|safe }}", {{ value|safe }})
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block config_extras %}
|
{% block config_extras %}
|
||||||
|
|
|
@ -20,13 +20,18 @@ from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
import aircox.urls
|
import aircox.urls
|
||||||
|
import aircox_streamer.urls
|
||||||
|
|
||||||
urlpatterns = aircox.urls.urls + [
|
urlpatterns = (
|
||||||
path("admin/", admin.site.urls),
|
aircox.urls.urls
|
||||||
path("accounts/", include("django.contrib.auth.urls")),
|
+ aircox_streamer.urls.urls
|
||||||
path("ckeditor/", include("ckeditor_uploader.urls")),
|
+ [
|
||||||
path("filer/", include("filer.urls")),
|
path("admin/", admin.site.urls),
|
||||||
]
|
path("accounts/", include("django.contrib.auth.urls")),
|
||||||
|
path("ckeditor/", include("ckeditor_uploader.urls")),
|
||||||
|
path("filer/", include("filer.urls")),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user