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
|
||||
|
||||
|
||||
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):
|
||||
dependencies = [
|
||||
("aircox", "0025_sound_is_removed_alter_sound_is_downloadable_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(get_sounds_info),
|
||||
migrations.AlterModelOptions(
|
||||
name="sound",
|
||||
options={"verbose_name": "Sound file", "verbose_name_plural": "Sound files"},
|
||||
|
@ -105,4 +162,5 @@ class Migration(migrations.Migration):
|
|||
"verbose_name_plural": "Episode Sounds",
|
||||
},
|
||||
),
|
||||
migrations.RunPython(restore_sounds_info),
|
||||
]
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import os
|
||||
|
||||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from filer.fields.image import FilerImageField
|
||||
|
||||
from aircox.conf import settings
|
||||
|
||||
__all__ = ("Station", "StationQuerySet", "Port")
|
||||
|
||||
|
@ -32,13 +29,6 @@ class Station(models.Model):
|
|||
|
||||
name = models.CharField(_("name"), max_length=64)
|
||||
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 station"),
|
||||
default=False,
|
||||
|
@ -96,12 +86,6 @@ class Station(models.Model):
|
|||
return self.name
|
||||
|
||||
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:
|
||||
qs = Station.objects.filter(default=True)
|
||||
if self.pk is not None:
|
||||
|
|
|
@ -19,7 +19,7 @@ class AdminMixin(LoginRequiredMixin, UserPassesTestMixin):
|
|||
return self.request.station
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.is_staff
|
||||
return self.request.user.is_admin
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
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")
|
||||
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
|
||||
declared)"""
|
||||
if as_dict and isinstance(data, list):
|
||||
data = {v[0]: v[1] for v in data}
|
||||
|
||||
for key, value in data.items():
|
||||
if hasattr(self, key) and not callable(getattr(self, key)):
|
||||
setattr(self, key, value)
|
||||
|
|
|
@ -43,9 +43,9 @@ class Source(Metadata):
|
|||
except ValueError:
|
||||
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:
|
||||
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):
|
||||
"""Skip the current source sound."""
|
||||
|
|
|
@ -8,8 +8,7 @@ import subprocess
|
|||
import psutil
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from aircox.conf import settings
|
||||
|
||||
from ..conf import settings
|
||||
from ..connector import Connector
|
||||
from .sources import PlaylistSource, QueueSource
|
||||
|
||||
|
@ -46,8 +45,8 @@ class Streamer:
|
|||
self.outputs = self.station.port_set.active().output()
|
||||
|
||||
self.id = self.station.slug.replace("-", "_")
|
||||
self.path = os.path.join(station.path, "station.liq")
|
||||
self.connector = connector or Connector(os.path.join(station.path, "station.sock"))
|
||||
self.path = settings.get_dir(station, "station.liq")
|
||||
self.connector = connector or Connector(settings.get_dir(station, "station.sock"))
|
||||
self.init_sources()
|
||||
|
||||
@property
|
||||
|
@ -98,7 +97,6 @@ class Streamer:
|
|||
{
|
||||
"station": self.station,
|
||||
"streamer": self,
|
||||
"settings": settings,
|
||||
},
|
||||
)
|
||||
data = re.sub("[\t ]+\n", "\n", data)
|
||||
|
|
|
@ -19,7 +19,7 @@ from aircox_streamer.controllers import Monitor, Streamer
|
|||
|
||||
|
||||
# force using UTC
|
||||
tz.activate(timezone.UTC)
|
||||
tz.activate(timezone.utc)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
|
@ -10,9 +10,9 @@ Base liquidsoap station configuration.
|
|||
|
||||
{% block functions %}
|
||||
{# Seek function #}
|
||||
def seek(source, t) =
|
||||
def seek(s, t) =
|
||||
t = float_of_string(default=0.,t)
|
||||
ret = source.seek(source,t)
|
||||
ret = source.seek(s,t)
|
||||
log("seek #{ret} seconds.")
|
||||
"#{ret}"
|
||||
end
|
||||
|
@ -30,6 +30,17 @@ def to_stream(live, stream)
|
|||
add(normalize=false, [live,stream])
|
||||
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 %}
|
||||
An interactive source is a source that:
|
||||
|
@ -45,10 +56,14 @@ def interactive (id, s) =
|
|||
server.register(namespace=id,
|
||||
description="Get source's track remaining time",
|
||||
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)
|
||||
|
||||
{# 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
|
||||
end
|
||||
|
||||
|
@ -66,9 +81,6 @@ end
|
|||
set("server.socket", true)
|
||||
set("server.socket.path", "{{ streamer.socket_path }}")
|
||||
set("log.file.path", "{{ station.path }}/liquidsoap.log")
|
||||
{% for key, value in settings.AIRCOX_LIQUIDSOAP_SET.items %}
|
||||
set("{{ key|safe }}", {{ value|safe }})
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block config_extras %}
|
||||
|
|
|
@ -20,13 +20,18 @@ from django.contrib import admin
|
|||
from django.urls import include, path
|
||||
|
||||
import aircox.urls
|
||||
import aircox_streamer.urls
|
||||
|
||||
urlpatterns = aircox.urls.urls + [
|
||||
path("admin/", admin.site.urls),
|
||||
path("accounts/", include("django.contrib.auth.urls")),
|
||||
path("ckeditor/", include("ckeditor_uploader.urls")),
|
||||
path("filer/", include("filer.urls")),
|
||||
]
|
||||
urlpatterns = (
|
||||
aircox.urls.urls
|
||||
+ aircox_streamer.urls.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:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(
|
||||
|
|
Loading…
Reference in New Issue
Block a user