Implemented first time setup wizard. There are still some problems to be solved.
@@ -7,7 +7,7 @@ from django.conf import settings as dj_settings
 | 
			
		||||
from .management.appconfig import global_prefs
 | 
			
		||||
from .management.jobs.synchronize import schedule_synchronize_global
 | 
			
		||||
from .scheduler import initialize_scheduler
 | 
			
		||||
 | 
			
		||||
from django.db.utils import OperationalError
 | 
			
		||||
 | 
			
		||||
def __initialize_logger():
 | 
			
		||||
    log_dir = os.path.join(dj_settings.DATA_DIR, 'logs')
 | 
			
		||||
@@ -29,8 +29,12 @@ def __initialize_logger():
 | 
			
		||||
def main():
 | 
			
		||||
    __initialize_logger()
 | 
			
		||||
 | 
			
		||||
    if global_prefs['hidden__initialized']:
 | 
			
		||||
        initialize_scheduler()
 | 
			
		||||
        schedule_synchronize_global()
 | 
			
		||||
    try:
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
            initialize_scheduler()
 | 
			
		||||
            schedule_synchronize_global()
 | 
			
		||||
    except OperationalError:
 | 
			
		||||
        # Settings table is not created when running migrate or makemigrations, so just don't do anything in this case.
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    logging.info('Initialization complete.')
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@ from dynamic_preferences.registries import global_preferences_registry
 | 
			
		||||
from dynamic_preferences.users.registries import user_preferences_registry
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.models import VIDEO_ORDER_CHOICES
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
# we create some section objects to link related preferences together
 | 
			
		||||
 | 
			
		||||
@@ -83,7 +85,7 @@ class AutoDeleteWatched(BooleanPreference):
 | 
			
		||||
@user_preferences_registry.register
 | 
			
		||||
class AutoDownloadEnabled(BooleanPreference):
 | 
			
		||||
    section = downloader
 | 
			
		||||
    name = 'download_enabled'
 | 
			
		||||
    name = 'auto_enabled'
 | 
			
		||||
    default = True
 | 
			
		||||
    required = True
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +93,7 @@ class AutoDownloadEnabled(BooleanPreference):
 | 
			
		||||
@user_preferences_registry.register
 | 
			
		||||
class DownloadGlobalLimit(IntegerPreference):
 | 
			
		||||
    section = downloader
 | 
			
		||||
    name = 'download_global_limit'
 | 
			
		||||
    name = 'global_limit'
 | 
			
		||||
    default = None
 | 
			
		||||
    required = False
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +109,7 @@ class DownloadGlobalSizeLimit(IntegerPreference):
 | 
			
		||||
@user_preferences_registry.register
 | 
			
		||||
class DownloadSubscriptionLimit(IntegerPreference):
 | 
			
		||||
    section = downloader
 | 
			
		||||
    name = 'download_limit_per_subscription'
 | 
			
		||||
    name = 'limit_per_subscription'
 | 
			
		||||
    default = 5
 | 
			
		||||
    required = False
 | 
			
		||||
 | 
			
		||||
@@ -115,7 +117,7 @@ class DownloadSubscriptionLimit(IntegerPreference):
 | 
			
		||||
@user_preferences_registry.register
 | 
			
		||||
class DownloadMaxAttempts(IntegerPreference):
 | 
			
		||||
    section = downloader
 | 
			
		||||
    name = 'download_max_attempts'
 | 
			
		||||
    name = 'max_download_attempts'
 | 
			
		||||
    default = 3
 | 
			
		||||
    required = True
 | 
			
		||||
 | 
			
		||||
@@ -133,7 +135,7 @@ class DownloadOrder(ChoicePreference):
 | 
			
		||||
class DownloadPath(StringPreference):
 | 
			
		||||
    section = downloader
 | 
			
		||||
    name = 'download_path'
 | 
			
		||||
    default = None
 | 
			
		||||
    default = os.path.join(settings.DATA_DIR, 'downloads')
 | 
			
		||||
    required = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ def synchronize_subscription(subscription: Subscription):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def schedule_synchronize_global():
 | 
			
		||||
    trigger = CronTrigger.from_crontab(global_prefs['synchronization_schedule'])
 | 
			
		||||
    trigger = CronTrigger.from_crontab(global_prefs['scheduler__synchronization_schedule'])
 | 
			
		||||
    job = scheduler.scheduler.add_job(synchronize, trigger, max_instances=1, coalesce=True)
 | 
			
		||||
    log.info('Scheduled synchronize job job=%s', job.id)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
import logging
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from apscheduler.schedulers.background import BackgroundScheduler
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
 | 
			
		||||
scheduler: BackgroundScheduler = None
 | 
			
		||||
 | 
			
		||||
@@ -13,7 +14,7 @@ def initialize_scheduler():
 | 
			
		||||
    executors = {
 | 
			
		||||
        'default': {
 | 
			
		||||
            'type': 'threadpool',
 | 
			
		||||
            'max_workers': settings.SCHEDULER_CONCURRENCY
 | 
			
		||||
            'max_workers': global_prefs['scheduler__concurrency']
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    job_defaults = {
 | 
			
		||||
 
 | 
			
		||||
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 37 KiB  | 
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 20 KiB  | 
| 
		 After Width: | Height: | Size: 19 KiB  | 
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 7.6 KiB  | 
| 
		 After Width: | Height: | Size: 22 KiB  | 
| 
		 After Width: | Height: | Size: 4.1 KiB  | 
| 
		 After Width: | Height: | Size: 12 KiB  | 
@@ -5,7 +5,7 @@
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <h1>Done!</h1>
 | 
			
		||||
        <p>The application is now ready to use!</p>
 | 
			
		||||
        <p>The setup is finished, and the application is now ready to use!</p>
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,66 @@
 | 
			
		||||
{% extends "YtManagerApp/master_default.html" %}
 | 
			
		||||
{% load crispy_forms_tags %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
 | 
			
		||||
        <h1>Step 1: Set up YouTube API key (optional)</h1>
 | 
			
		||||
        <p>This program uses the YouTube API in order to obtain information about videos, channels and playlists.
 | 
			
		||||
        In order to access this API, YouTube requires that we register on their site and request an API key. YouTube
 | 
			
		||||
        uses this key in order to limit the number of requests made to their platform, in order to prevent abuses.</p>
 | 
			
		||||
 | 
			
		||||
        <p>To use this program, it is <em>recommended</em> that you create an API key for your own account. While a key
 | 
			
		||||
        is already provided the developer, there is a chance that the quota limits will be reached, which will prevent
 | 
			
		||||
        this program from reaching YouTube. Follow the steps below, or press <strong>Skip</strong> to use the provided key.</p>
 | 
			
		||||
 | 
			
		||||
        <h4>
 | 
			
		||||
            <a data-toggle="collapse" href="#HowToObtainKey" role="button" aria-expanded="false" aria-controls="HowToObtainKey">
 | 
			
		||||
                <span class="typcn typcn-arrow-sorted-down"></span>How to obtain a key
 | 
			
		||||
            </a>
 | 
			
		||||
        </h4>
 | 
			
		||||
 | 
			
		||||
        <div class="collapse" id="HowToObtainKey">
 | 
			
		||||
            <ol>
 | 
			
		||||
                <li>Visit <a href="https://developers.google.com/">https://developers.google.com/</a>, log in or create an account, if necessary.</li>
 | 
			
		||||
                <li>Go to <a href="https://console.developers.google.com/project"></a>, and click on the <strong>Create project</strong> button.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_create_project.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Give a name to the project, and then click on <strong>Create</strong>. Wait for a few seconds, until Google finishes creating the project.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_project_name.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Make sure the newly created project is selected in the top bar and then, in the left sidebar,
 | 
			
		||||
                    go to <strong>APIs & Services</strong> - <strong>Library</strong>.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_goto_apis.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Find and click on the <strong>YouTube Data API v3</strong> from the list.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_select_ytapi.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Click <strong>Enable</strong> to enable the YouTube API for your account.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_enable_ytapi.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>In the navigation sidebar, go to the <strong>Credentials</strong> page.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_goto_credentials.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Click on <strong>Create credentials</strong>.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_create_credential.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Fill the requested information; we will need access to the <strong>YouTube Data v3</strong>,
 | 
			
		||||
                    we will be calling the API from a <strong>Web server</strong>, and we will access the <strong>Public data</strong>.
 | 
			
		||||
                    After filling the information, click on the <strong>What credentials do I need?</strong> button.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_create_credential_options.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>Copy the created key in the box below, and then hit <strong>Done</strong>.
 | 
			
		||||
                    <img class="d-block" src="{% static 'YtManagerApp/img/first_time/ytapi_done.png' %}">
 | 
			
		||||
                </li>
 | 
			
		||||
            </ol>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
 | 
			
		||||
        <a href="{% url 'first_time_2' %}">Skip</a>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock body %}
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
{% extends "YtManagerApp/master_default.html" %}
 | 
			
		||||
{% load crispy_forms_tags %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
 | 
			
		||||
        <h1>Step 2: Create an administrator account</h1>
 | 
			
		||||
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock body %}
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
{% extends "YtManagerApp/master_default.html" %}
 | 
			
		||||
{% load crispy_forms_tags %}
 | 
			
		||||
{% load static %}
 | 
			
		||||
 | 
			
		||||
{% block body %}
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
 | 
			
		||||
        <h1>Step 3: Configure the server</h1>
 | 
			
		||||
 | 
			
		||||
        <p>Here you can customize some basic options for the application. There are many more options which can be changed in the settings page.</p>
 | 
			
		||||
 | 
			
		||||
        {% crispy form %}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
{% endblock body %}
 | 
			
		||||
@@ -24,6 +24,7 @@ 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 import first_time
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    # Authentication URLs
 | 
			
		||||
@@ -59,4 +60,11 @@ urlpatterns = [
 | 
			
		||||
    path('', index, name='home'),
 | 
			
		||||
    path('settings/', SettingsView.as_view(), name='settings'),
 | 
			
		||||
 | 
			
		||||
    # 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.Step2CreateAdminUserView.as_view(), name='first_time_2'),
 | 
			
		||||
    path('first_time/step3_config', first_time.Step3ConfigureView.as_view(), name='first_time_3'),
 | 
			
		||||
    path('first_time/done', first_time.DoneView.as_view(), name='first_time_done'),
 | 
			
		||||
 | 
			
		||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,49 +3,165 @@ 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 django.views.generic import UpdateView, FormView
 | 
			
		||||
from django.shortcuts import render, redirect
 | 
			
		||||
from YtManagerApp.views.auth import RegisterView
 | 
			
		||||
from YtManagerApp.models import UserSettings
 | 
			
		||||
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
from django.http import HttpResponseForbidden
 | 
			
		||||
 | 
			
		||||
class SettingsForm(forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = UserSettings
 | 
			
		||||
        exclude = ['user']
 | 
			
		||||
 | 
			
		||||
class ProtectInitializedMixin(object):
 | 
			
		||||
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
            return redirect('home')
 | 
			
		||||
        return super().get(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        if global_prefs['hidden__initialized']:
 | 
			
		||||
            return HttpResponseForbidden()
 | 
			
		||||
        return super().post(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Step 0: welcome screen
 | 
			
		||||
#
 | 
			
		||||
class Step0WelcomeForm(forms.Form):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.helper = FormHelper()
 | 
			
		||||
        self.helper.layout = Layout(
 | 
			
		||||
            Submit('submit', value='Continue')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Step0WelcomeView(ProtectInitializedMixin, FormView):
 | 
			
		||||
    template_name = 'YtManagerApp/first_time_setup/step0_welcome.html'
 | 
			
		||||
    form_class = Step0WelcomeForm
 | 
			
		||||
    success_url = reverse_lazy('first_time_1')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Step 1: setup API key
 | 
			
		||||
#
 | 
			
		||||
class Step1ApiKeyForm(forms.Form):
 | 
			
		||||
    api_key = forms.CharField(label="YouTube API Key:")
 | 
			
		||||
 | 
			
		||||
    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')
 | 
			
		||||
            'api_key',
 | 
			
		||||
            Submit('submit', value='Continue'),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SettingsView(LoginRequiredMixin, UpdateView):
 | 
			
		||||
    form_class = SettingsForm
 | 
			
		||||
    model = UserSettings
 | 
			
		||||
    template_name = 'YtManagerApp/settings.html'
 | 
			
		||||
    success_url = reverse_lazy('home')
 | 
			
		||||
class Step1ApiKeyView(ProtectInitializedMixin, FormView):
 | 
			
		||||
    template_name = 'YtManagerApp/first_time_setup/step1_apikey.html'
 | 
			
		||||
    form_class = Step1ApiKeyForm
 | 
			
		||||
    success_url = reverse_lazy('first_time_2')
 | 
			
		||||
 | 
			
		||||
    def get_object(self, queryset=None):
 | 
			
		||||
        obj, _ = self.model.objects.get_or_create(user=self.request.user)
 | 
			
		||||
        return obj
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Step 2: create admin user
 | 
			
		||||
#
 | 
			
		||||
class Step2CreateAdminUserView(ProtectInitializedMixin, RegisterView):
 | 
			
		||||
    template_name = 'YtManagerApp/first_time_setup/step2_admin.html'
 | 
			
		||||
    success_url = reverse_lazy('first_time_3')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Step 3: configure server
 | 
			
		||||
#
 | 
			
		||||
class Step3ConfigureForm(forms.Form):
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    auto_download = forms.BooleanField(
 | 
			
		||||
        label="Download videos automatically",
 | 
			
		||||
        required=False
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    download_location = forms.CharField(
 | 
			
		||||
        label="Download location",
 | 
			
		||||
        help_text="Location on the server where videos are downloaded.",
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.helper = FormHelper()
 | 
			
		||||
        self.helper.layout = Layout(
 | 
			
		||||
            HTML('<h3>Server settings</h3>'),
 | 
			
		||||
            'sync_schedule',
 | 
			
		||||
            'allow_registrations',
 | 
			
		||||
            HTML('<h3>User settings</h3>'),
 | 
			
		||||
            'auto_download',
 | 
			
		||||
            'download_location',
 | 
			
		||||
            Submit('submit', value='Continue'),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Step3ConfigureView(ProtectInitializedMixin, FormView):
 | 
			
		||||
    template_name = 'YtManagerApp/first_time_setup/step3_configure.html'
 | 
			
		||||
    form_class = Step3ConfigureForm
 | 
			
		||||
    success_url = reverse_lazy('first_time_done')
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        auto_download = form.cleaned_data['auto_download']
 | 
			
		||||
        if auto_download is not None:
 | 
			
		||||
            self.request.user.preferences['downloader__auto_enabled'] = 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
 | 
			
		||||
 | 
			
		||||
        # Set initialized to true
 | 
			
		||||
        global_prefs['hidden__initialized'] = True
 | 
			
		||||
        
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Done screen
 | 
			
		||||
#
 | 
			
		||||
class DoneForm(forms.Form):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.helper = FormHelper()
 | 
			
		||||
        self.helper.layout = Layout(
 | 
			
		||||
            Submit('submit', value='Finish')
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DoneView(FormView):
 | 
			
		||||
    template_name = 'YtManagerApp/first_time_setup/done.html'
 | 
			
		||||
    form_class = DoneForm
 | 
			
		||||
    success_url = reverse_lazy('home')
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,12 @@ from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.http import HttpRequest, HttpResponseBadRequest, JsonResponse
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
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 YtManagerApp.management.videos import get_videos
 | 
			
		||||
from YtManagerApp.management.appconfig import global_prefs
 | 
			
		||||
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
 | 
			
		||||
@@ -94,6 +95,10 @@ def __tree_sub_id(sub_id):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def index(request: HttpRequest):
 | 
			
		||||
 | 
			
		||||
    if not global_prefs['hidden__initialized']:
 | 
			
		||||
        return redirect('first_time_0')
 | 
			
		||||
 | 
			
		||||
    context = {
 | 
			
		||||
        'config_errors': settings.CONFIG_ERRORS,
 | 
			
		||||
        'config_warnings': settings.CONFIG_WARNINGS,
 | 
			
		||||
 
 | 
			
		||||