Fixed user settings page.

This commit is contained in:
Tiberiu Chibici 2018-12-29 20:43:39 +02:00
parent 076d09e294
commit 10fced57e1
8 changed files with 224 additions and 210 deletions

View File

@ -12,8 +12,6 @@ import os
hidden = Section('hidden')
general = Section('general')
scheduler = Section('scheduler')
manager = Section('manager')
downloader = Section('downloader')
# Hidden settings
@ -60,7 +58,6 @@ class SchedulerConcurrency(IntegerPreference):
# User settings
@user_preferences_registry.register
class MarkDeletedAsWatched(BooleanPreference):
section = manager
name = 'mark_deleted_as_watched'
default = True
required = True
@ -68,47 +65,41 @@ class MarkDeletedAsWatched(BooleanPreference):
@user_preferences_registry.register
class AutoDeleteWatched(BooleanPreference):
section = manager
name = 'auto_delete_watched'
name = 'automatically_delete_watched'
default = True
required = True
@user_preferences_registry.register
class AutoDownloadEnabled(BooleanPreference):
section = downloader
name = 'auto_enabled'
name = 'auto_download'
default = True
required = True
@user_preferences_registry.register
class DownloadGlobalLimit(IntegerPreference):
section = downloader
name = 'global_limit'
default = None
name = 'download_global_limit'
default = -1
required = False
@user_preferences_registry.register
class DownloadGlobalSizeLimit(IntegerPreference):
section = downloader
name = 'global_size_limit_mb'
default = None
name = 'download_global_size_limit'
default = -1
required = False
@user_preferences_registry.register
class DownloadSubscriptionLimit(IntegerPreference):
section = downloader
name = 'limit_per_subscription'
name = 'download_subscription_limit'
default = 5
required = False
@user_preferences_registry.register
class DownloadMaxAttempts(IntegerPreference):
section = downloader
name = 'max_download_attempts'
default = 3
required = True
@ -116,7 +107,6 @@ class DownloadMaxAttempts(IntegerPreference):
@user_preferences_registry.register
class DownloadOrder(ChoicePreference):
section = downloader
name = 'download_order'
choices = VIDEO_ORDER_CHOICES
default = 'playlist'
@ -125,7 +115,6 @@ class DownloadOrder(ChoicePreference):
@user_preferences_registry.register
class DownloadPath(StringPreference):
section = downloader
name = 'download_path'
default = os.path.join(settings.DATA_DIR, 'downloads')
required = False
@ -133,7 +122,6 @@ class DownloadPath(StringPreference):
@user_preferences_registry.register
class DownloadFilePattern(StringPreference):
section = downloader
name = 'download_file_pattern'
default = '${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]'
required = True
@ -141,7 +129,6 @@ class DownloadFilePattern(StringPreference):
@user_preferences_registry.register
class DownloadFormat(StringPreference):
section = downloader
name = 'download_format'
default = 'bestvideo+bestaudio'
required = True
@ -149,39 +136,34 @@ class DownloadFormat(StringPreference):
@user_preferences_registry.register
class DownloadSubtitles(BooleanPreference):
section = downloader
name = 'subtitles_enabled'
name = 'download_subtitles'
default = True
required = True
@user_preferences_registry.register
class DownloadAutogeneratedSubtitles(BooleanPreference):
section = downloader
name = 'autogenerated_subtitles'
name = 'download_autogenerated_subtitles'
default = False
required = True
@user_preferences_registry.register
class DownloadAllSubtitles(BooleanPreference):
section = downloader
name = 'all_subtitles'
name = 'download_subtitles_all'
default = False
required = False
@user_preferences_registry.register
class DownloadSubtitlesLangs(StringPreference):
section = downloader
name = 'subtitles_langs'
name = 'download_subtitles_langs'
default = 'en,ro'
required = False
@user_preferences_registry.register
class DownloadSubtitlesFormat(StringPreference):
section = downloader
name = 'subtitles_format'
default = False
name = 'download_subtitles_format'
default = ''
required = False

View File

@ -14,9 +14,9 @@ log = logging.getLogger('downloader')
def __get_subscription_config(sub: Subscription):
user = sub.user
enabled = first_non_null(sub.auto_download, user.preferences['download_enabled'])
enabled = first_non_null(sub.auto_download, user.preferences['auto_download'])
global_limit = user.preferences['download_global_limit']
limit = first_non_null(sub.download_limit, user.preferences['download_limit_per_subscription'])
limit = first_non_null(sub.download_limit, user.preferences['download_subscription_limit'])
order = first_non_null(sub.download_order, user.preferences['download_order'])
order = VIDEO_ORDER_MAPPING[order]

View File

@ -59,9 +59,9 @@ def __build_youtube_dl_params(video: Video):
'outtmpl': output_path,
'writethumbnail': True,
'writedescription': True,
'writesubtitles': user.preferences['subtitles_enabled'],
'writeautomaticsub': user.preferences['autogenerated_subtitles'],
'allsubtitles': user.preferences['all_subtitles'],
'writesubtitles': user.preferences['download_subtitles'],
'writeautomaticsub': user.preferences['download_autogenerated_subtitles'],
'allsubtitles': user.preferences['download_subtitles_all'],
'postprocessors': [
{
'key': 'FFmpegMetadata'
@ -69,12 +69,12 @@ def __build_youtube_dl_params(video: Video):
]
}
sub_langs = user.preferences['subtitles_langs'].split(',')
sub_langs = user.preferences['download_subtitles_langs'].split(',')
sub_langs = [i.strip() for i in sub_langs]
if len(sub_langs) > 0:
youtube_dl_params['subtitleslangs'] = sub_langs
sub_format = user.preferences['subtitles_format']
sub_format = user.preferences['download_subtitles_format']
if len(sub_format) > 0:
youtube_dl_params['subtitlesformat'] = sub_format
@ -93,7 +93,7 @@ def download_video(video: Video, attempt: int = 1):
_lock.acquire()
try:
max_attempts = user.preferences['download_max_attempts']
max_attempts = user.preferences['max_download_attempts']
youtube_dl_params, output_path = __build_youtube_dl_params(video)
with youtube_dl.YoutubeDL(youtube_dl_params) as yt:

View File

@ -73,7 +73,7 @@ def __detect_deleted(subscription: Subscription):
video.downloaded_path = None
# Mark watched?
if user.preferences['MarkDeletedAsWatched']:
if user.preferences['mark_deleted_as_watched']:
video.watched = True
video.save()

View File

@ -30,136 +30,6 @@ VIDEO_ORDER_MAPPING = {
}
class UserSettings(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
mark_deleted_as_watched = models.BooleanField(
null=True, blank=True,
help_text='When a downloaded video is deleted from the system, it will be marked as \'watched\'.')
delete_watched = models.BooleanField(
null=True, blank=True,
help_text='Videos marked as watched are automatically deleted.')
auto_download = models.BooleanField(
null=True, blank=True,
help_text='Enables or disables automatic downloading.')
download_global_limit = models.IntegerField(
null=True, blank=True,
help_text='Limits the total number of videos downloaded (-1 = no limit).')
download_subscription_limit = models.IntegerField(
null=True, blank=True,
help_text='Limits the number of videos downloaded per subscription (-1 = no limit). '
' This setting can be overriden for each individual subscription in the subscription edit dialog.')
download_order = models.CharField(
null=True, blank=True,
max_length=100,
choices=VIDEO_ORDER_CHOICES,
help_text='The order in which videos will be downloaded.'
)
download_path = models.CharField(
null=True, blank=True,
max_length=1024,
help_text='Path on the disk where downloaded videos are stored. '
' You can use environment variables using syntax: <code>${env:...}</code>'
)
download_file_pattern = models.CharField(
null=True, blank=True,
max_length=1024,
help_text='A pattern which describes how downloaded files are organized. Extensions are automatically appended.'
' You can use the following fields, using the <code>${field}</code> syntax:'
' channel, channel_id, playlist, playlist_id, playlist_index, title, id.'
' Example: <code>${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]</code>')
download_format = models.CharField(
null=True, blank=True,
max_length=256,
help_text='Download format that will be passed to youtube-dl. '
' See the <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#format-selection">'
' youtube-dl documentation</a> for more details.')
download_subtitles = models.BooleanField(
null=True, blank=True,
help_text='Enable downloading subtitles for the videos.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.')
download_autogenerated_subtitles = models.BooleanField(
null=True, blank=True,
help_text='Enables downloading the automatically generated subtitle.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.')
download_subtitles_all = models.BooleanField(
null=True, blank=True,
help_text='If enabled, all the subtitles in all the available languages will be downloaded.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.')
download_subtitles_langs = models.CharField(
null=True, blank=True,
max_length=250,
help_text='Comma separated list of languages for which subtitles will be downloaded.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.')
download_subtitles_format = models.CharField(
null=True, blank=True,
max_length=100,
help_text='Subtitles format preference. Examples: srt/ass/best'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.')
@staticmethod
def find_by_user(user: User):
result = UserSettings.objects.filter(user=user)
if len(result) > 0:
return result.first()
return None
def __str__(self):
return str(self.user)
def to_dict(self):
ret = {}
if self.mark_deleted_as_watched is not None:
ret['MarkDeletedAsWatched'] = self.mark_deleted_as_watched
if self.delete_watched is not None:
ret['DeleteWatched'] = self.delete_watched
if self.auto_download is not None:
ret['AutoDownload'] = self.auto_download
if self.download_global_limit is not None:
ret['DownloadGlobalLimit'] = self.download_global_limit
if self.download_subscription_limit is not None:
ret['DownloadSubscriptionLimit'] = self.download_subscription_limit
if self.download_order is not None:
ret['DownloadOrder'] = self.download_order
if self.download_path is not None:
ret['DownloadPath'] = self.download_path
if self.download_file_pattern is not None:
ret['DownloadFilePattern'] = self.download_file_pattern
if self.download_format is not None:
ret['DownloadFormat'] = self.download_format
if self.download_subtitles is not None:
ret['DownloadSubtitles'] = self.download_subtitles
if self.download_autogenerated_subtitles is not None:
ret['DownloadAutogeneratedSubtitles'] = self.download_autogenerated_subtitles
if self.download_subtitles_all is not None:
ret['DownloadSubtitlesAll'] = self.download_subtitles_all
if self.download_subtitles_langs is not None:
ret['DownloadSubtitlesLangs'] = self.download_subtitles_langs
if self.download_subtitles_format is not None:
ret['DownloadSubtitlesFormat'] = self.download_subtitles_format
return ret
class SubscriptionFolder(models.Model):
name = models.CharField(null=False, max_length=250)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)

View File

@ -138,8 +138,8 @@ class Step3ConfigureView(WizardStepMixin, FormView):
initial = super().get_initial()
initial['allow_registrations'] = appconfig.allow_registrations
initial['sync_schedule'] = appconfig.sync_schedule
initial['auto_download'] = self.request.user.preferences['downloader__auto_enabled']
initial['download_location'] = self.request.user.preferences['downloader__download_path']
initial['auto_download'] = self.request.user.preferences['auto_download']
initial['download_location'] = self.request.user.preferences['download_path']
return initial
def form_valid(self, form):
@ -153,11 +153,11 @@ class Step3ConfigureView(WizardStepMixin, FormView):
auto_download = form.cleaned_data['auto_download']
if auto_download is not None:
self.request.user.preferences['downloader__auto_enabled'] = auto_download
self.request.user.preferences['auto_download'] = auto_download
download_location = form.cleaned_data['download_location']
if download_location is not None and len(download_location) > 0:
self.request.user.preferences['downloader__download_path'] = download_location
self.request.user.preferences['download_path'] = download_location
# Set initialized to true
appconfig.initialized = True

View File

@ -2,23 +2,163 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit
from django import forms
from YtManagerApp.models import UserSettings
from YtManagerApp.dynamic_preferences_registry import MarkDeletedAsWatched, AutoDeleteWatched, AutoDownloadEnabled, \
DownloadGlobalLimit, DownloadGlobalSizeLimit, DownloadSubscriptionLimit, DownloadMaxAttempts, DownloadOrder, \
DownloadPath, DownloadFilePattern, DownloadFormat, DownloadSubtitles, DownloadAutogeneratedSubtitles, \
DownloadAllSubtitles, DownloadSubtitlesLangs, DownloadSubtitlesFormat
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.models import VIDEO_ORDER_CHOICES
class SettingsForm(forms.ModelForm):
class Meta:
model = UserSettings
exclude = ['user']
class SettingsForm(forms.Form):
mark_deleted_as_watched = forms.BooleanField(
help_text='When a downloaded video is deleted from the system, it will be marked as \'watched\'.',
initial=MarkDeletedAsWatched.default,
required=False
)
automatically_delete_watched = forms.BooleanField(
help_text='Videos marked as watched are automatically deleted.',
initial=AutoDeleteWatched.default,
required=False
)
auto_download = forms.BooleanField(
help_text='Enables or disables automatic downloading.',
initial=AutoDownloadEnabled.default,
required=False
)
download_global_limit = forms.IntegerField(
help_text='Limits the total number of videos downloaded (-1/unset = no limit).',
initial=DownloadGlobalLimit.default,
required=False
)
download_global_size_limit = forms.IntegerField(
help_text='Limits the total amount of space used in MB (-1/unset = no limit).',
initial=DownloadGlobalSizeLimit.default,
required=False
)
download_subscription_limit = forms.IntegerField(
help_text='Limits the number of videos downloaded per subscription (-1/unset = no limit). '
' This setting can be overriden for each individual subscription in the subscription edit dialog.',
initial=DownloadSubscriptionLimit.default,
required=False
)
max_download_attempts = forms.IntegerField(
help_text='How many times to attempt downloading a video until giving up.',
initial=DownloadMaxAttempts.default,
min_value=1,
required=True
)
download_order = forms.ChoiceField(
help_text='The order in which videos will be downloaded.',
choices=VIDEO_ORDER_CHOICES,
initial=DownloadOrder.default,
required=True
)
download_path = forms.CharField(
help_text='Path on the disk where downloaded videos are stored. '
'You can use environment variables using syntax: <code>${env:...}</code>',
initial=DownloadPath.default,
max_length=1024,
required=True
)
download_file_pattern = forms.CharField(
help_text='A pattern which describes how downloaded files are organized. Extensions are automatically appended.'
' You can use the following fields, using the <code>${field}</code> syntax:'
' channel, channel_id, playlist, playlist_id, playlist_index, title, id.'
' Example: <code>${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]</code>',
initial=DownloadFilePattern.default,
max_length=1024,
required=True
)
download_format = forms.CharField(
help_text='Download format that will be passed to youtube-dl. '
' See the <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#format-selection">'
' youtube-dl documentation</a> for more details.',
initial=DownloadFormat.default,
required=True
)
download_subtitles = forms.BooleanField(
help_text='Enable downloading subtitles for the videos.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.',
initial=DownloadSubtitles.default,
required=False
)
download_autogenerated_subtitles = forms.BooleanField(
help_text='Enables downloading the automatically generated subtitle.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.',
initial=DownloadAutogeneratedSubtitles.default,
required=False
)
download_subtitles_all = forms.BooleanField(
help_text='If enabled, all the subtitles in all the available languages will be downloaded.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.',
initial=DownloadAllSubtitles.default,
required=False
)
download_subtitles_langs = forms.CharField(
help_text='Comma separated list of languages for which subtitles will be downloaded.'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.',
initial=DownloadSubtitlesLangs.default,
required=False
)
download_subtitles_format = forms.CharField(
help_text='Subtitles format preference. Examples: srt/ass/best'
' The flag is passed directly to youtube-dl. You can find more information'
' <a href="https://github.com/rg3/youtube-dl/blob/master/README.md#subtitle-options">here</a>.',
initial=DownloadSubtitlesFormat.default,
required=False
)
ALL_PROPS = [
'mark_deleted_as_watched',
'automatically_delete_watched',
'auto_download',
'download_path',
'download_file_pattern',
'download_format',
'download_order',
'download_global_limit',
'download_global_size_limit',
'download_subscription_limit',
'max_download_attempts',
'download_subtitles',
'download_subtitles_langs',
'download_subtitles_all',
'download_autogenerated_subtitles',
'download_subtitles_format',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-3'
self.helper.field_class = 'col-lg-9'
self.helper.label_class = 'col-lg-6'
self.helper.field_class = 'col-lg-6'
self.helper.layout = Layout(
'mark_deleted_as_watched',
'delete_watched',
'automatically_delete_watched',
HTML('<h2>Download settings</h2>'),
'auto_download',
'download_path',
@ -26,7 +166,9 @@ class SettingsForm(forms.ModelForm):
'download_format',
'download_order',
'download_global_limit',
'download_global_size_limit',
'download_subscription_limit',
'max_download_attempts',
HTML('<h2>Subtitles download settings</h2>'),
'download_subtitles',
'download_subtitles_langs',
@ -36,6 +178,16 @@ class SettingsForm(forms.ModelForm):
Submit('submit', value='Save')
)
@staticmethod
def get_initials(user):
return {
x: user.preferences[x] for x in SettingsForm.ALL_PROPS
}
def save(self, user):
for prop in SettingsForm.ALL_PROPS:
user.preferences[prop] = self.cleaned_data[prop]
class AdminSettingsForm(forms.Form):
@ -78,3 +230,29 @@ class AdminSettingsForm(forms.Form):
'scheduler_concurrency',
Submit('submit', value='Save')
)
@staticmethod
def get_initials():
return {
'api_key': appconfig.youtube_api_key,
'allow_registrations': appconfig.allow_registrations,
'sync_schedule': appconfig.sync_schedule,
'scheduler_concurrency': appconfig.concurrency,
}
def save(self):
api_key = self.cleaned_data['api_key']
if api_key is not None and len(api_key) > 0:
appconfig.youtube_api_key = api_key
allow_registrations = self.cleaned_data['allow_registrations']
if allow_registrations is not None:
appconfig.allow_registrations = allow_registrations
sync_schedule = self.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
appconfig.sync_schedule = sync_schedule
concurrency = self.cleaned_data['scheduler_concurrency']
if concurrency is not None:
appconfig.concurrency = concurrency

View File

@ -1,22 +1,24 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseForbidden
from django.urls import reverse_lazy
from django.views.generic import UpdateView, FormView
from django.views.generic import FormView
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.models import UserSettings
from YtManagerApp.views.forms.settings import SettingsForm, AdminSettingsForm
class SettingsView(LoginRequiredMixin, UpdateView):
class SettingsView(LoginRequiredMixin, FormView):
form_class = SettingsForm
model = UserSettings
template_name = 'YtManagerApp/settings.html'
success_url = reverse_lazy('home')
def get_object(self, queryset=None):
obj, _ = self.model.objects.get_or_create(user=self.request.user)
return obj
def get_initial(self):
initial = super().get_initial()
initial.update(SettingsForm.get_initials(self.request.user))
return initial
def form_valid(self, form):
form.save(self.request.user)
return super().form_valid(form)
class AdminSettingsView(LoginRequiredMixin, FormView):
@ -37,27 +39,9 @@ class AdminSettingsView(LoginRequiredMixin, FormView):
def get_initial(self):
initial = super().get_initial()
initial['api_key'] = appconfig.youtube_api_key
initial['allow_registrations'] = appconfig.allow_registrations
initial['sync_schedule'] = appconfig.sync_schedule
initial['scheduler_concurrency'] = appconfig.concurrency
initial.update(AdminSettingsForm.get_initials())
return initial
def form_valid(self, form):
api_key = form.cleaned_data['api_key']
if api_key is not None and len(api_key) > 0:
appconfig.youtube_api_key = api_key
allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None:
appconfig.allow_registrations = allow_registrations
sync_schedule = form.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
appconfig.sync_schedule = sync_schedule
concurrency = form.cleaned_data['scheduler_concurrency']
if concurrency is not None:
appconfig.concurrency = concurrency
form.save()
return super().form_valid(form)