Phase 1 - extend model for new fields

pull/338/head
KuhnChris 2023-02-14 21:52:50 +01:00
parent f14d2dd29e
commit 24a49d2f14
8 changed files with 169 additions and 10 deletions

1
.gitignore vendored
View File

@ -132,3 +132,4 @@ dmypy.json
.pyre/
Pipfile.lock
.vscode/launch.json

View File

@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
autopep8 = "*"
[packages]
django = "~=3.2"

View File

@ -0,0 +1,34 @@
# Generated by Django 3.2.18 on 2023-02-14 20:52
from django.db import migrations, models
import sync.models
class Migration(migrations.Migration):
dependencies = [
('sync', '0014_alter_media_media_file'),
]
operations = [
migrations.AddField(
model_name='source',
name='embed_metadata',
field=models.BooleanField(default=False, help_text='Embed metadata from source into file', verbose_name='embed metadata'),
),
migrations.AddField(
model_name='source',
name='embed_thumbnail',
field=models.BooleanField(default=False, help_text='Embed thumbnail into the file', verbose_name='embed thumbnail'),
),
migrations.AddField(
model_name='source',
name='enable_sponsorblock',
field=models.BooleanField(default=True, help_text='Use SponsorBlock?', verbose_name='enable sponsorblock'),
),
migrations.AddField(
model_name='source',
name='sponsorblock_categories',
field=sync.models.CommaSepChoiceField(default='all', possible_choices=(('all', 'All'), ('sponsor', 'Sponsor'), ('intro', 'Intermission/Intro Animation'), ('outro', 'Endcards/Credits'), ('selfpromo', 'Unpaid/Self Promotion'), ('preview', 'Preview/Recap'), ('filler', 'Filler Tangent'), ('interaction', 'Interaction Reminder'), ('music_offtopic', 'Non-Music Section'))),
),
]

View File

@ -7,6 +7,7 @@ from datetime import datetime, timedelta
from pathlib import Path
from django.conf import settings
from django.db import models
from django.forms import MultipleChoiceField, CheckboxSelectMultiple
from django.core.files.storage import FileSystemStorage
from django.utils.text import slugify
from django.utils import timezone
@ -23,6 +24,63 @@ from .mediaservers import PlexMediaServer
media_file_storage = FileSystemStorage(location=str(settings.DOWNLOAD_ROOT), base_url='/media-data/')
class CommaSepField(models.Field):
"Implements comma-separated storage of lists"
def __init__(self, separator=",", *args, **kwargs):
self.separator = separator
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
# Only include kwarg if it's not the default
if self.separator != ",":
kwargs['separator'] = self.separator
return name, path, args, kwargs
class CustomCheckboxSelectMultiple(CheckboxSelectMultiple):
template_name = 'widgets/checkbox_select.html'
option_template_name = 'widgets/checkbox_option.html'
class CommaSepChoiceField(CommaSepField):
"Implements comma-separated storage of lists"
def __init__(self, separator=",", possible_choices=(("","")), *args, **kwargs):
print(">",separator, possible_choices, args, kwargs)
self.possible_choices = possible_choices
super().__init__(separator=separator, *args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
print("<",name,path,args,kwargs)
# Only include kwarg if it's not the default
if self.separator != ",":
kwargs['separator'] = self.separator
kwargs['possible_choices'] = self.possible_choices
return name, path, args, kwargs
def db_type(self, _connection):
return 'char(1024)'
def get_choices(self):
choiceArray = []
if self.possible_choices is None:
return choiceArray
for t in self.possible_choices:
choiceArray.append(t)
return choiceArray
def formfield(self, **kwargs):
# This is a fairly standard way to set up some defaults
# while letting the caller override them.
print(self.choices)
defaults = {'form_class': MultipleChoiceField,
'choices': self.get_choices,
'widget': CustomCheckboxSelectMultiple}
defaults.update(kwargs)
#del defaults.required
return super().formfield(**defaults)
class Source(models.Model):
'''
@ -106,6 +164,43 @@ class Source(models.Model):
EXTENSION_MKV = 'mkv'
EXTENSIONS = (EXTENSION_M4A, EXTENSION_OGG, EXTENSION_MKV)
# as stolen from: https://wiki.sponsor.ajay.app/w/Types / https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/postprocessor/sponsorblock.py
SPONSORBLOCK_CATEGORIES_CHOICES = (
('all', 'All'),
('sponsor', 'Sponsor'),
('intro', 'Intermission/Intro Animation'),
('outro', 'Endcards/Credits'),
('selfpromo', 'Unpaid/Self Promotion'),
('preview', 'Preview/Recap'),
('filler', 'Filler Tangent'),
('interaction', 'Interaction Reminder'),
('music_offtopic', 'Non-Music Section'),
)
sponsorblock_categories = CommaSepChoiceField(
possible_choices=SPONSORBLOCK_CATEGORIES_CHOICES,
default="all"
)
embed_metadata = models.BooleanField(
_('embed metadata'),
default=False,
help_text=_('Embed metadata from source into file')
)
embed_thumbnail = models.BooleanField(
_('embed thumbnail'),
default=False,
help_text=_('Embed thumbnail into the file')
)
enable_sponsorblock = models.BooleanField(
_('enable sponsorblock'),
default=True,
help_text=_('Use SponsorBlock?')
)
# Fontawesome icons used for the source on the front end
ICONS = {
SOURCE_TYPE_YOUTUBE_CHANNEL: '<i class="fab fa-youtube"></i>',

View File

@ -0,0 +1,7 @@
<!--<input type="{{ option.type }}" name="{{ option.name }}" value="{{ option.value }}" id="{{ option.value }}"><BR>
<label for="{{ option.value }}">{{option.label}}</label>-->
<label>
<input type="{{ option.type }}" name="{{ option.name }}" value="{{ option.value }}" id="{{ option.value }}">
<span>{{option.label}}</span>
</label>

View File

@ -0,0 +1,5 @@
{% for group, options, index in widget.optgroups %}
{% for option in options %}
{% include option.template_name with option=option %}
{% endfor%}
{% endfor %}

View File

@ -297,7 +297,9 @@ class EditSourceMixin:
fields = ('source_type', 'key', 'name', 'directory', 'media_format',
'index_schedule', 'download_media', 'download_cap', 'delete_old_media',
'days_to_keep', 'source_resolution', 'source_vcodec', 'source_acodec',
'prefer_60fps', 'prefer_hdr', 'fallback', 'copy_thumbnails', 'write_nfo', 'write_json')
'prefer_60fps', 'prefer_hdr', 'fallback', 'copy_thumbnails', 'write_nfo',
'write_json', 'embed_metadata', 'embed_thumbnail', 'enable_sponsorblock',
'sponsorblock_categories')
errors = {
'invalid_media_format': _('Invalid media format, the media format contains '
'errors or is empty. Check the table at the end of '

View File

@ -64,7 +64,7 @@ def get_media_info(url):
return response
def download_media(url, media_format, extension, output_file, info_json, sponsor_categories="all"):
def download_media(url, media_format, extension, output_file, info_json, sponsor_categories="all", embed_thumbnail=False, embed_metadata=False, skip_sponsors=True):
'''
Downloads a YouTube URL to a file on disk.
'''
@ -101,23 +101,37 @@ def download_media(url, media_format, extension, output_file, info_json, sponsor
log.warn(f'[youtube-dl] unknown event: {str(event)}')
hook.download_progress = 0
opts = get_yt_opts()
opts.update({
ytopts = {
'format': media_format,
'merge_output_format': extension,
'outtmpl': output_file,
'quiet': True,
'progress_hooks': [hook],
'writeinfojson': info_json,
'postprocessors': [{
'postprocessors': []
}
sbopt = {
'key': 'SponsorBlock',
'categories': [sponsor_categories]
},{
}
ffmdopt = {
'key': 'FFmpegMetadata',
'add_chapters': True,
'add_metadata': True
}]
})
}
opts = get_yt_opts()
if embed_thumbnail:
ytopts['postprocessors'].push({'key': 'EmbedThumbnail'})
if embed_metadata:
ffmdopt["embed-metadata"] = True
if skip_sponsors:
ytopts['postprocessors'].push(sbopt)
ytopts['postprocessors'].push(ffmdopt)
opts.update(ytopts)
with yt_dlp.YoutubeDL(opts) as y:
try:
return y.download([url])