mirror of
				https://github.com/chibicitiberiu/ytsm.git
				synced 2024-02-24 05:43:31 +00:00 
			
		
		
		
	Added admin settings page. Improve dynamic_preferences integration.
This commit is contained in:
		@@ -5,7 +5,7 @@ import sys
 | 
			
		||||
 | 
			
		||||
from django.conf import settings as dj_settings
 | 
			
		||||
 | 
			
		||||
from .management.appconfig import global_prefs
 | 
			
		||||
from .management.appconfig import appconfig
 | 
			
		||||
from .management.jobs.synchronize import schedule_synchronize_global
 | 
			
		||||
from .scheduler import initialize_scheduler
 | 
			
		||||
from django.db.utils import OperationalError
 | 
			
		||||
@@ -40,7 +40,7 @@ def main():
 | 
			
		||||
    __initialize_logger()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
        if appconfig.initialized:
 | 
			
		||||
            initialize_scheduler()
 | 
			
		||||
            schedule_synchronize_global()
 | 
			
		||||
    except OperationalError:
 | 
			
		||||
 
 | 
			
		||||
@@ -33,14 +33,6 @@ class YouTubeAPIKey(StringPreference):
 | 
			
		||||
    required = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@global_preferences_registry.register
 | 
			
		||||
class AllowCDN(BooleanPreference):
 | 
			
		||||
    section = general
 | 
			
		||||
    name = 'allow_cdn'
 | 
			
		||||
    default = True
 | 
			
		||||
    required = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@global_preferences_registry.register
 | 
			
		||||
class AllowRegistrations(BooleanPreference):
 | 
			
		||||
    section = general
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,34 @@
 | 
			
		||||
from dynamic_preferences.registries import global_preferences_registry
 | 
			
		||||
from YtManagerApp.dynamic_preferences_registry import Initialized, YouTubeAPIKey, AllowRegistrations, SyncSchedule, SchedulerConcurrency
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AppConfig(object):
 | 
			
		||||
    # Properties
 | 
			
		||||
    props = {
 | 
			
		||||
        'initialized': Initialized,
 | 
			
		||||
        'youtube_api_key': YouTubeAPIKey,
 | 
			
		||||
        'allow_registrations': AllowRegistrations,
 | 
			
		||||
        'sync_schedule': SyncSchedule,
 | 
			
		||||
        'concurrency': SchedulerConcurrency
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Init
 | 
			
		||||
    def __init__(self, pref_manager):
 | 
			
		||||
        self.__pref_manager = pref_manager
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, item):
 | 
			
		||||
        prop_class = AppConfig.props[item]
 | 
			
		||||
        prop_full_name = prop_class.section.name + "__" + prop_class.name
 | 
			
		||||
        return self.__pref_manager[prop_full_name]
 | 
			
		||||
 | 
			
		||||
    def __setattr__(self, key, value):
 | 
			
		||||
        if key in AppConfig.props:
 | 
			
		||||
            prop_class = AppConfig.props[key]
 | 
			
		||||
            prop_full_name = prop_class.section.name + "__" + prop_class.name
 | 
			
		||||
            self.__pref_manager[prop_full_name] = value
 | 
			
		||||
        else:
 | 
			
		||||
            super().__setattr__(key, value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
global_prefs = global_preferences_registry.manager()
 | 
			
		||||
appconfig = AppConfig(global_prefs)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ from threading import Lock
 | 
			
		||||
from apscheduler.triggers.cron import CronTrigger
 | 
			
		||||
 | 
			
		||||
from YtManagerApp import scheduler
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all, downloader_process_subscription
 | 
			
		||||
from YtManagerApp.models import *
 | 
			
		||||
from YtManagerApp.utils import youtube
 | 
			
		||||
@@ -149,7 +149,7 @@ def synchronize_subscription(subscription: Subscription):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def schedule_synchronize_global():
 | 
			
		||||
    trigger = CronTrigger.from_crontab(global_prefs['scheduler__synchronization_schedule'])
 | 
			
		||||
    trigger = CronTrigger.from_crontab(appconfig.sync_schedule)
 | 
			
		||||
    job = scheduler.scheduler.add_job(synchronize, trigger, max_instances=1, coalesce=True)
 | 
			
		||||
    log.info('Scheduled synchronize job job=%s', job.id)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import logging
 | 
			
		||||
from typing import Callable, Union, Any, Optional
 | 
			
		||||
import os
 | 
			
		||||
from typing import Callable, Union, Any, Optional
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models.functions import Lower
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.utils import youtube
 | 
			
		||||
 | 
			
		||||
# help_text = user shown text
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import logging
 | 
			
		||||
 | 
			
		||||
from apscheduler.schedulers.background import BackgroundScheduler
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
 | 
			
		||||
scheduler: BackgroundScheduler = None
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ def initialize_scheduler():
 | 
			
		||||
    executors = {
 | 
			
		||||
        'default': {
 | 
			
		||||
            'type': 'threadpool',
 | 
			
		||||
            'max_workers': global_prefs['scheduler__concurrency']
 | 
			
		||||
            'max_workers': appconfig.concurrency
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    job_defaults = {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  height: 2rem;
 | 
			
		||||
  line-height: 2rem;
 | 
			
		||||
  padding: 0rem 1rem;
 | 
			
		||||
  padding: 0 1rem;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-content: center;
 | 
			
		||||
  font-size: 10pt; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
"version": 3,
 | 
			
		||||
"mappings": "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",
 | 
			
		||||
"mappings": "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",
 | 
			
		||||
"sources": ["style.scss"],
 | 
			
		||||
"names": [],
 | 
			
		||||
"file": "style.css"
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ $accent-color: #007bff;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    height: 2rem;
 | 
			
		||||
    line-height: 2rem;
 | 
			
		||||
    padding: 0rem 1rem;
 | 
			
		||||
    padding: 0 1rem;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-content: center;
 | 
			
		||||
    font-size: 10pt;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,9 @@
 | 
			
		||||
                            </a>
 | 
			
		||||
                            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="userDropdown">
 | 
			
		||||
                                <a class="dropdown-item" href="{% url 'settings' %}">Settings</a>
 | 
			
		||||
                                {% if request.user.is_superuser %}
 | 
			
		||||
                                    <a class="dropdown-item" href="{% url 'admin_settings' %}">Admin settings</a>
 | 
			
		||||
                                {% endif %}
 | 
			
		||||
                                <div class="dropdown-divider"></div>
 | 
			
		||||
                                <a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
 | 
			
		||||
                            </div>
 | 
			
		||||
@@ -54,9 +57,11 @@
 | 
			
		||||
                        <li class="nav-item">
 | 
			
		||||
                            <a class="nav-link" href="{% url 'login' %}">Login</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li class="nav-item">
 | 
			
		||||
                            <a class="nav-link" href="{% url 'register' %}">Register</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        {% if global_preferences.general__allow_registrations %}
 | 
			
		||||
                            <li class="nav-item">
 | 
			
		||||
                                <a class="nav-link" href="{% url 'register' %}">Register</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								app/YtManagerApp/templates/YtManagerApp/settings_admin.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/YtManagerApp/templates/YtManagerApp/settings_admin.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
{% extends "YtManagerApp/master_default.html" %}
 | 
			
		||||
{% load crispy_forms_tags %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <h1>Admin settings</h1>
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock body %}
 | 
			
		||||
@@ -24,9 +24,17 @@
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        <h5>Register</h5>
 | 
			
		||||
        {% if not global_preferences.general__allow_registrations %}
 | 
			
		||||
            <div class="alert alert-danger" role="alert">
 | 
			
		||||
                Registrations are disabled by the administrator!
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
        {% else %}
 | 
			
		||||
 | 
			
		||||
            <h5>Register</h5>
 | 
			
		||||
            {% crispy form %}
 | 
			
		||||
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -23,7 +23,7 @@ from .views.actions import SyncNowView, DeleteVideoFilesView, DownloadVideoFiles
 | 
			
		||||
from .views.auth import ExtendedLoginView, RegisterView, RegisterDoneView
 | 
			
		||||
from .views.index import index, ajax_get_tree, ajax_get_videos, CreateFolderModal, UpdateFolderModal, DeleteFolderModal, \
 | 
			
		||||
    CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal
 | 
			
		||||
from .views.settings import SettingsView
 | 
			
		||||
from .views.settings import SettingsView, AdminSettingsView
 | 
			
		||||
from .views import first_time
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
@@ -59,8 +59,9 @@ urlpatterns = [
 | 
			
		||||
    # Pages
 | 
			
		||||
    path('', index, name='home'),
 | 
			
		||||
    path('settings/', SettingsView.as_view(), name='settings'),
 | 
			
		||||
    path('admin_settings/', AdminSettingsView.as_view(), name='admin_settings'),
 | 
			
		||||
 | 
			
		||||
    # First time setup
 | 
			
		||||
                  # First time setup
 | 
			
		||||
    path('first_time/step0_welcome', first_time.Step0WelcomeView.as_view(), name='first_time_0'),
 | 
			
		||||
    path('first_time/step1_apikey', first_time.Step1ApiKeyView.as_view(), name='first_time_1'),
 | 
			
		||||
    path('first_time/step2_admin', first_time.Step2SetupAdminUserView.as_view(), name='first_time_2'),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,8 @@ class YoutubeAPI(YouTube):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def build_public() -> 'YoutubeAPI':
 | 
			
		||||
        return YoutubeAPI(key=settings.YOUTUBE_API_KEY)
 | 
			
		||||
        from YtManagerApp.management.appconfig import youtube_api_key
 | 
			
		||||
        return YoutubeAPI(key=youtube_api_key)
 | 
			
		||||
 | 
			
		||||
    # @staticmethod
 | 
			
		||||
    # def build_oauth() -> 'YoutubeAPI':
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,12 @@ from django.contrib.auth import login, authenticate
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.contrib.auth.views import LoginView
 | 
			
		||||
from django.http import HttpResponseForbidden
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.views.generic import FormView, TemplateView
 | 
			
		||||
 | 
			
		||||
from .forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExtendedLoginView(LoginView):
 | 
			
		||||
@@ -33,6 +35,12 @@ class RegisterView(FormView):
 | 
			
		||||
        context['is_first_user'] = (User.objects.count() == 0)
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        if not appconfig.allow_registrations:
 | 
			
		||||
            return HttpResponseForbidden("Registrations are disabled!")
 | 
			
		||||
 | 
			
		||||
        return super().post(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RegisterDoneView(LoginRequiredMixin, TemplateView):
 | 
			
		||||
    template_name = 'registration/register_done.html'
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ from django.shortcuts import redirect
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.views.generic import FormView
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm
 | 
			
		||||
from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, UserCreationForm
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +18,7 @@ class WizardStepMixin(object):
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
 | 
			
		||||
        # Prevent access if application is already initialized
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
        if appconfig.initialized:
 | 
			
		||||
            logger.debug(f"Attempted to access {request.path}, but first time setup already run. Redirected to home "
 | 
			
		||||
                         f"page.")
 | 
			
		||||
            return redirect('home')
 | 
			
		||||
@@ -26,7 +26,7 @@ class WizardStepMixin(object):
 | 
			
		||||
        return super().get(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
        if appconfig.initialized:
 | 
			
		||||
            logger.debug(f"Attempted to post {request.path}, but first time setup already run.")
 | 
			
		||||
            return HttpResponseForbidden()
 | 
			
		||||
        return super().post(request, *args, **kwargs)
 | 
			
		||||
@@ -51,14 +51,14 @@ class Step1ApiKeyView(WizardStepMixin, FormView):
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        initial = super().get_initial()
 | 
			
		||||
        initial['api_key'] = global_prefs['general__youtube_api_key']
 | 
			
		||||
        initial['api_key'] = appconfig.youtube_api_key
 | 
			
		||||
        return initial
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        key = form.cleaned_data['api_key']
 | 
			
		||||
        # TODO: validate key
 | 
			
		||||
        if key is not None and len(key) > 0:
 | 
			
		||||
            global_prefs['general__youtube_api_key'] = key
 | 
			
		||||
            appconfig.youtube_api_key = key
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
@@ -100,9 +100,6 @@ class Step2SetupAdminUserView(WizardStepMixin, FormView):
 | 
			
		||||
 | 
			
		||||
        return super().get(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def form_invalid(self, form):
 | 
			
		||||
        print("FORM INVALID!")
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        if isinstance(form, ExtendedAuthenticationForm):
 | 
			
		||||
            login(self.request, form.get_user())
 | 
			
		||||
@@ -139,8 +136,8 @@ class Step3ConfigureView(WizardStepMixin, FormView):
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        initial = super().get_initial()
 | 
			
		||||
        initial['allow_registrations'] = global_prefs['general__allow_registrations']
 | 
			
		||||
        initial['sync_schedule'] =  global_prefs['scheduler__synchronization_schedule']
 | 
			
		||||
        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']
 | 
			
		||||
        return initial
 | 
			
		||||
@@ -148,11 +145,11 @@ class Step3ConfigureView(WizardStepMixin, FormView):
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        allow_registrations = form.cleaned_data['allow_registrations']
 | 
			
		||||
        if allow_registrations is not None:
 | 
			
		||||
            global_prefs['general__allow_registrations'] = allow_registrations
 | 
			
		||||
            appconfig.allow_registrations = allow_registrations
 | 
			
		||||
 | 
			
		||||
        sync_schedule = form.cleaned_data['sync_schedule']
 | 
			
		||||
        if sync_schedule is not None and len(sync_schedule) > 0:
 | 
			
		||||
            global_prefs['scheduler__synchronization_schedule'] = sync_schedule
 | 
			
		||||
            appconfig.sync_schedule = sync_schedule
 | 
			
		||||
 | 
			
		||||
        auto_download = form.cleaned_data['auto_download']
 | 
			
		||||
        if auto_download is not None:
 | 
			
		||||
@@ -163,7 +160,7 @@ class Step3ConfigureView(WizardStepMixin, FormView):
 | 
			
		||||
            self.request.user.preferences['downloader__download_path'] = download_location
 | 
			
		||||
 | 
			
		||||
        # Set initialized to true
 | 
			
		||||
        global_prefs['hidden__initialized'] = True
 | 
			
		||||
        appconfig.initialized = True
 | 
			
		||||
        
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,10 @@ import logging
 | 
			
		||||
from crispy_forms.helper import FormHelper
 | 
			
		||||
from crispy_forms.layout import Layout, HTML, Submit, Column
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.auth import authenticate, login
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from YtManagerApp.views.forms.auth import ExtendedUserCreationForm
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.views.forms.auth import ExtendedUserCreationForm
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger("FirstTimeWizard")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								app/YtManagerApp/views/forms/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								app/YtManagerApp/views/forms/settings.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
from crispy_forms.helper import FormHelper
 | 
			
		||||
from crispy_forms.layout import Layout, HTML, Submit
 | 
			
		||||
from django import forms
 | 
			
		||||
 | 
			
		||||
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('<h2>Download settings</h2>'),
 | 
			
		||||
            'auto_download',
 | 
			
		||||
            'download_path',
 | 
			
		||||
            'download_file_pattern',
 | 
			
		||||
            'download_format',
 | 
			
		||||
            'download_order',
 | 
			
		||||
            'download_global_limit',
 | 
			
		||||
            'download_subscription_limit',
 | 
			
		||||
            HTML('<h2>Subtitles download settings</h2>'),
 | 
			
		||||
            'download_subtitles',
 | 
			
		||||
            'download_subtitles_langs',
 | 
			
		||||
            'download_subtitles_all',
 | 
			
		||||
            'download_autogenerated_subtitles',
 | 
			
		||||
            'download_subtitles_format',
 | 
			
		||||
            Submit('submit', value='Save')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AdminSettingsForm(forms.Form):
 | 
			
		||||
 | 
			
		||||
    api_key = forms.CharField(label="YouTube API key")
 | 
			
		||||
 | 
			
		||||
    allow_registrations = forms.BooleanField(
 | 
			
		||||
        label="Allow user registrations",
 | 
			
		||||
        help_text="Disabling this option will prevent anyone from registering to the site.",
 | 
			
		||||
        initial=True,
 | 
			
		||||
        required=False
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    sync_schedule = forms.CharField(
 | 
			
		||||
        label="Synchronization schedule",
 | 
			
		||||
        help_text="How often should the application look for new videos.",
 | 
			
		||||
        initial="5 * * * *",
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    scheduler_concurrency = forms.IntegerField(
 | 
			
		||||
        label="Synchronization concurrency",
 | 
			
		||||
        help_text="How many jobs are executed executed in parallel. Since most jobs are I/O bound (mostly use the hard "
 | 
			
		||||
                  "drive and network), there is no significant advantage to increase it.",
 | 
			
		||||
        initial=2,
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    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(
 | 
			
		||||
            HTML('<h2>General settings</h2>'),
 | 
			
		||||
            'api_key',
 | 
			
		||||
            'allow_registrations',
 | 
			
		||||
            HTML('<h2>Scheduler settings</h2>'),
 | 
			
		||||
            'sync_schedule',
 | 
			
		||||
            'scheduler_concurrency',
 | 
			
		||||
            Submit('submit', value='Save')
 | 
			
		||||
        )
 | 
			
		||||
@@ -10,7 +10,7 @@ from django.views.generic import CreateView, UpdateView, DeleteView, FormView
 | 
			
		||||
from django.views.generic.edit import FormMixin
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from YtManagerApp.management.videos import get_videos
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
from YtManagerApp.models import Subscription, SubscriptionFolder, VIDEO_ORDER_CHOICES, VIDEO_ORDER_MAPPING
 | 
			
		||||
from YtManagerApp.utils import youtube, subscription_file_parser
 | 
			
		||||
from YtManagerApp.views.controls.modal import ModalMixin
 | 
			
		||||
@@ -96,7 +96,7 @@ def __tree_sub_id(sub_id):
 | 
			
		||||
 | 
			
		||||
def index(request: HttpRequest):
 | 
			
		||||
 | 
			
		||||
    if not global_prefs['hidden__initialized']:
 | 
			
		||||
    if not appconfig.initialized:
 | 
			
		||||
        return redirect('first_time_0')
 | 
			
		||||
 | 
			
		||||
    context = {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +1,11 @@
 | 
			
		||||
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.http import HttpResponseForbidden
 | 
			
		||||
from django.urls import reverse_lazy
 | 
			
		||||
from django.views.generic import UpdateView
 | 
			
		||||
from django.views.generic import UpdateView, FormView
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.management.appconfig import appconfig
 | 
			
		||||
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('<h2>Download settings</h2>'),
 | 
			
		||||
            'auto_download',
 | 
			
		||||
            'download_path',
 | 
			
		||||
            'download_file_pattern',
 | 
			
		||||
            'download_format',
 | 
			
		||||
            'download_order',
 | 
			
		||||
            'download_global_limit',
 | 
			
		||||
            'download_subscription_limit',
 | 
			
		||||
            HTML('<h2>Subtitles download settings</h2>'),
 | 
			
		||||
            'download_subtitles',
 | 
			
		||||
            'download_subtitles_langs',
 | 
			
		||||
            'download_subtitles_all',
 | 
			
		||||
            'download_autogenerated_subtitles',
 | 
			
		||||
            'download_subtitles_format',
 | 
			
		||||
            Submit('submit', value='Save')
 | 
			
		||||
        )
 | 
			
		||||
from YtManagerApp.views.forms.settings import SettingsForm, AdminSettingsForm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SettingsView(LoginRequiredMixin, UpdateView):
 | 
			
		||||
@@ -49,3 +17,53 @@ class SettingsView(LoginRequiredMixin, UpdateView):
 | 
			
		||||
    def get_object(self, queryset=None):
 | 
			
		||||
        obj, _ = self.model.objects.get_or_create(user=self.request.user)
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AdminSettingsView(LoginRequiredMixin, FormView):
 | 
			
		||||
    form_class = AdminSettingsForm
 | 
			
		||||
    template_name = 'YtManagerApp/settings_admin.html'
 | 
			
		||||
    success_url = reverse_lazy('home')
 | 
			
		||||
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        if not request.user.is_authenticated or not request.user.is_superuser:
 | 
			
		||||
            return HttpResponseForbidden()
 | 
			
		||||
 | 
			
		||||
        return super().get(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        if not request.user.is_authenticated or not request.user.is_superuser:
 | 
			
		||||
            return HttpResponseForbidden()
 | 
			
		||||
 | 
			
		||||
        return super().post(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
        # TODO: present stats
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user