Support multiple youtube configs.
authorRadek Czajka <rczajka@rczajka.pl>
Thu, 22 Oct 2020 13:38:56 +0000 (15:38 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Thu, 22 Oct 2020 13:38:56 +0000 (15:38 +0200)
requirements.txt
src/archive/migrations/0019_auto_20201022_1414.py [new file with mode: 0644]
src/archive/migrations/0020_auto_20201022_1535.py [new file with mode: 0644]
src/archive/models.py
src/archive/tasks.py
src/youtube/migrations/0015_auto_20201022_1414.py [new file with mode: 0644]
src/youtube/migrations/0016_populate_youtube_relations.py [new file with mode: 0644]
src/youtube/migrations/0017_auto_20201022_1535.py [new file with mode: 0644]
src/youtube/models.py
src/youtube/tasks.py
src/youtube/views.py

index 1c6781e..f910911 100644 (file)
@@ -15,3 +15,5 @@ requests-toolbelt==0.9.1
 requests-oauthlib==1.3.0
 PyYAML==5.3.1
 Pillow==7.2.0
+librarian==1.9
+py3-aeneas==1.1.0
diff --git a/src/archive/migrations/0019_auto_20201022_1414.py b/src/archive/migrations/0019_auto_20201022_1414.py
new file mode 100644 (file)
index 0000000..aa4ebd5
--- /dev/null
@@ -0,0 +1,40 @@
+# Generated by Django 3.1.2 on 2020-10-22 14:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('youtube', '0015_auto_20201022_1414'),
+        ('archive', '0018_auto_20200703_1718'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='project',
+            name='youtube',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='youtube.youtube'),
+        ),
+        migrations.AlterField(
+            model_name='audiobook',
+            name='mp3_status',
+            field=models.SmallIntegerField(choices=[(5, 'Queued'), (10, 'Waiting'), (20, 'Encoding'), (30, 'Tagging'), (40, 'Converting audio'), (50, 'Converting video'), (60, 'Assembling audio'), (70, 'Assembling video'), (80, 'Joining audio and video'), (100, 'Sending'), (110, 'Setting thumbnail')], editable=False, null=True),
+        ),
+        migrations.AlterField(
+            model_name='audiobook',
+            name='ogg_status',
+            field=models.SmallIntegerField(choices=[(5, 'Queued'), (10, 'Waiting'), (20, 'Encoding'), (30, 'Tagging'), (40, 'Converting audio'), (50, 'Converting video'), (60, 'Assembling audio'), (70, 'Assembling video'), (80, 'Joining audio and video'), (100, 'Sending'), (110, 'Setting thumbnail')], editable=False, null=True),
+        ),
+        migrations.AlterField(
+            model_name='audiobook',
+            name='slug',
+            field=models.SlugField(blank=True, help_text='WL catalogue slug of the book.', max_length=120),
+        ),
+        migrations.AlterField(
+            model_name='audiobook',
+            name='youtube_status',
+            field=models.SmallIntegerField(choices=[(5, 'Queued'), (10, 'Waiting'), (20, 'Encoding'), (30, 'Tagging'), (40, 'Converting audio'), (50, 'Converting video'), (60, 'Assembling audio'), (70, 'Assembling video'), (80, 'Joining audio and video'), (100, 'Sending'), (110, 'Setting thumbnail')], editable=False, null=True),
+        ),
+    ]
diff --git a/src/archive/migrations/0020_auto_20201022_1535.py b/src/archive/migrations/0020_auto_20201022_1535.py
new file mode 100644 (file)
index 0000000..ace0a98
--- /dev/null
@@ -0,0 +1,20 @@
+# Generated by Django 3.1.2 on 2020-10-22 15:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('youtube', '0017_auto_20201022_1535'),
+        ('archive', '0019_auto_20201022_1414'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='project',
+            name='youtube',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='youtube.youtube'),
+        ),
+    ]
index 3be61d4..b15bec2 100644 (file)
@@ -1,3 +1,4 @@
+import io
 import json
 import os.path
 
@@ -27,6 +28,7 @@ class Project(models.Model):
     name = models.CharField(max_length=128, unique=True, db_index=True, verbose_name="Nazwa")
     sponsors = models.TextField(blank=True, null=True, verbose_name="Sponsorzy")
     description = models.TextField(blank=True, verbose_name="Opis")
+    youtube = models.ForeignKey('youtube.YouTube', models.PROTECT)
 
     class Meta:
         verbose_name = _("project")
@@ -81,7 +83,7 @@ class Audiobook(models.Model):
     encoded_by = models.CharField(max_length=255, verbose_name=_('encoded by'))
     date = models.CharField(max_length=255, verbose_name=_('date'))
     project = models.ForeignKey(Project, models.PROTECT, verbose_name=_('project'))
-    slug = models.SlugField(max_length=120, help_text=_('WL catalogue slug of the book.'))
+    slug = models.SlugField(max_length=120, blank=True, help_text=_('WL catalogue slug of the book.'))
     translator = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('translator'))
     modified = models.DateTimeField(null=True, editable=False)
     license = models.ForeignKey(License, models.PROTECT, null=True, blank=True, verbose_name=_('license'))
@@ -222,5 +224,26 @@ class Audiobook(models.Model):
 
     @cached_property
     def book(self):
-        apidata = requests.get(f'https://wolnelektury.pl/api/books/{self.slug}/').json()
+        if self.slug:
+            apidata = requests.get(f'https://wolnelektury.pl/api/books/{self.slug}/').json()
+        else:
+            return {}
         return apidata
+
+    @property
+    def document(self):
+        from librarian.document import WLDocument, parser
+        from lxml import etree
+
+        xml_url = self.book.get('xml', None)
+        if xml_url is None:
+            return None
+
+        return WLDocument(
+                etree.parse(
+                    io.BytesIO(
+                        requests.get(xml_url).content
+                    )
+                    ,parser = parser
+                )
+            )
index e50d9b2..b5c64ce 100644 (file)
@@ -89,6 +89,7 @@ class AudioFormatTask(Task):
     def run(self, uid, aid, publish=True):
         aid = int(aid)
         audiobook = Audiobook.objects.get(id=aid)
+        self.audiobook = audiobook
         self.set_status(aid, status.ENCODING)
 
         if uid:
diff --git a/src/youtube/migrations/0015_auto_20201022_1414.py b/src/youtube/migrations/0015_auto_20201022_1414.py
new file mode 100644 (file)
index 0000000..d837410
--- /dev/null
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.2 on 2020-10-22 14:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('youtube', '0014_auto_20200706_1219'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='thumbnailtemplate',
+            name='youtube',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='youtube.youtube'),
+        ),
+        migrations.AddField(
+            model_name='youtube',
+            name='name',
+            field=models.CharField(default='Wolne Lektury', max_length=255),
+            preserve_default=False,
+        ),
+    ]
diff --git a/src/youtube/migrations/0016_populate_youtube_relations.py b/src/youtube/migrations/0016_populate_youtube_relations.py
new file mode 100644 (file)
index 0000000..2ea388a
--- /dev/null
@@ -0,0 +1,32 @@
+# Generated by Django 3.1.2 on 2020-10-22 14:15
+
+from django.db import migrations
+
+
+def populate_youtube_relations(apps, schema_editor):
+    YouTube = apps.get_model('youtube', 'YouTube')
+    ThumbnailTemplate = apps.get_model('youtube', 'ThumbnailTemplate')
+    Project = apps.get_model('archive', 'Project')
+
+    try:
+        yt = YouTube.objects.first()
+    except YouTube.DoesNotExist:
+        yt = YouTube.objects.create(name='default')
+
+    Project.objects.filter(youtube=None).update(youtube=yt)
+    ThumbnailTemplate.objects.filter(youtube=None).update(youtube=yt)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('youtube', '0015_auto_20201022_1414'),
+        ('archive', '0019_auto_20201022_1414'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            populate_youtube_relations,
+            migrations.RunPython.noop
+        )
+    ]
diff --git a/src/youtube/migrations/0017_auto_20201022_1535.py b/src/youtube/migrations/0017_auto_20201022_1535.py
new file mode 100644 (file)
index 0000000..63a0e34
--- /dev/null
@@ -0,0 +1,19 @@
+# Generated by Django 3.1.2 on 2020-10-22 15:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('youtube', '0016_populate_youtube_relations'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='thumbnailtemplate',
+            name='youtube',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='youtube.youtube'),
+        ),
+    ]
index 356e335..df071f1 100644 (file)
@@ -24,6 +24,7 @@ YOUTUBE_TITLE_LIMIT = 100
 
 
 class YouTube(models.Model):
+    name = models.CharField(max_length=255)
     title_template = models.CharField(max_length=1024, blank=True)
     description_template = models.TextField(blank=True)
     category = models.IntegerField(null=True, blank=True, choices=[
@@ -44,6 +45,9 @@ class YouTube(models.Model):
         verbose_name = _("YouTube configuration")
         verbose_name_plural = _("YouTube configurations")
 
+    def __str__(self):
+        return self.name
+
     def get_context(self, audiobook):
         return Context(dict(
             audiobook=audiobook,
@@ -212,6 +216,7 @@ class Font(models.Model):
 
 
 class ThumbnailTemplate(models.Model):
+    youtube = models.ForeignKey(YouTube, models.CASCADE)
     order = models.SmallIntegerField()
     is_active = models.BooleanField()
     background = models.FileField(upload_to='youtube/thumbnail')
index 63051c9..cad3d11 100644 (file)
@@ -8,7 +8,7 @@ class YouTubeTask(AudioFormatTask):
     prefix = 'youtube'
 
     def encode(self, in_paths, out_path):
-        YouTube.objects.first().prepare_file(in_paths, out_path)
+        self.audiobook.project.youtube.prepare_file(in_paths, out_path)
 
     def set_tags(self, audiobook, filename):
         pass
@@ -19,7 +19,7 @@ class YouTubeTask(AudioFormatTask):
         os.unlink(file_name)
 
     def put(self, user, audiobook, filename):
-        YouTube.objects.first().publish(audiobook, filename)
+        audiobook.project.youtube.publish(audiobook, filename)
 
     def get_source_file_paths(self, audiobook):
         if not audiobook.youtube_volume:
index 18bd1d7..e0bcab2 100644 (file)
@@ -34,7 +34,7 @@ def book_publish(request, slug):
 def thumbnail(request, aid, thumbnail_id=None):
     audiobook = get_object_or_404(Audiobook, id=aid)
     if thumbnail_id is None:
-        yt = models.YouTube.objects.first()
+        yt = audiobook.project.youtube
         buf = yt.prepare_thumbnail(audiobook)
     else:
         template = get_object_or_404(models.ThumbnailTemplate, id=thumbnail_id)
@@ -49,7 +49,7 @@ class Preview(DetailView):
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
-        yt = models.YouTube.objects.first()
+        yt = ctx['object'].project.youtube
         ctx['data'] = yt.get_data(ctx['object'])
         ctx['title'] = yt.get_title(ctx['object'])
         ctx['description'] = yt.get_description(ctx['object'])
@@ -62,8 +62,9 @@ class Update(SingleObjectMixin, View):
     model = Audiobook
 
     def post(self, request, pk):
-        yt = models.YouTube.objects.first()
-        yt.update_data(self.get_object())
+        obj = self.get_object()
+        yt = obj.project.youtube
+        yt.update_data(obj)
         return redirect(reverse('file', args=[pk]))
 
 
@@ -72,6 +73,7 @@ class UpdateThumbnail(SingleObjectMixin, View):
     model = Audiobook
 
     def post(self, request, pk):
-        yt = models.YouTube.objects.first()
-        yt.update_thumbnail(self.get_object())
+        obj = self.get_object()
+        yt = obj.project.youtube
+        yt.update_thumbnail(obj)
         return redirect(reverse('file', args=[pk]))