Major refactor of codebase.

This commit is contained in:
2020-04-11 00:30:24 +03:00
parent fd5d05232f
commit 1022ce353c
33 changed files with 1408 additions and 963 deletions

View File

@ -3,12 +3,12 @@ from django.http import JsonResponse
from django.views.generic import View
from YtManagerApp.models import Video
from YtManagerApp.scheduler.jobs.synchronize_job import SynchronizeJob
from YtManagerApp.services import Services
class SyncNowView(LoginRequiredMixin, View):
def post(self, *args, **kwargs):
SynchronizeJob.schedule_now()
Services.subscriptionManager().synchronize_all()
return JsonResponse({
'success': True
})

View File

@ -36,7 +36,7 @@ class RegisterView(FormView):
return context
def post(self, request, *args, **kwargs):
if not Services.appConfig.allow_registrations:
if not Services.appConfig().allow_registrations:
return HttpResponseForbidden("Registrations are disabled!")
return super().post(request, *args, **kwargs)

View File

@ -8,7 +8,6 @@ from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views.generic import FormView
from YtManagerApp.scheduler.jobs.synchronize_job import SynchronizeJob
from YtManagerApp.services import Services
from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, \
UserCreationForm, LoginForm
@ -16,7 +15,7 @@ from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdm
logger = logging.getLogger("FirstTimeWizard")
class WizardStepMixin:
class WizardStepMixin(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -24,7 +23,7 @@ class WizardStepMixin:
def get(self, request, *args, **kwargs):
# Prevent access if application is already initialized
if Services.appConfig.initialized:
if Services.appConfig().initialized:
logger.debug(f"Attempted to access {request.path}, but first time setup already run. Redirected to home "
f"page.")
return redirect('home')
@ -32,7 +31,7 @@ class WizardStepMixin:
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if Services.appConfig.initialized:
if Services.appConfig().initialized:
logger.debug(f"Attempted to post {request.path}, but first time setup already run.")
return HttpResponseForbidden()
return super().post(request, *args, **kwargs)
@ -65,14 +64,14 @@ class Step1ApiKeyView(WizardStepMixin, FormView):
def get_initial(self):
initial = super().get_initial()
initial['api_key'] = Services.appConfig.youtube_api_key
initial['api_key'] = Services.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:
Services.appConfig.youtube_api_key = key
Services.appConfig().youtube_api_key = key
return super().form_valid(form)
@ -149,8 +148,8 @@ class Step3ConfigureView(WizardStepMixin, FormView):
def get_initial(self):
initial = super().get_initial()
initial['allow_registrations'] = Services.appConfig.allow_registrations
initial['sync_schedule'] = Services.appConfig.sync_schedule
initial['allow_registrations'] = Services.appConfig().allow_registrations
initial['sync_schedule'] = Services.appConfig().sync_schedule
initial['auto_download'] = self.request.user.preferences['auto_download']
initial['download_location'] = self.request.user.preferences['download_path']
return initial
@ -158,11 +157,11 @@ class Step3ConfigureView(WizardStepMixin, FormView):
def form_valid(self, form):
allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None:
Services.appConfig.allow_registrations = allow_registrations
Services.appConfig().allow_registrations = allow_registrations
sync_schedule = form.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
Services.appConfig.sync_schedule = sync_schedule
Services.appConfig().sync_schedule = sync_schedule
auto_download = form.cleaned_data['auto_download']
if auto_download is not None:
@ -173,11 +172,11 @@ class Step3ConfigureView(WizardStepMixin, FormView):
self.request.user.preferences['download_path'] = download_location
# Set initialized to true
Services.appConfig.initialized = True
Services.appConfig().initialized = True
# Start scheduler if not started
Services.scheduler.initialize()
SynchronizeJob.schedule_global_job()
Services.scheduler().initialize()
Services.subscriptionManager().schedule_global_synchronize_job()
return super().form_valid(form)

View File

@ -234,25 +234,25 @@ class AdminSettingsForm(forms.Form):
@staticmethod
def get_initials():
return {
'api_key': Services.appConfig.youtube_api_key,
'allow_registrations': Services.appConfig.allow_registrations,
'sync_schedule': Services.appConfig.sync_schedule,
'scheduler_concurrency': Services.appConfig.concurrency,
'api_key': Services.appConfig().youtube_api_key,
'allow_registrations': Services.appConfig().allow_registrations,
'sync_schedule': Services.appConfig().sync_schedule,
'scheduler_concurrency': Services.appConfig().concurrency,
}
def save(self):
api_key = self.cleaned_data['api_key']
if api_key is not None and len(api_key) > 0:
Services.appConfig.youtube_api_key = api_key
Services.appConfig().youtube_api_key = api_key
allow_registrations = self.cleaned_data['allow_registrations']
if allow_registrations is not None:
Services.appConfig.allow_registrations = allow_registrations
Services.appConfig().allow_registrations = allow_registrations
sync_schedule = self.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
Services.appConfig.sync_schedule = sync_schedule
Services.appConfig().sync_schedule = sync_schedule
concurrency = self.cleaned_data['scheduler_concurrency']
if concurrency is not None:
Services.appConfig.concurrency = concurrency
Services.appConfig().concurrency = concurrency

View File

@ -1,22 +1,21 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, HTML
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db.models import Q
from django.http import HttpRequest, HttpResponseBadRequest, JsonResponse
from django.shortcuts import render, redirect
from django.views.generic import CreateView, UpdateView, DeleteView, FormView
from django.views.generic.edit import FormMixin
from django.conf import settings
from django.core.paginator import Paginator
from YtManagerApp.management.videos import get_videos
from YtManagerApp.models import Subscription, SubscriptionFolder, VIDEO_ORDER_CHOICES, VIDEO_ORDER_MAPPING
from YtManagerApp.services import Services
from YtManagerApp.utils import youtube, subscription_file_parser
from YtManagerApp.views.controls.modal import ModalMixin
import logging
from YtManagerApp.models import Subscription, SubscriptionFolder, VIDEO_ORDER_CHOICES, VIDEO_ORDER_MAPPING
from YtManagerApp.providers.video_provider import InvalidURLError
from YtManagerApp.services import Services
from YtManagerApp.utils import subscription_file_parser
from YtManagerApp.views.controls.modal import ModalMixin
class VideoFilterForm(forms.Form):
@ -110,8 +109,7 @@ def __tree_sub_id(sub_id):
def index(request: HttpRequest):
if not Services.appConfig.initialized:
if not Services.appConfig().initialized:
return redirect('first_time_0')
context = {
@ -129,7 +127,6 @@ def index(request: HttpRequest):
@login_required
def ajax_get_tree(request: HttpRequest):
def visit(node):
if isinstance(node, SubscriptionFolder):
return {
@ -157,7 +154,7 @@ def ajax_get_videos(request: HttpRequest):
if request.method == 'POST':
form = VideoFilterForm(request.POST)
if form.is_valid():
videos = get_videos(
videos = Services.videoManager().get_videos(
user=request.user,
sort_order=form.cleaned_data['sort'],
query=form.cleaned_data['query'],
@ -272,7 +269,6 @@ class CreateSubscriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.yt_api = youtube.YoutubeAPI.build_public()
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
@ -289,16 +285,9 @@ class CreateSubscriptionForm(forms.ModelForm):
def clean_playlist_url(self):
playlist_url: str = self.cleaned_data['playlist_url']
try:
parsed_url = self.yt_api.parse_url(playlist_url)
except youtube.InvalidURL as e:
Services.videoProviderManager().validate_subscription_url(playlist_url)
except InvalidURLError as e:
raise forms.ValidationError(str(e))
is_playlist = 'playlist' in parsed_url
is_channel = parsed_url['type'] in ('channel', 'user', 'channel_custom')
if not is_channel and not is_playlist:
raise forms.ValidationError('The given URL must link to a channel or a playlist!')
return playlist_url
@ -308,10 +297,12 @@ class CreateSubscriptionModal(LoginRequiredMixin, ModalMixin, CreateView):
def form_valid(self, form):
form.instance.user = self.request.user
api = youtube.YoutubeAPI.build_public()
try:
form.instance.fetch_from_url(form.cleaned_data['playlist_url'], api)
except youtube.InvalidURL as e:
subscription: Subscription = Services.videoProviderManager().fetch_subscription(
form.cleaned_data['playlist_url'])
form.instance.copy_from(subscription)
form.instance.user = self.request.user
except InvalidURLError as e:
return self.modal_response(form, False, str(e))
except ValueError as e:
return self.modal_response(form, False, str(e))
@ -327,8 +318,9 @@ class CreateSubscriptionModal(LoginRequiredMixin, ModalMixin, CreateView):
# except youtube.APIError as e:
# return self.modal_response(
# form, False, 'An error occurred while communicating with the YouTube API: ' + str(e))
return super().form_valid(form)
response = super().form_valid(form)
Services.subscriptionManager().synchronize(form.instance)
return response
class UpdateSubscriptionForm(forms.ModelForm):
@ -407,7 +399,6 @@ class ImportSubscriptionsForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.yt_api = youtube.YoutubeAPI.build_public()
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
@ -456,28 +447,18 @@ class ImportSubscriptionsModal(LoginRequiredMixin, ModalMixin, FormView):
try:
url_list = list(subscription_file_parser.parse(file))
except subscription_file_parser.FormatNotSupportedError:
return super().modal_response(form, success=False,
return super().modal_response(form,
success=False,
error_msg="The file could not be parsed! "
"Possible problems: format not supported, file is malformed.")
print(form.cleaned_data)
# Create subscriptions
api = youtube.YoutubeAPI.build_public()
for url in url_list:
sub = Subscription()
sub.user = self.request.user
sub.parent_folder = form.cleaned_data['parent_folder']
sub.auto_download = form.cleaned_data['auto_download']
sub.download_limit = form.cleaned_data['download_limit']
sub.download_order = form.cleaned_data['download_order']
sub.automatically_delete_watched = form.cleaned_data["automatically_delete_watched"]
try:
sub.fetch_from_url(url, api)
except Exception as e:
logging.error("Import subscription error - error processing URL %s: %s", url, e)
continue
sub.save()
Services.subscriptionManager().import_multiple(url_list,
form.cleaned_data['parent_folder'],
form.cleaned_data['auto_download'],
form.cleaned_data['download_limit'],
form.cleaned_data['download_order'],
form.cleaned_data["automatically_delete_watched"])
return super().form_valid(form)

View File

@ -3,7 +3,7 @@ from django.http import HttpResponseForbidden
from django.urls import reverse_lazy
from django.views.generic import FormView
from YtManagerApp.scheduler.jobs.synchronize_job import SynchronizeJob
from YtManagerApp.services import Services
from YtManagerApp.views.forms.settings import SettingsForm, AdminSettingsForm
@ -45,5 +45,5 @@ class AdminSettingsView(LoginRequiredMixin, FormView):
def form_valid(self, form):
form.save()
SynchronizeJob.schedule_global_job()
Services.subscriptionManager().schedule_global_synchronize_job()
return super().form_valid(form)

View File

@ -1,8 +1,6 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, StreamingHttpResponse, FileResponse
from django.urls import reverse, reverse_lazy
from django.views import View
from django.http import HttpRequest, FileResponse
from django.views.generic import DetailView
from YtManagerApp.models import Video