diff --git a/app/YtManagerApp/appmain.py b/app/YtManagerApp/appmain.py
index 8651670..03e3ae7 100644
--- a/app/YtManagerApp/appmain.py
+++ b/app/YtManagerApp/appmain.py
@@ -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:
diff --git a/app/YtManagerApp/dynamic_preferences_registry.py b/app/YtManagerApp/dynamic_preferences_registry.py
index 895f158..d8a3235 100644
--- a/app/YtManagerApp/dynamic_preferences_registry.py
+++ b/app/YtManagerApp/dynamic_preferences_registry.py
@@ -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
diff --git a/app/YtManagerApp/management/appconfig.py b/app/YtManagerApp/management/appconfig.py
index 1743fd6..43e4137 100644
--- a/app/YtManagerApp/management/appconfig.py
+++ b/app/YtManagerApp/management/appconfig.py
@@ -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)
diff --git a/app/YtManagerApp/management/jobs/synchronize.py b/app/YtManagerApp/management/jobs/synchronize.py
index d55636a..bd6814c 100644
--- a/app/YtManagerApp/management/jobs/synchronize.py
+++ b/app/YtManagerApp/management/jobs/synchronize.py
@@ -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)
diff --git a/app/YtManagerApp/models.py b/app/YtManagerApp/models.py
index 0d5c7cf..c500f36 100644
--- a/app/YtManagerApp/models.py
+++ b/app/YtManagerApp/models.py
@@ -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
diff --git a/app/YtManagerApp/scheduler.py b/app/YtManagerApp/scheduler.py
index ca6958e..92514f9 100644
--- a/app/YtManagerApp/scheduler.py
+++ b/app/YtManagerApp/scheduler.py
@@ -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 = {
diff --git a/app/YtManagerApp/static/YtManagerApp/css/style.css b/app/YtManagerApp/static/YtManagerApp/css/style.css
index c3a4aca..b18229c 100644
--- a/app/YtManagerApp/static/YtManagerApp/css/style.css
+++ b/app/YtManagerApp/static/YtManagerApp/css/style.css
@@ -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; }
diff --git a/app/YtManagerApp/static/YtManagerApp/css/style.css.map b/app/YtManagerApp/static/YtManagerApp/css/style.css.map
index a16ab80..57051a8 100644
--- a/app/YtManagerApp/static/YtManagerApp/css/style.css.map
+++ b/app/YtManagerApp/static/YtManagerApp/css/style.css.map
@@ -1,6 +1,6 @@
{
"version": 3,
-"mappings": "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",
+"mappings": "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",
"sources": ["style.scss"],
"names": [],
"file": "style.css"
diff --git a/app/YtManagerApp/static/YtManagerApp/css/style.scss b/app/YtManagerApp/static/YtManagerApp/css/style.scss
index 33a9fb9..83b0400 100644
--- a/app/YtManagerApp/static/YtManagerApp/css/style.scss
+++ b/app/YtManagerApp/static/YtManagerApp/css/style.scss
@@ -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;
diff --git a/app/YtManagerApp/templates/YtManagerApp/master_default.html b/app/YtManagerApp/templates/YtManagerApp/master_default.html
index 6b025f6..3f23b69 100644
--- a/app/YtManagerApp/templates/YtManagerApp/master_default.html
+++ b/app/YtManagerApp/templates/YtManagerApp/master_default.html
@@ -46,6 +46,9 @@
@@ -54,9 +57,11 @@
Login
-
- Register
-
+ {% if global_preferences.general__allow_registrations %}
+
+ Register
+
+ {% endif %}
{% endif %}
diff --git a/app/YtManagerApp/templates/YtManagerApp/settings_admin.html b/app/YtManagerApp/templates/YtManagerApp/settings_admin.html
new file mode 100644
index 0000000..35bf333
--- /dev/null
+++ b/app/YtManagerApp/templates/YtManagerApp/settings_admin.html
@@ -0,0 +1,11 @@
+{% extends "YtManagerApp/master_default.html" %}
+{% load crispy_forms_tags %}
+
+{% block body %}
+
+
+
Admin settings
+ {% crispy form %}
+
+
+{% endblock body %}
\ No newline at end of file
diff --git a/app/YtManagerApp/templates/registration/register.html b/app/YtManagerApp/templates/registration/register.html
index 73f9c99..a8c43d0 100644
--- a/app/YtManagerApp/templates/registration/register.html
+++ b/app/YtManagerApp/templates/registration/register.html
@@ -24,9 +24,17 @@
{% endif %}
{% endif %}
- Register
+ {% if not global_preferences.general__allow_registrations %}
+
+ Registrations are disabled by the administrator!
+
- {% crispy form %}
+ {% else %}
+
+ Register
+ {% crispy form %}
+
+ {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/app/YtManagerApp/urls.py b/app/YtManagerApp/urls.py
index 3ca0bef..dc487c1 100644
--- a/app/YtManagerApp/urls.py
+++ b/app/YtManagerApp/urls.py
@@ -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'),
diff --git a/app/YtManagerApp/utils/youtube.py b/app/YtManagerApp/utils/youtube.py
index 9e735ff..e2d7576 100644
--- a/app/YtManagerApp/utils/youtube.py
+++ b/app/YtManagerApp/utils/youtube.py
@@ -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':
diff --git a/app/YtManagerApp/views/auth.py b/app/YtManagerApp/views/auth.py
index 1a39429..ec668fe 100644
--- a/app/YtManagerApp/views/auth.py
+++ b/app/YtManagerApp/views/auth.py
@@ -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'
diff --git a/app/YtManagerApp/views/first_time.py b/app/YtManagerApp/views/first_time.py
index beae807..9b20dd8 100644
--- a/app/YtManagerApp/views/first_time.py
+++ b/app/YtManagerApp/views/first_time.py
@@ -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)
diff --git a/app/YtManagerApp/views/forms/first_time.py b/app/YtManagerApp/views/forms/first_time.py
index 128191a..6f4dfae 100644
--- a/app/YtManagerApp/views/forms/first_time.py
+++ b/app/YtManagerApp/views/forms/first_time.py
@@ -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")
diff --git a/app/YtManagerApp/views/forms/settings.py b/app/YtManagerApp/views/forms/settings.py
new file mode 100644
index 0000000..b22e096
--- /dev/null
+++ b/app/YtManagerApp/views/forms/settings.py
@@ -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('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 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('General settings
'),
+ 'api_key',
+ 'allow_registrations',
+ HTML('Scheduler settings
'),
+ 'sync_schedule',
+ 'scheduler_concurrency',
+ Submit('submit', value='Save')
+ )
diff --git a/app/YtManagerApp/views/index.py b/app/YtManagerApp/views/index.py
index 2a0c791..49a2909 100644
--- a/app/YtManagerApp/views/index.py
+++ b/app/YtManagerApp/views/index.py
@@ -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 = {
diff --git a/app/YtManagerApp/views/settings.py b/app/YtManagerApp/views/settings.py
index f7a019b..ac4f8ad 100644
--- a/app/YtManagerApp/views/settings.py
+++ b/app/YtManagerApp/views/settings.py
@@ -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('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')
- )
+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)