diff --git a/app/YtManager/settings.py b/app/YtManager/settings.py index ec966e5..4a59e6d 100644 --- a/app/YtManager/settings.py +++ b/app/YtManager/settings.py @@ -32,6 +32,8 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', + 'dynamic_preferences', + 'dynamic_preferences.users.apps.UserPreferencesConfig', ] MIDDLEWARE = [ @@ -58,6 +60,7 @@ TEMPLATES = [ 'django.template.context_processors.media', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'dynamic_preferences.processors.global_preferences', ], }, }, diff --git a/app/YtManagerApp/dynamic_preferences_registry.py b/app/YtManagerApp/dynamic_preferences_registry.py new file mode 100644 index 0000000..9dcbb4d --- /dev/null +++ b/app/YtManagerApp/dynamic_preferences_registry.py @@ -0,0 +1,158 @@ +from dynamic_preferences.types import BooleanPreference, StringPreference, IntegerPreference, ChoicePreference +from dynamic_preferences.preferences import Section +from dynamic_preferences.registries import global_preferences_registry +from dynamic_preferences.users.registries import user_preferences_registry + +from YtManagerApp.models import VIDEO_ORDER_CHOICES + +# we create some section objects to link related preferences together + +hidden = Section('hidden') +general = Section('general') +scheduler = Section('scheduler') +manager = Section('manager') +downloader = Section('downloader') + +# Hidden settings +@global_preferences_registry.register +class Initialized(BooleanPreference): + section = hidden + name = 'initialized' + default = False + +# General settings +@global_preferences_registry.register +class YouTubeAPIKey(StringPreference): + section = general + name = 'youtube_api_key' + default = 'AIzaSyBabzE4Bup77WexdLMa9rN9z-wJidEfNX8' + required = True + +@global_preferences_registry.register +class SyncSchedule(StringPreference): + section = scheduler + name = 'synchronization_schedule' + default = '5 * * * *' # hourly + required = True + +@global_preferences_registry.register +class SchedulerConcurrency(IntegerPreference): + section = scheduler + name = 'concurrency' + default = 2 + required = True + +# User settings + +@user_preferences_registry.register +class MarkDeletedAsWatched(BooleanPreference): + section = manager + name = 'mark_deleted_as_watched' + default = True + required = True + +@user_preferences_registry.register +class AutoDeleteWatched(BooleanPreference): + section = manager + name = 'auto_delete_watched' + default = True + required = True + +@user_preferences_registry.register +class AutoDownloadEnabled(BooleanPreference): + section = downloader + name = 'enabled' + default = True + required = True + +@user_preferences_registry.register +class DownloadGlobalLimit(IntegerPreference): + section = downloader + name = 'global_limit' + default = None + required = False + +@user_preferences_registry.register +class DownloadGlobalSizeLimit(IntegerPreference): + section = downloader + name = 'global_size_limit_mb' + default = None + required = False + +@user_preferences_registry.register +class DownloadSubscriptionLimit(IntegerPreference): + section = downloader + name = 'limit_per_subscription' + default = 5 + required = False + +@user_preferences_registry.register +class DownloadMaxAttempts(IntegerPreference): + section = downloader + name = 'max_attempts' + default = 3 + required = True + +@user_preferences_registry.register +class DownloadOrder(ChoicePreference): + section = downloader + name = 'order' + choices = VIDEO_ORDER_CHOICES + default = 'playlist' + required = True + +@user_preferences_registry.register +class DownloadPath(StringPreference): + section = downloader + name = 'path' + default = None + required = False + +@user_preferences_registry.register +class DownloadFilePattern(StringPreference): + section = downloader + name = 'file_pattern' + default = '${channel}/${playlist}/S01E${playlist_index} - ${title} [${id}]' + required = True + +@user_preferences_registry.register +class DownloadFormat(StringPreference): + section = downloader + name = 'format' + default = 'bestvideo+bestaudio' + required = True + +@user_preferences_registry.register +class DownloadSubtitles(BooleanPreference): + section = downloader + name = 'subtitles_enabled' + default = True + required = True + +@user_preferences_registry.register +class DownloadAutogeneratedSubtitles(BooleanPreference): + section = downloader + name = 'autogenerated_subtitles' + default = False + required = True + +@user_preferences_registry.register +class DownloadAllSubtitles(BooleanPreference): + section = downloader + name = 'all_subtitles' + default = False + required = False + +@user_preferences_registry.register +class DownloadSubtitlesLangs(StringPreference): + section = downloader + name = 'subtitles_langs' + default = 'en,ro' + required = False + +@user_preferences_registry.register +class DownloadSubtitlesFormat(StringPreference): + section = downloader + name = 'subtitles_format' + default = False + required = False \ No newline at end of file diff --git a/app/YtManagerApp/templates/YtManagerApp/first_time_setup/0_welcome.html b/app/YtManagerApp/templates/YtManagerApp/first_time_setup/0_welcome.html new file mode 100644 index 0000000..bad5b7a --- /dev/null +++ b/app/YtManagerApp/templates/YtManagerApp/first_time_setup/0_welcome.html @@ -0,0 +1,12 @@ +{% extends "YtManagerApp/master_default.html" %} +{% load crispy_forms_tags %} + +{% block body %} + +
+

Welcome

+

This wizard will guide you through setting up the application.

+ {% crispy form %} +
+ +{% endblock body %} \ No newline at end of file diff --git a/app/YtManagerApp/templates/YtManagerApp/first_time_setup/9_done.html b/app/YtManagerApp/templates/YtManagerApp/first_time_setup/9_done.html new file mode 100644 index 0000000..43e1fdb --- /dev/null +++ b/app/YtManagerApp/templates/YtManagerApp/first_time_setup/9_done.html @@ -0,0 +1,12 @@ +{% extends "YtManagerApp/master_default.html" %} +{% load crispy_forms_tags %} + +{% block body %} + +
+

Done!

+

The application is now ready to use!

+ {% crispy form %} +
+ +{% endblock body %} \ No newline at end of file diff --git a/app/YtManagerApp/views/first_time.py b/app/YtManagerApp/views/first_time.py new file mode 100644 index 0000000..f7a019b --- /dev/null +++ b/app/YtManagerApp/views/first_time.py @@ -0,0 +1,51 @@ +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, HTML, Submit +from django import forms +from django.contrib.auth.mixins import LoginRequiredMixin +from django.urls import reverse_lazy +from django.views.generic import UpdateView + +from YtManagerApp.models import UserSettings + + +class SettingsForm(forms.ModelForm): + class Meta: + model = UserSettings + exclude = ['user'] + + 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.layout = Layout( + 'mark_deleted_as_watched', + 'delete_watched', + HTML('

Download settings

'), + 'auto_download', + 'download_path', + 'download_file_pattern', + 'download_format', + 'download_order', + 'download_global_limit', + 'download_subscription_limit', + HTML('

Subtitles download settings

'), + 'download_subtitles', + 'download_subtitles_langs', + 'download_subtitles_all', + 'download_autogenerated_subtitles', + 'download_subtitles_format', + Submit('submit', value='Save') + ) + + +class SettingsView(LoginRequiredMixin, UpdateView): + 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 diff --git a/config/config.ini b/config/config.ini index e709861..727f047 100644 --- a/config/config.ini +++ b/config/config.ini @@ -4,7 +4,7 @@ ; The global section contains settings that apply to the entire server [global] -;Debug=False +Debug=True ; This is the folder where thumbnails will be downloaded. By default project_root/data/media is used. ;MediaRoot= diff --git a/requirements.txt b/requirements.txt index 9ee6d74..d78e284 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ apscheduler gunicorn django django-crispy-forms +django-dynamic-preferences dj_database_url youtube-dl google-api-python-client