ffmpeg embed thumbnails, configuration
This commit is contained in:
		
							parent
							
								
									24a49d2f14
								
							
						
					
					
						commit
						2772e85d9f
					
				| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					from typing import Any, Optional, Dict
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
from xml.etree import ElementTree
 | 
					from xml.etree import ElementTree
 | 
				
			||||||
| 
						 | 
					@ -7,7 +8,7 @@ from datetime import datetime, timedelta
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.forms import MultipleChoiceField,  CheckboxSelectMultiple
 | 
					from django.forms import MultipleChoiceField,  CheckboxSelectMultiple, Field, TypedMultipleChoiceField
 | 
				
			||||||
from django.core.files.storage import FileSystemStorage
 | 
					from django.core.files.storage import FileSystemStorage
 | 
				
			||||||
from django.utils.text import slugify
 | 
					from django.utils.text import slugify
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
| 
						 | 
					@ -24,37 +25,44 @@ from .mediaservers import PlexMediaServer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
media_file_storage = FileSystemStorage(location=str(settings.DOWNLOAD_ROOT), base_url='/media-data/')
 | 
					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):
 | 
					class CustomCheckboxSelectMultiple(CheckboxSelectMultiple):
 | 
				
			||||||
    template_name = 'widgets/checkbox_select.html'
 | 
					    template_name = 'widgets/checkbox_select.html'
 | 
				
			||||||
    option_template_name = 'widgets/checkbox_option.html'
 | 
					    option_template_name = 'widgets/checkbox_option.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CommaSepChoiceField(CommaSepField):
 | 
					    def get_context(self, name: str, value: Any, attrs) -> Dict[str, Any]:
 | 
				
			||||||
 | 
					        ctx = super().get_context(name, value, attrs)['widget']
 | 
				
			||||||
 | 
					        ctx["multipleChoiceProperties"] = []
 | 
				
			||||||
 | 
					        for _group, options, _index in ctx["optgroups"]:
 | 
				
			||||||
 | 
					            for option in options:
 | 
				
			||||||
 | 
					                if not isinstance(value,list) and ( option["value"] in value.selected_choices or ( value.allow_all and value.all_choice in value.selected_choices ) ):
 | 
				
			||||||
 | 
					                    checked = True
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    checked = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ctx["multipleChoiceProperties"].append({
 | 
				
			||||||
 | 
					                    "template_name": option["template_name"],
 | 
				
			||||||
 | 
					                    "type": option["type"],
 | 
				
			||||||
 | 
					                    "value": option["value"],
 | 
				
			||||||
 | 
					                    "label": option["label"],
 | 
				
			||||||
 | 
					                    "name": option["name"],
 | 
				
			||||||
 | 
					                    "checked": checked})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return { 'widget': ctx }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommaSepChoiceField(models.Field):
 | 
				
			||||||
    "Implements comma-separated storage of lists"
 | 
					    "Implements comma-separated storage of lists"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, separator=",", possible_choices=(("","")), *args, **kwargs):
 | 
					    def __init__(self, separator=",", possible_choices=(("","")), all_choice="", all_label="All", allow_all=False, *args, **kwargs):
 | 
				
			||||||
        print(">",separator, possible_choices, args, kwargs)
 | 
					        self.separator = separator
 | 
				
			||||||
        self.possible_choices = possible_choices
 | 
					        self.possible_choices = possible_choices
 | 
				
			||||||
        super().__init__(separator=separator, *args, **kwargs)
 | 
					        self.selected_choices = []
 | 
				
			||||||
 | 
					        self.allow_all = allow_all
 | 
				
			||||||
 | 
					        self.all_label = all_label
 | 
				
			||||||
 | 
					        self.all_choice = all_choice
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def deconstruct(self):
 | 
					    def deconstruct(self):
 | 
				
			||||||
        name, path, args, kwargs = super().deconstruct()
 | 
					        name, path, args, kwargs = super().deconstruct()
 | 
				
			||||||
        print("<",name,path,args,kwargs)
 | 
					 | 
				
			||||||
        # Only include kwarg if it's not the default
 | 
					 | 
				
			||||||
        if self.separator != ",":
 | 
					        if self.separator != ",":
 | 
				
			||||||
            kwargs['separator'] = self.separator
 | 
					            kwargs['separator'] = self.separator
 | 
				
			||||||
        kwargs['possible_choices'] = self.possible_choices
 | 
					        kwargs['possible_choices'] = self.possible_choices
 | 
				
			||||||
| 
						 | 
					@ -63,25 +71,54 @@ class CommaSepChoiceField(CommaSepField):
 | 
				
			||||||
    def db_type(self, _connection):
 | 
					    def db_type(self, _connection):
 | 
				
			||||||
        return 'char(1024)'
 | 
					        return 'char(1024)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_choices(self):
 | 
					    def get_my_choices(self):
 | 
				
			||||||
        choiceArray = []
 | 
					        choiceArray = []
 | 
				
			||||||
        if self.possible_choices is None:
 | 
					        if self.possible_choices is None:
 | 
				
			||||||
            return choiceArray
 | 
					            return choiceArray
 | 
				
			||||||
 | 
					        if self.allow_all:
 | 
				
			||||||
 | 
					            choiceArray.append((self.all_choice, _(self.all_label)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for t in self.possible_choices:
 | 
					        for t in self.possible_choices:
 | 
				
			||||||
            choiceArray.append(t)
 | 
					            choiceArray.append(t)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        return choiceArray
 | 
					        return choiceArray
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def formfield(self, **kwargs):
 | 
					    def formfield(self, **kwargs):
 | 
				
			||||||
        # This is a fairly standard way to set up some defaults
 | 
					        # This is a fairly standard way to set up some defaults
 | 
				
			||||||
        # while letting the caller override them.
 | 
					        # while letting the caller override them.
 | 
				
			||||||
        print(self.choices)
 | 
					 | 
				
			||||||
        defaults = {'form_class': MultipleChoiceField, 
 | 
					        defaults = {'form_class': MultipleChoiceField, 
 | 
				
			||||||
                    'choices': self.get_choices,
 | 
					                    'choices': self.get_my_choices,
 | 
				
			||||||
                    'widget': CustomCheckboxSelectMultiple}
 | 
					                    'widget': CustomCheckboxSelectMultiple,
 | 
				
			||||||
 | 
					                    'label': '',
 | 
				
			||||||
 | 
					                    'required': False}
 | 
				
			||||||
        defaults.update(kwargs)
 | 
					        defaults.update(kwargs)
 | 
				
			||||||
        #del defaults.required
 | 
					        #del defaults.required
 | 
				
			||||||
        return super().formfield(**defaults)
 | 
					        return super().formfield(**defaults)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def from_db_value(self, value, expr, conn):
 | 
				
			||||||
 | 
					        if value is None:
 | 
				
			||||||
 | 
					            self.selected_choices = []
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.selected_choices = value.split(",")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_prep_value(self, value):
 | 
				
			||||||
 | 
					        if value is None:
 | 
				
			||||||
 | 
					            return ""
 | 
				
			||||||
 | 
					        if not isinstance(value,list):
 | 
				
			||||||
 | 
					            print("?! CommaSepChoiceField -> ",value)
 | 
				
			||||||
 | 
					            return ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ",".join(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Source(models.Model):
 | 
					class Source(models.Model):
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
        A Source is a source of media. Currently, this is either a YouTube channel
 | 
					        A Source is a source of media. Currently, this is either a YouTube channel
 | 
				
			||||||
| 
						 | 
					@ -167,7 +204,6 @@ class Source(models.Model):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # as stolen from: https://wiki.sponsor.ajay.app/w/Types / https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/postprocessor/sponsorblock.py
 | 
					    # 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 = (
 | 
					    SPONSORBLOCK_CATEGORIES_CHOICES = (
 | 
				
			||||||
        ('all', 'All'),
 | 
					 | 
				
			||||||
        ('sponsor', 'Sponsor'),
 | 
					        ('sponsor', 'Sponsor'),
 | 
				
			||||||
        ('intro', 'Intermission/Intro Animation'),
 | 
					        ('intro', 'Intermission/Intro Animation'),
 | 
				
			||||||
        ('outro', 'Endcards/Credits'),
 | 
					        ('outro', 'Endcards/Credits'),
 | 
				
			||||||
| 
						 | 
					@ -179,8 +215,13 @@ class Source(models.Model):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    sponsorblock_categories = CommaSepChoiceField(
 | 
					    sponsorblock_categories = CommaSepChoiceField(
 | 
				
			||||||
 | 
					            _(''),
 | 
				
			||||||
            possible_choices=SPONSORBLOCK_CATEGORIES_CHOICES,
 | 
					            possible_choices=SPONSORBLOCK_CATEGORIES_CHOICES,
 | 
				
			||||||
            default="all"
 | 
					            all_choice="all",
 | 
				
			||||||
 | 
					            allow_all=True,
 | 
				
			||||||
 | 
					            all_label="(all options)",
 | 
				
			||||||
 | 
					            default="all",
 | 
				
			||||||
 | 
					            help_text=_("Select the sponsorblocks you want to enforce")
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    embed_metadata = models.BooleanField(
 | 
					    embed_metadata = models.BooleanField(
 | 
				
			||||||
| 
						 | 
					@ -1380,7 +1421,9 @@ class Media(models.Model):
 | 
				
			||||||
                                    f'no valid format available')
 | 
					                                    f'no valid format available')
 | 
				
			||||||
        # Download the media with youtube-dl
 | 
					        # Download the media with youtube-dl
 | 
				
			||||||
        download_youtube_media(self.url, format_str, self.source.extension,
 | 
					        download_youtube_media(self.url, format_str, self.source.extension,
 | 
				
			||||||
                               str(self.filepath), self.source.write_json)
 | 
					                               str(self.filepath), self.source.write_json, 
 | 
				
			||||||
 | 
					                               self.source.sponsorblock_categories, self.source.embed_thumbnail,
 | 
				
			||||||
 | 
					                               self.source.embed_metadata, self.source.enable_sponsorblock)
 | 
				
			||||||
        # Return the download paramaters
 | 
					        # Return the download paramaters
 | 
				
			||||||
        return format_str, self.source.extension
 | 
					        return format_str, self.source.extension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,6 @@
 | 
				
			||||||
    <label for="{{ option.value }}">{{option.label}}</label>-->
 | 
					    <label for="{{ option.value }}">{{option.label}}</label>-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<label>
 | 
					<label>
 | 
				
			||||||
    <input type="{{ option.type }}" name="{{ option.name }}" value="{{ option.value }}" id="{{ option.value }}">
 | 
					    <input type="{{ option.type }}" name="{{ option.name }}" value="{{ option.value }}" id="{{ option.value }}" {% if option.checked %}checked{% endif %}>
 | 
				
			||||||
    <span>{{option.label}}</span>
 | 
					    <span>{{option.label}}</span>
 | 
				
			||||||
</label>
 | 
					</label>
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
{% for group, options, index in widget.optgroups %}
 | 
					</label>
 | 
				
			||||||
    {% for option in options %}
 | 
					{% for option in widget.multipleChoiceProperties %}
 | 
				
			||||||
    {% include option.template_name with option=option %}
 | 
					    {% include option.template_name with option=option %}
 | 
				
			||||||
{% endfor %}
 | 
					{% endfor %}
 | 
				
			||||||
{% endfor %}
 | 
					<label>
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,9 @@ def get_media_info(url):
 | 
				
			||||||
    return response
 | 
					    return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def download_media(url, media_format, extension, output_file, info_json, sponsor_categories="all", embed_thumbnail=False, embed_metadata=False, skip_sponsors=True):
 | 
					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.
 | 
					        Downloads a YouTube URL to a file on disk.
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
| 
						 | 
					@ -122,13 +124,13 @@ def download_media(url, media_format, extension, output_file, info_json, sponsor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    opts = get_yt_opts()
 | 
					    opts = get_yt_opts()
 | 
				
			||||||
    if embed_thumbnail:
 | 
					    if embed_thumbnail:
 | 
				
			||||||
        ytopts['postprocessors'].push({'key': 'EmbedThumbnail'})
 | 
					        ytopts['postprocessors'].append({'key': 'EmbedThumbnail'})
 | 
				
			||||||
    if embed_metadata:
 | 
					    if embed_metadata:
 | 
				
			||||||
        ffmdopt["embed-metadata"] = True
 | 
					        ffmdopt["add_metadata"] = True
 | 
				
			||||||
    if skip_sponsors:
 | 
					    if skip_sponsors:
 | 
				
			||||||
        ytopts['postprocessors'].push(sbopt)
 | 
					        ytopts['postprocessors'].append(sbopt)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    ytopts['postprocessors'].push(ffmdopt)
 | 
					    ytopts['postprocessors'].append(ffmdopt)
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    opts.update(ytopts)
 | 
					    opts.update(ytopts)
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue