Split the settings page (#528)

* Split the settings page

* Add a dashboard page for future use

Create a settings base to keep all the settings pages consistent

* Correct Python formatting

* Fix snapshots not showing in new view
This commit is contained in:
Clark 2023-08-31 04:11:31 +00:00 committed by GitHub
parent 4ded8988c3
commit fcc1c2a648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 602 additions and 464 deletions

View File

@ -0,0 +1,17 @@
{# Base file for all of the settings pages to ensure a common menu #}
{% extends "home/base.html" %}
{% load static %}
{% block content %}
<div class="boxed-content">
<div class="info-box-item child-page-nav">
<a href="{% url 'settings' %}"><h3>Dashboard</h3></a>
<a href="{% url 'settings_user' %}"><h3>User</h3></a>
<a href="{% url 'settings_application' %}"><h3>Application</h3></a>
<a href="{% url 'settings_scheduling' %}"><h3>Scheduling</h3></a>
<a href="{% url 'settings_actions' %}"><h3>Actions</h3></a>
</div>
<div id="notifications" data="setting reindex"></div>
{% block settings_content %}{% endblock %}
</div>
<script type="text/javascript" src="{% static 'progress.js' %}"></script>
{% endblock content %}

View File

@ -6,7 +6,7 @@
<div class="channel-banner">
<a href="/channel/{{ channel_info.channel_id }}/"><img src="/cache/channels/{{ channel_info.channel_id }}_banner.jpg" alt="channel_banner"></a>
</div>
<div class="info-box-item channel-nav">
<div class="info-box-item child-page-nav">
<a href="{% url 'channel_id' channel_info.channel_id %}"><h3>Videos</h3></a>
{% if has_streams %}
<a href="{% url 'channel_id_live' channel_info.channel_id %}"><h3>Streams</h3></a>

View File

@ -6,7 +6,7 @@
<div class="channel-banner">
<a href="{% url 'channel_id' channel_info.channel_id %}"><img src="{{ channel_info.channel_banner_url }}" alt="channel_banner"></a>
</div>
<div class="info-box-item channel-nav">
<div class="info-box-item child-page-nav">
<a href="{% url 'channel_id' channel_info.channel_id %}"><h3>Videos</h3></a>
{% if has_streams %}
<a href="{% url 'channel_id_live' channel_info.channel_id %}"><h3>Streams</h3></a>

View File

@ -6,7 +6,7 @@
<div class="channel-banner">
<a href="{% url 'channel_id' channel_info.channel_id %}"><img src="{{ channel_info.channel_banner_url }}" alt="channel_banner"></a>
</div>
<div class="info-box-item channel-nav">
<div class="info-box-item child-page-nav">
<a href="{% url 'channel_id' channel_info.channel_id %}"><h3>Videos</h3></a>
{% if has_streams %}
<a href="{% url 'channel_id_live' channel_info.channel_id %}"><h3>Streams</h3></a>

View File

@ -1,441 +1,11 @@
{% extends "home/base.html" %}
{% extends "home/base_settings.html" %}
{% load static %}
{% block content %}
<div class="boxed-content">
<div id="notifications" data="setting reindex"></div>
<div class="title-bar">
<h1>User Configurations</h1>
</div>
<form action="/settings/" method="POST" name="user-update">
{% csrf_token %}
<div class="settings-group">
<h2>Color scheme</h2>
<div class="settings-item">
<p>Current color scheme: <span class="settings-current">{{ config.application.colors }}</span></p>
<i>Select your preferred color scheme between dark and light mode.</i><br>
{{ user_form.colors }}
</div>
</div>
<div class="settings-group">
<h2>Archive View</h2>
<div class="settings-item">
<p>Current page size: <span class="settings-current">{{ config.archive.page_size }}</span></p>
<i>Result of videos showing in archive page</i><br>
{{ user_form.page_size }}
</div>
</div>
<button type="submit" name="user-settings">Update User Configurations</button>
</form>
<div class="title-bar">
<h1>Application Configurations</h1>
</div>
<form action="/settings/" method="POST" name="application-update">
{% csrf_token %}
<div class="settings-group">
<h2 id="subscriptions">Subscriptions</h2>
<p>Disable shorts or streams by setting their page size to 0 (zero).</p>
<div class="settings-item">
<p>YouTube page size: <span class="settings-current">{{ config.subscriptions.channel_size }}</span></p>
<i>Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_channel_size }}
</div>
<div class="settings-item">
<p>YouTube Live page size: <span class="settings-current">{{ config.subscriptions.live_channel_size }}</span></p>
<i>Live Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_live_channel_size }}
</div>
<div class="settings-item">
<p>YouTube Shorts page size: <span class="settings-current">{{ config.subscriptions.shorts_channel_size }}</span></p>
<i>Shorts Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_shorts_channel_size }}
</div>
<div class="settings-item">
<p>Auto start download from your subscriptions: <span class="settings-current">{{ config.subscriptions.auto_start}}</span></p>
<i>Enable this will automatically start and prioritize videos from your subscriptions.</i><br>
{{ app_form.subscriptions_auto_start }}
</div>
</div>
<div class="settings-group">
<h2 id="downloads">Downloads</h2>
<div class="settings-item">
<p>Current download speed limit in KB/s: <span class="settings-current">{{ config.downloads.limit_speed }}</span></p>
<i>Limit download speed. 0 (zero) to deactivate, e.g. 1000 (1MB/s). Speeds are in KB/s. Setting takes effect on new download jobs or application restart.</i><br>
{{ app_form.downloads_limit_speed }}
</div>
<div class="settings-item">
<p>Current throttled rate limit in KB/s: <span class="settings-current">{{ config.downloads.throttledratelimit }}</span></p>
<i>Download will restart if speeds drop below specified amount. 0 (zero) to deactivate, e.g. 100. Speeds are in KB/s.</i><br>
{{ app_form.downloads_throttledratelimit }}
</div>
<div class="settings-item">
<p>Current scraping sleep interval: <span class="settings-current">{{ config.downloads.sleep_interval }}</p>
<i>Seconds to sleep between calls to YouTube. Might be necessary to avoid throttling. Recommended 3.</i><br>
{{ app_form.downloads_sleep_interval }}
</div>
<div class="settings-item">
<p><span class="danger-zone">Danger Zone</span>: Current auto delete watched videos: <span class="settings-current">{{ config.downloads.autodelete_days }}</span></p>
<i>Auto delete watched videos after x days, 0 (zero) to deactivate:</i><br>
{{ app_form.downloads_autodelete_days }}
</div>
</div>
<div class="settings-group">
<h2 id="format">Download Format</h2>
<div class="settings-item">
<p>Limit video and audio quality format for yt-dlp.<br>
Currently: <span class="settings-current">{{ config.downloads.format }}</span>
</p>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">bestvideo[height<=720]+bestaudio/best[height<=720]</span>: best audio and max video height of 720p.</li>
<li><span class="settings-current">bestvideo[height<=1080]+bestaudio/best[height<=1080]</span>: best audio and max video height of 1080p.</li>
<li><span class="settings-current">bestvideo[height<=1080][vcodec*=avc1]+bestaudio[acodec*=mp4a]/mp4</span>: Max 1080p video height with iOS compatible video and audio codecs.</li>
<li><span class="settings-current">0</span>: deactivate and download the best quality possible as decided by yt-dlp.</li>
</ul>
<i>Make sure your custom format gets merged into a single file. Check out the <a href="https://github.com/yt-dlp/yt-dlp#format-selection" target="_blank">documentation</a> for valid configurations.</i><br>
{{ app_form.downloads_format }}
<br>
</div>
<div class="settings-item">
<p>Force sort order to have precedence over all yt-dlp fields.<br>
Currently: <span class="settings-current">{{ config.downloads.format_sort }}</span>
</p>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">res,codec:av1</span>: prefer AV1 over all other video codecs.</li>
<li><span class="settings-current">0</span>: deactivate and keep the default as decided by yt-dlp.</li>
</ul>
<i>Not all codecs are supported by all browsers. The default value ensures best compatibility. Check out the <a href="https://github.com/yt-dlp/yt-dlp#sorting-formats" target="_blank">documentation</a> for valid configurations.</i><br>
{{ app_form.downloads_format_sort }}
<br>
</div>
<div class="settings-item">
<p>Prefer translated metadata language: <span class="settings-current">{{ config.downloads.extractor_lang }}</span></p>
<i>This will change the language this video gets indexed as. That will only be available if the uploader provides translations. Add as two letter ISO language code, check the <a href="https://github.com/yt-dlp/yt-dlp#youtube" target="_blank">documentation</a> which languages are available.</i><br>
{{ app_form.downloads_extractor_lang}}
</div>
<div class="settings-item">
<p>Current metadata embed setting: <span class="settings-current">{{ config.downloads.add_metadata }}</span></p>
<i>Metadata is not embedded into the downloaded files by default.</i><br>
{{ app_form.downloads_add_metadata }}
</div>
<div class="settings-item">
<p>Current thumbnail embed setting: <span class="settings-current">{{ config.downloads.add_thumbnail }}</span></p>
<i>Embed thumbnail into the mediafile.</i><br>
{{ app_form.downloads_add_thumbnail }}
</div>
</div>
<div class="settings-group">
<h2 id="format">Subtitles</h2>
<div class="settings-item">
<p>Subtitles download setting: <span class="settings-current">{{ config.downloads.subtitle }}</span><br>
<i>Choose which subtitles to download, add comma separated language codes,<br>
e.g. <span class="settings-current">en, de, zh-Hans</span></i><br>
{{ app_form.downloads_subtitle }}</p>
</div>
<div class="settings-item">
<p>Subtitle source settings: <span class="settings-current">{{ config.downloads.subtitle_source }}</span></p>
<i>Download only user generated, or also less accurate auto generated subtitles.</i><br>
{{ app_form.downloads_subtitle_source }}
</div>
<div class="settings-item">
<p>Index and make subtitles searchable: <span class="settings-current">{{ config.downloads.subtitle_index }}</span></p>
<i>Store subtitle lines in Elasticsearch. Not recommended for low-end hardware.</i><br>
{{ app_form.downloads_subtitle_index }}
</div>
</div>
<div class="settings-group">
<h2 id="comments">Comments</h2>
<div class="settings-item">
<p>Download and index comments: <span class="settings-current">{{ config.downloads.comment_max }}</span><br>
<i>Follow the yt-dlp max_comments documentation, <a href="https://github.com/yt-dlp/yt-dlp#youtube" target="_blank">max-comments,max-parents,max-replies,max-replies-per-thread</a>:</i><br>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">all,100,all,30</span>: Get 100 max-parents and 30 max-replies-per-thread.</li>
<li><span class="settings-current">1000,all,all,50</span>: Get a total of 1000 comments over all, 50 replies per thread.</li>
</ul>
{{ app_form.downloads_comment_max }}</p>
</div>
<div class="settings-item">
<p>Selected comment sort method: <span class="settings-current">{{ config.downloads.comment_sort }}</span><br>
<i>Select how many comments and threads to download:</i><br>
{{ app_form.downloads_comment_sort }}</p>
</div>
</div>
<div class="settings-group">
<h2 id="format">Cookie</h2>
<div class="settings-item">
<p>Import YouTube cookie: <span class="settings-current">{{ config.downloads.cookie_import }}</span><br></p>
<p>For automatic cookie import use <b>Tube Archivist Companion</b> <a href="https://github.com/tubearchivist/browser-extension" target="_blank">browser extension</a>.</p>
<i>For manual cookie import, place your cookie file named <span class="settings-current">cookies.google.txt</span> in <span class="settings-current">cache/import</span> before enabling. Instructions in the <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki.</a></i><br>
{{ app_form.downloads_cookie_import }}<br>
{% if config.downloads.cookie_import %}
<div id="cookieMessage">
<button onclick="handleCookieValidate()" type="button" id="cookieButton">Validate Cookie File</button>
</div>
{% endif %}
</div>
</div>
<div class="settings-group">
<h2 id="integrations">Integrations</h2>
<div class="settings-item">
<p>API token: <button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button></p>
<div id="text-reveal" class="description-text">
<p>{{ api_token }}</p>
<button class="danger-button" type="button" onclick="resetToken()">Revoke</button>
</div>
</div>
<div class="settings-item">
<p>Integrate with <a href="https://returnyoutubedislike.com/" target="_blank">returnyoutubedislike.com</a> to get dislikes and average ratings back: <span class="settings-current">{{ config.downloads.integrate_ryd }}</span></p>
<i>Before activating that, make sure you have a scraping sleep interval of at least 3 secs set to avoid ratelimiting issues.</i><br>
{{ app_form.downloads_integrate_ryd }}
</div>
<div class="settings-item">
<p>Integrate with <a href="https://sponsor.ajay.app/" target="_blank">SponsorBlock</a> to get sponsored timestamps: <span class="settings-current">{{ config.downloads.integrate_sponsorblock }}</span></p>
<i>Before activating that, make sure you have a scraping sleep interval of at least 3 secs set to avoid ratelimiting issues.</i><br>
{{ app_form.downloads_integrate_sponsorblock }}
</div>
</div>
<div class="settings-group">
<h2 id="snapshots">Snapshots</h2>
<div class="settings-item">
<p>Current system snapshot: <span class="settings-current">{{ config.application.enable_snapshot }}</span></p>
<i>Automatically create daily deduplicated snapshots of the index, stored in Elasticsearch. Read first before activating: <a target="_blank" href="https://docs.tubearchivist.com/settings/#snapshots">Wiki</a>.</i><br>
{{ app_form.application_enable_snapshot }}
</div>
<div>
{% if snapshots %}
<p>Create next snapshot: <span class="settings-current">{{ snapshots.next_exec_str }}</span>, snapshots expire after <span class="settings-current">{{ snapshots.expire_after }}</span>. <button onclick="createSnapshot()" id="createButton">Create snapshot now</button></p>
<br>
{% for snapshot in snapshots.snapshots %}
<p><button id="{{ snapshot.id }}" onclick="restoreSnapshot(id)">Restore</button> Snapshot created on: <span class="settings-current">{{ snapshot.start_date }}</span>, took <span class="settings-current">{{ snapshot.duration_s }}s</span> to create. State: <i>{{ snapshot.state }}</i></p>
{% endfor %}
{% endif %}
</div>
</div>
<button type="submit" name="application-settings">Update Application Configurations</button>
</form>
<div class="title-bar">
<h1>Scheduler Setup</h1>
<div class="settings-group">
<p>Schedule settings expect a cron like format, where the first value is minute, second is hour and third is day of the week.</p>
<p>Examples:</p>
<ul>
<li><span class="settings-current">0 15 *</span>: Run task every day at 15:00 in the afternoon.</li>
<li><span class="settings-current">30 8 */2</span>: Run task every second day of the week (Sun, Tue, Thu, Sat) at 08:30 in the morning.</li>
<li><span class="settings-current">auto</span>: Sensible default.</li>
<li><span class="settings-current">0</span>: (zero), deactivate that task.</li>
</ul>
<p>Note:</p>
<ul>
<li>Changes in the scheduler settings require a container restart to take effect.</li>
<li>Avoid an unnecessary frequent schedule to not get blocked by YouTube. For that reason, the scheduler doesn't support schedules that trigger more than once per hour.</li>
</ul>
</div>
</div>
<form action="/settings/" method="POST" name="scheduler-update">
{% csrf_token %}
<div class="settings-group">
<h2>Rescan Subscriptions</h2>
<div class="settings-item">
<p>Current rescan schedule: <span class="settings-current">
{% if config.scheduler.update_subscribed %}
{% for key, value in config.scheduler.update_subscribed.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Become a sponsor and join <a href="https://members.tubearchivist.com/" target="_blank">members.tubearchivist.com</a> to get access to <span class="settings-current">real time</span> notifications for new videos uploaded by your favorite channels.</p>
<p>Periodically rescan your subscriptions:</p>
{{ scheduler_form.update_subscribed }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.update_subscribed_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.update_subscribed_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.update_subscribed_notify }}</span></p>
{% endif %}
{{ scheduler_form.update_subscribed_notify }}
</div>
</div>
<div class="settings-group">
<h2>Start download</h2>
<div class="settings-item">
<p>Current Download schedule: <span class="settings-current">
{% if config.scheduler.download_pending %}
{% for key, value in config.scheduler.download_pending.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Automatic video download schedule:</p>
{{ scheduler_form.download_pending }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.download_pending_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.download_pending_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.download_pending_notify }}</span></p>
{% endif %}
{{ scheduler_form.download_pending_notify }}
</div>
</div>
<div class="settings-group">
<h2>Refresh Metadata</h2>
<div class="settings-item">
<p>Current Metadata refresh schedule: <span class="settings-current">
{% if config.scheduler.check_reindex %}
{% for key, value in config.scheduler.check_reindex.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Daily schedule to refresh metadata from YouTube:</p>
{{ scheduler_form.check_reindex }}
</div>
<div class="settings-item">
<p>Current refresh for metadata older than x days: <span class="settings-current">{{ config.scheduler.check_reindex_days }}</span></p>
<p>Refresh older than x days, recommended 90:</p>
{{ scheduler_form.check_reindex_days }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.check_reindex_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.check_reindex_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.check_reindex_notify }}</span></p>
{% endif %}
{{ scheduler_form.check_reindex_notify }}
</div>
</div>
<div class="settings-group">
<h2>Thumbnail check</h2>
<div class="settings-item">
<p>Current thumbnail check schedule: <span class="settings-current">
{% if config.scheduler.thumbnail_check %}
{% for key, value in config.scheduler.thumbnail_check.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Periodically check and cleanup thumbnails:</p>
{{ scheduler_form.thumbnail_check }}
</div>
</div>
<div class="settings-group">
<h2>ZIP file index backup</h2>
<div class="settings-item">
<p><i>Zip file backups are very slow for large archives and consistency is not guaranteed, use snapshots instead. Make sure no other tasks are running when creating a Zip file backup.</i></p>
<p>Current index backup schedule: <span class="settings-current">
{% if config.scheduler.run_backup %}
{% for key, value in config.scheduler.run_backup.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Automatically backup metadata to a zip file:</p>
{{ scheduler_form.run_backup }}
</div>
<div class="settings-item">
<p>Current backup files to keep: <span class="settings-current">{{ config.scheduler.run_backup_rotate }}</span></p>
<p>Max auto backups to keep:</p>
{{ scheduler_form.run_backup_rotate }}
</div>
</div>
<button type="submit" name="scheduler-settings">Update Scheduler Settings</button>
</form>
<div class="title-bar">
<h1>Actions</h1>
</div>
<div class="settings-group">
<h2>Delete download queue</h2>
<p>Delete your pending or previously ignored videos from your download queue.<p>
<button onclick="deleteQueue(this)" id="ignore-button" data-id="ignore" title="Delete all previously ignored videos from the queue">Delete all ignored</button>
<button onclick="deleteQueue(this)" id="pending-button" data-id="pending" title="Delete all pending videos from the queue">Delete all queued</button>
</div>
<div class="settings-group">
<h2>Manual media files import.</h2>
<p>Add files to the <span class="settings-current">cache/import</span> folder. Make sure to follow the instructions in the Github <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki</a>.</p>
<div id="manual-import">
<button onclick="manualImport()">Start import</button>
</div>
</div>
<div class="settings-group">
<h2>Embed thumbnails into media file.</h2>
<p>Set extracted youtube thumbnail as cover art of the media file.</p>
<div id="re-embed">
<button onclick="reEmbed()">Start process</button>
</div>
</div>
<div class="settings-group">
<h2>ZIP file index backup</h2>
<p>Export your database to a zip file stored at <span class="settings-current">cache/backup</span>.</p>
<p><i>Zip file backups are very slow for large archives and consistency is not guaranteed, use snapshots instead. Make sure no other tasks are running when creating a Zip file backup.</i></p>
<div id="db-backup">
<button onclick="dbBackup()">Start backup</button>
</div>
</div>
<div class="settings-group">
<h2>Restore from backup</h2>
<p><span class="danger-zone">Danger Zone</span>: This will replace your existing index with the backup.</p>
<p>Restore from available backup files from <span class="settings-current">cache/backup</span>.</p>
{% if available_backups %}
<div class="backup-grid-row">
<span></span>
<span>Timestamp</span>
<span>Source</span>
<span>Filename</span>
</div>
{% for backup in available_backups %}
<div class="backup-grid-row" id="{{ backup.filename }}">
<button onclick="dbRestore(this)" data-id="{{ backup.filename }}">Restore</button>
<span>{{ backup.timestamp }}</span>
<span>{{ backup.reason }}</span>
<span>{{ backup.filename }}</span>
</div>
{% endfor %}
{% else %}
<p>No backups found.</p>
{% endif %}
</div>
<div class="settings-group">
<h2>Rescan filesystem</h2>
<p><span class="danger-zone">Danger Zone</span>: This will delete the metadata of deleted videos from the filesystem.</p>
<p>Rescan your media folder looking for missing videos and clean up index. More infos on the Github <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki</a>.</p>
<div id="fs-rescan">
<button onclick="fsRescan()">Rescan filesystem</button>
</div>
</div>
{% if request.user.is_superuser %}
<div class="title-bar">
<h1>Users</h1>
</div>
<div class="settings-group">
<h2>User Management</h2>
<p>Access the admin interface for basic user management functionality like adding and deleting users, changing passwords and more.</p>
<a href="/admin/"><button>Admin Interface</button></a>
</div>
{% endif %}
{% block settings_content %}
<div class="title-bar">
<h1>Dashboard Coming Soon</h1>
</div>
<script type="text/javascript" src="{% static 'progress.js' %}"></script>
{% endblock content %}
<div class="settings-group">
<p>Tube Archivist settings have been moved to their own pages!</p>
<p>You can now find them by selecting an item from the sub-menu at the top of the page.</p>
</div>
{% endblock settings_content %}

View File

@ -0,0 +1,66 @@
{% extends "home/base_settings.html" %}
{% load static %}
{% block settings_content %}
<div class="title-bar">
<h1>Actions</h1>
</div>
<div class="settings-group">
<h2>Delete download queue</h2>
<p>Delete your pending or previously ignored videos from your download queue.<p>
<button onclick="deleteQueue(this)" id="ignore-button" data-id="ignore" title="Delete all previously ignored videos from the queue">Delete all ignored</button>
<button onclick="deleteQueue(this)" id="pending-button" data-id="pending" title="Delete all pending videos from the queue">Delete all queued</button>
</div>
<div class="settings-group">
<h2>Manual media files import.</h2>
<p>Add files to the <span class="settings-current">cache/import</span> folder. Make sure to follow the instructions in the Github <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki</a>.</p>
<div id="manual-import">
<button onclick="manualImport()">Start import</button>
</div>
</div>
<div class="settings-group">
<h2>Embed thumbnails into media file.</h2>
<p>Set extracted youtube thumbnail as cover art of the media file.</p>
<div id="re-embed">
<button onclick="reEmbed()">Start process</button>
</div>
</div>
<div class="settings-group">
<h2>ZIP file index backup</h2>
<p>Export your database to a zip file stored at <span class="settings-current">cache/backup</span>.</p>
<p><i>Zip file backups are very slow for large archives and consistency is not guaranteed, use snapshots instead. Make sure no other tasks are running when creating a Zip file backup.</i></p>
<div id="db-backup">
<button onclick="dbBackup()">Start backup</button>
</div>
</div>
<div class="settings-group">
<h2>Restore from backup</h2>
<p><span class="danger-zone">Danger Zone</span>: This will replace your existing index with the backup.</p>
<p>Restore from available backup files from <span class="settings-current">cache/backup</span>.</p>
{% if available_backups %}
<div class="backup-grid-row">
<span></span>
<span>Timestamp</span>
<span>Source</span>
<span>Filename</span>
</div>
{% for backup in available_backups %}
<div class="backup-grid-row" id="{{ backup.filename }}">
<button onclick="dbRestore(this)" data-id="{{ backup.filename }}">Restore</button>
<span>{{ backup.timestamp }}</span>
<span>{{ backup.reason }}</span>
<span>{{ backup.filename }}</span>
</div>
{% endfor %}
{% else %}
<p>No backups found.</p>
{% endif %}
</div>
<div class="settings-group">
<h2>Rescan filesystem</h2>
<p><span class="danger-zone">Danger Zone</span>: This will delete the metadata of deleted videos from the filesystem.</p>
<p>Rescan your media folder looking for missing videos and clean up index. More infos on the Github <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki</a>.</p>
<div id="fs-rescan">
<button onclick="fsRescan()">Rescan filesystem</button>
</div>
</div>
{% endblock settings_content %}

View File

@ -0,0 +1,192 @@
{% extends "home/base_settings.html" %}
{% load static %}
{% block settings_content %}
<div class="title-bar">
<h1>Application Configurations</h1>
</div>
<form action="{% url 'settings_application' %}" method="POST" name="application-update">
{% csrf_token %}
<div class="settings-group">
<h2 id="subscriptions">Subscriptions</h2>
<p>Disable shorts or streams by setting their page size to 0 (zero).</p>
<div class="settings-item">
<p>YouTube page size: <span class="settings-current">{{ config.subscriptions.channel_size }}</span></p>
<i>Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_channel_size }}
</div>
<div class="settings-item">
<p>YouTube Live page size: <span class="settings-current">{{ config.subscriptions.live_channel_size }}</span></p>
<i>Live Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_live_channel_size }}
</div>
<div class="settings-item">
<p>YouTube Shorts page size: <span class="settings-current">{{ config.subscriptions.shorts_channel_size }}</span></p>
<i>Shorts Videos to scan to find new items for the <b>Rescan subscriptions</b> task, max recommended 50.</i><br>
{{ app_form.subscriptions_shorts_channel_size }}
</div>
<div class="settings-item">
<p>Auto start download from your subscriptions: <span class="settings-current">{{ config.subscriptions.auto_start}}</span></p>
<i>Enable this will automatically start and prioritize videos from your subscriptions.</i><br>
{{ app_form.subscriptions_auto_start }}
</div>
</div>
<div class="settings-group">
<h2 id="downloads">Downloads</h2>
<div class="settings-item">
<p>Current download speed limit in KB/s: <span class="settings-current">{{ config.downloads.limit_speed }}</span></p>
<i>Limit download speed. 0 (zero) to deactivate, e.g. 1000 (1MB/s). Speeds are in KB/s. Setting takes effect on new download jobs or application restart.</i><br>
{{ app_form.downloads_limit_speed }}
</div>
<div class="settings-item">
<p>Current throttled rate limit in KB/s: <span class="settings-current">{{ config.downloads.throttledratelimit }}</span></p>
<i>Download will restart if speeds drop below specified amount. 0 (zero) to deactivate, e.g. 100. Speeds are in KB/s.</i><br>
{{ app_form.downloads_throttledratelimit }}
</div>
<div class="settings-item">
<p>Current scraping sleep interval: <span class="settings-current">{{ config.downloads.sleep_interval }}</p>
<i>Seconds to sleep between calls to YouTube. Might be necessary to avoid throttling. Recommended 3.</i><br>
{{ app_form.downloads_sleep_interval }}
</div>
<div class="settings-item">
<p><span class="danger-zone">Danger Zone</span>: Current auto delete watched videos: <span class="settings-current">{{ config.downloads.autodelete_days }}</span></p>
<i>Auto delete watched videos after x days, 0 (zero) to deactivate:</i><br>
{{ app_form.downloads_autodelete_days }}
</div>
</div>
<div class="settings-group">
<h2 id="format">Download Format</h2>
<div class="settings-item">
<p>Limit video and audio quality format for yt-dlp.<br>
Currently: <span class="settings-current">{{ config.downloads.format }}</span>
</p>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">bestvideo[height<=720]+bestaudio/best[height<=720]</span>: best audio and max video height of 720p.</li>
<li><span class="settings-current">bestvideo[height<=1080]+bestaudio/best[height<=1080]</span>: best audio and max video height of 1080p.</li>
<li><span class="settings-current">bestvideo[height<=1080][vcodec*=avc1]+bestaudio[acodec*=mp4a]/mp4</span>: Max 1080p video height with iOS compatible video and audio codecs.</li>
<li><span class="settings-current">0</span>: deactivate and download the best quality possible as decided by yt-dlp.</li>
</ul>
<i>Make sure your custom format gets merged into a single file. Check out the <a href="https://github.com/yt-dlp/yt-dlp#format-selection" target="_blank">documentation</a> for valid configurations.</i><br>
{{ app_form.downloads_format }}
<br>
</div>
<div class="settings-item">
<p>Force sort order to have precedence over all yt-dlp fields.<br>
Currently: <span class="settings-current">{{ config.downloads.format_sort }}</span>
</p>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">res,codec:av1</span>: prefer AV1 over all other video codecs.</li>
<li><span class="settings-current">0</span>: deactivate and keep the default as decided by yt-dlp.</li>
</ul>
<i>Not all codecs are supported by all browsers. The default value ensures best compatibility. Check out the <a href="https://github.com/yt-dlp/yt-dlp#sorting-formats" target="_blank">documentation</a> for valid configurations.</i><br>
{{ app_form.downloads_format_sort }}
<br>
</div>
<div class="settings-item">
<p>Prefer translated metadata language: <span class="settings-current">{{ config.downloads.extractor_lang }}</span></p>
<i>This will change the language this video gets indexed as. That will only be available if the uploader provides translations. Add as two letter ISO language code, check the <a href="https://github.com/yt-dlp/yt-dlp#youtube" target="_blank">documentation</a> which languages are available.</i><br>
{{ app_form.downloads_extractor_lang}}
</div>
<div class="settings-item">
<p>Current metadata embed setting: <span class="settings-current">{{ config.downloads.add_metadata }}</span></p>
<i>Metadata is not embedded into the downloaded files by default.</i><br>
{{ app_form.downloads_add_metadata }}
</div>
<div class="settings-item">
<p>Current thumbnail embed setting: <span class="settings-current">{{ config.downloads.add_thumbnail }}</span></p>
<i>Embed thumbnail into the mediafile.</i><br>
{{ app_form.downloads_add_thumbnail }}
</div>
</div>
<div class="settings-group">
<h2 id="format">Subtitles</h2>
<div class="settings-item">
<p>Subtitles download setting: <span class="settings-current">{{ config.downloads.subtitle }}</span><br>
<i>Choose which subtitles to download, add comma separated language codes,<br>
e.g. <span class="settings-current">en, de, zh-Hans</span></i><br>
{{ app_form.downloads_subtitle }}</p>
</div>
<div class="settings-item">
<p>Subtitle source settings: <span class="settings-current">{{ config.downloads.subtitle_source }}</span></p>
<i>Download only user generated, or also less accurate auto generated subtitles.</i><br>
{{ app_form.downloads_subtitle_source }}
</div>
<div class="settings-item">
<p>Index and make subtitles searchable: <span class="settings-current">{{ config.downloads.subtitle_index }}</span></p>
<i>Store subtitle lines in Elasticsearch. Not recommended for low-end hardware.</i><br>
{{ app_form.downloads_subtitle_index }}
</div>
</div>
<div class="settings-group">
<h2 id="comments">Comments</h2>
<div class="settings-item">
<p>Download and index comments: <span class="settings-current">{{ config.downloads.comment_max }}</span><br>
<i>Follow the yt-dlp max_comments documentation, <a href="https://github.com/yt-dlp/yt-dlp#youtube" target="_blank">max-comments,max-parents,max-replies,max-replies-per-thread</a>:</i><br>
<p>Example configurations:</p>
<ul>
<li><span class="settings-current">all,100,all,30</span>: Get 100 max-parents and 30 max-replies-per-thread.</li>
<li><span class="settings-current">1000,all,all,50</span>: Get a total of 1000 comments over all, 50 replies per thread.</li>
</ul>
{{ app_form.downloads_comment_max }}</p>
</div>
<div class="settings-item">
<p>Selected comment sort method: <span class="settings-current">{{ config.downloads.comment_sort }}</span><br>
<i>Select how many comments and threads to download:</i><br>
{{ app_form.downloads_comment_sort }}</p>
</div>
</div>
<div class="settings-group">
<h2 id="format">Cookie</h2>
<div class="settings-item">
<p>Import YouTube cookie: <span class="settings-current">{{ config.downloads.cookie_import }}</span><br></p>
<p>For automatic cookie import use <b>Tube Archivist Companion</b> <a href="https://github.com/tubearchivist/browser-extension" target="_blank">browser extension</a>.</p>
<i>For manual cookie import, place your cookie file named <span class="settings-current">cookies.google.txt</span> in <span class="settings-current">cache/import</span> before enabling. Instructions in the <a href="https://docs.tubearchivist.com/settings/" target="_blank">Wiki.</a></i><br>
{{ app_form.downloads_cookie_import }}<br>
{% if config.downloads.cookie_import %}
<div id="cookieMessage">
<button onclick="handleCookieValidate()" type="button" id="cookieButton">Validate Cookie File</button>
</div>
{% endif %}
</div>
</div>
<div class="settings-group">
<h2 id="integrations">Integrations</h2>
<div class="settings-item">
<p>API token: <button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button></p>
<div id="text-reveal" class="description-text">
<p>{{ api_token }}</p>
<button class="danger-button" type="button" onclick="resetToken()">Revoke</button>
</div>
</div>
<div class="settings-item">
<p>Integrate with <a href="https://returnyoutubedislike.com/" target="_blank">returnyoutubedislike.com</a> to get dislikes and average ratings back: <span class="settings-current">{{ config.downloads.integrate_ryd }}</span></p>
<i>Before activating that, make sure you have a scraping sleep interval of at least 3 secs set to avoid ratelimiting issues.</i><br>
{{ app_form.downloads_integrate_ryd }}
</div>
<div class="settings-item">
<p>Integrate with <a href="https://sponsor.ajay.app/" target="_blank">SponsorBlock</a> to get sponsored timestamps: <span class="settings-current">{{ config.downloads.integrate_sponsorblock }}</span></p>
<i>Before activating that, make sure you have a scraping sleep interval of at least 3 secs set to avoid ratelimiting issues.</i><br>
{{ app_form.downloads_integrate_sponsorblock }}
</div>
</div>
<div class="settings-group">
<h2 id="snapshots">Snapshots</h2>
<div class="settings-item">
<p>Current system snapshot: <span class="settings-current">{{ config.application.enable_snapshot }}</span></p>
<i>Automatically create daily deduplicated snapshots of the index, stored in Elasticsearch. Read first before activating: <a target="_blank" href="https://docs.tubearchivist.com/settings/#snapshots">Wiki</a>.</i><br>
{{ app_form.application_enable_snapshot }}
</div>
<div>
{% if snapshots %}
<p>Create next snapshot: <span class="settings-current">{{ snapshots.next_exec_str }}</span>, snapshots expire after <span class="settings-current">{{ snapshots.expire_after }}</span>. <button onclick="createSnapshot()" id="createButton">Create snapshot now</button></p>
<br>
{% for snapshot in snapshots.snapshots %}
<p><button id="{{ snapshot.id }}" onclick="restoreSnapshot(id)">Restore</button> Snapshot created on: <span class="settings-current">{{ snapshot.start_date }}</span>, took <span class="settings-current">{{ snapshot.duration_s }}s</span> to create. State: <i>{{ snapshot.state }}</i></p>
{% endfor %}
{% endif %}
</div>
</div>
<button type="submit" name="application-settings">Update Application Configurations</button>
</form>
{% endblock settings_content %}

View File

@ -0,0 +1,154 @@
{% extends "home/base_settings.html" %}
{% load static %}
{% block settings_content %}
<div class="title-bar">
<h1>Scheduler Setup</h1>
<div class="settings-group">
<p>Schedule settings expect a cron like format, where the first value is minute, second is hour and third is day of the week.</p>
<p>Examples:</p>
<ul>
<li><span class="settings-current">0 15 *</span>: Run task every day at 15:00 in the afternoon.</li>
<li><span class="settings-current">30 8 */2</span>: Run task every second day of the week (Sun, Tue, Thu, Sat) at 08:30 in the morning.</li>
<li><span class="settings-current">auto</span>: Sensible default.</li>
<li><span class="settings-current">0</span>: (zero), deactivate that task.</li>
</ul>
<p>Note:</p>
<ul>
<li>Changes in the scheduler settings require a container restart to take effect.</li>
<li>Avoid an unnecessary frequent schedule to not get blocked by YouTube. For that reason, the scheduler doesn't support schedules that trigger more than once per hour.</li>
</ul>
</div>
</div>
<form action="{% url 'settings_scheduling' %}" method="POST" name="scheduler-update">
{% csrf_token %}
<div class="settings-group">
<h2>Rescan Subscriptions</h2>
<div class="settings-item">
<p>Current rescan schedule: <span class="settings-current">
{% if config.scheduler.update_subscribed %}
{% for key, value in config.scheduler.update_subscribed.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Become a sponsor and join <a href="https://members.tubearchivist.com/" target="_blank">members.tubearchivist.com</a> to get access to <span class="settings-current">real time</span> notifications for new videos uploaded by your favorite channels.</p>
<p>Periodically rescan your subscriptions:</p>
{{ scheduler_form.update_subscribed }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.update_subscribed_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.update_subscribed_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.update_subscribed_notify }}</span></p>
{% endif %}
{{ scheduler_form.update_subscribed_notify }}
</div>
</div>
<div class="settings-group">
<h2>Start download</h2>
<div class="settings-item">
<p>Current Download schedule: <span class="settings-current">
{% if config.scheduler.download_pending %}
{% for key, value in config.scheduler.download_pending.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Automatic video download schedule:</p>
{{ scheduler_form.download_pending }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.download_pending_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.download_pending_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.download_pending_notify }}</span></p>
{% endif %}
{{ scheduler_form.download_pending_notify }}
</div>
</div>
<div class="settings-group">
<h2>Refresh Metadata</h2>
<div class="settings-item">
<p>Current Metadata refresh schedule: <span class="settings-current">
{% if config.scheduler.check_reindex %}
{% for key, value in config.scheduler.check_reindex.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Daily schedule to refresh metadata from YouTube:</p>
{{ scheduler_form.check_reindex }}
</div>
<div class="settings-item">
<p>Current refresh for metadata older than x days: <span class="settings-current">{{ config.scheduler.check_reindex_days }}</span></p>
<p>Refresh older than x days, recommended 90:</p>
{{ scheduler_form.check_reindex_days }}
</div>
<div class="settings-item">
<p>Send notification on task completed:</p>
{% if config.scheduler.check_reindex_notify %}
<p><button type="button" onclick="textReveal(this)" id="text-reveal-button">Show</button> stored notification links</p>
<div id="text-reveal" class="description-text">
<p>{{ config.scheduler.check_reindex_notify|linebreaks }}</p>
</div>
{% else %}
<p>Current notification urls: <span class="settings-current">{{ config.scheduler.check_reindex_notify }}</span></p>
{% endif %}
{{ scheduler_form.check_reindex_notify }}
</div>
</div>
<div class="settings-group">
<h2>Thumbnail check</h2>
<div class="settings-item">
<p>Current thumbnail check schedule: <span class="settings-current">
{% if config.scheduler.thumbnail_check %}
{% for key, value in config.scheduler.thumbnail_check.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Periodically check and cleanup thumbnails:</p>
{{ scheduler_form.thumbnail_check }}
</div>
</div>
<div class="settings-group">
<h2>ZIP file index backup</h2>
<div class="settings-item">
<p><i>Zip file backups are very slow for large archives and consistency is not guaranteed, use snapshots instead. Make sure no other tasks are running when creating a Zip file backup.</i></p>
<p>Current index backup schedule: <span class="settings-current">
{% if config.scheduler.run_backup %}
{% for key, value in config.scheduler.run_backup.items %}
{{ value }}
{% endfor %}
{% else %}
False
{% endif %}
</span></p>
<p>Automatically backup metadata to a zip file:</p>
{{ scheduler_form.run_backup }}
</div>
<div class="settings-item">
<p>Current backup files to keep: <span class="settings-current">{{ config.scheduler.run_backup_rotate }}</span></p>
<p>Max auto backups to keep:</p>
{{ scheduler_form.run_backup_rotate }}
</div>
</div>
<button type="submit" name="scheduler-settings">Update Scheduler Settings</button>
</form>
{% endblock settings_content %}

View File

@ -0,0 +1,37 @@
{% extends "home/base_settings.html" %}
{% load static %}
{% block settings_content %}
<div class="title-bar">
<h1>User Configurations</h1>
</div>
<form action="{% url 'settings_user' %}" method="POST" name="user-update">
{% csrf_token %}
<div class="settings-group">
<h2>Color scheme</h2>
<div class="settings-item">
<p>Current color scheme: <span class="settings-current">{{ config.application.colors }}</span></p>
<i>Select your preferred color scheme between dark and light mode.</i><br>
{{ user_form.colors }}
</div>
</div>
<div class="settings-group">
<h2>Archive View</h2>
<div class="settings-item">
<p>Current page size: <span class="settings-current">{{ config.archive.page_size }}</span></p>
<i>Result of videos showing in archive page</i><br>
{{ user_form.page_size }}
</div>
</div>
<button type="submit" name="user-settings">Update User Configurations</button>
</form>
{% if request.user.is_superuser %}
<div class="title-bar">
<h1>Users</h1>
</div>
<div class="settings-group">
<h2>User Management</h2>
<p>Access the admin interface for basic user management functionality like adding and deleting users, changing passwords and more.</p>
<a href="/admin/"><button>Admin Interface</button></a>
</div>
{% endif %}
{% endblock settings_content %}

View File

@ -26,6 +26,26 @@ urlpatterns = [
login_required(views.SettingsView.as_view()),
name="settings",
),
path(
"settings/user/",
login_required(views.SettingsUserView.as_view()),
name="settings_user",
),
path(
"settings/application/",
login_required(views.SettingsApplicationView.as_view()),
name="settings_application",
),
path(
"settings/scheduling/",
login_required(views.SettingsSchedulingView.as_view()),
name="settings_scheduling",
),
path(
"settings/actions/",
login_required(views.SettingsActionsView.as_view()),
name="settings_actions",
),
path("process/", login_required(views.process), name="process"),
path(
"channel/",

View File

@ -981,7 +981,21 @@ class SearchView(ArchivistResultsView):
class SettingsView(MinView):
"""resolves to /settings/
handle the settings page, display current settings,
handle the settings dashboard
"""
def get(self, request):
"""read and display the dashboard"""
context = self.get_min_context(request)
context.update({"title": "Settings Dashboard"})
return render(request, "home/settings.html", context)
class SettingsUserView(MinView):
"""resolves to /settings/user/
handle the settings sub-page for user settings,
display current settings,
take post request from the form to update settings
"""
@ -990,18 +1004,47 @@ class SettingsView(MinView):
context = self.get_min_context(request)
context.update(
{
"title": "Settings",
"title": "User Settings",
"config": AppConfig(request.user.id).config,
"user_form": UserSettingsForm(),
}
)
return render(request, "home/settings_user.html", context)
def post(self, request):
"""handle form post to update settings"""
user_form = UserSettingsForm(request.POST)
if user_form.is_valid():
user_form_post = user_form.cleaned_data
if any(user_form_post.values()):
AppConfig().set_user_config(user_form_post, request.user.id)
sleep(1)
return redirect("settings_user", permanent=True)
class SettingsApplicationView(MinView):
"""resolves to /settings/application/
handle the settings sub-page for application configuration,
display current settings,
take post request from the form to update settings
"""
def get(self, request):
"""read and display current application settings"""
context = self.get_min_context(request)
context.update(
{
"title": "Application Settings",
"config": AppConfig(request.user.id).config,
"api_token": self.get_token(request),
"available_backups": ElasticBackup().get_all_backup_files(),
"user_form": UserSettingsForm(),
"app_form": ApplicationSettingsForm(),
"scheduler_form": SchedulerSettingsForm(),
"snapshots": ElasticSnapshot().get_snapshot_stats(),
}
)
return render(request, "home/settings.html", context)
return render(request, "home/settings_application.html", context)
@staticmethod
def get_token(request):
@ -1012,12 +1055,7 @@ class SettingsView(MinView):
def post(self, request):
"""handle form post to update settings"""
user_form = UserSettingsForm(request.POST)
config_handler = AppConfig()
if user_form.is_valid():
user_form_post = user_form.cleaned_data
if any(user_form_post.values()):
config_handler.set_user_config(user_form_post, request.user.id)
app_form = ApplicationSettingsForm(request.POST)
if app_form.is_valid():
@ -1027,15 +1065,8 @@ class SettingsView(MinView):
updated = config_handler.update_config(app_form_post)
self.post_process_updated(updated, config_handler.config)
scheduler_form = SchedulerSettingsForm(request.POST)
if scheduler_form.is_valid():
scheduler_form_post = scheduler_form.cleaned_data
if any(scheduler_form_post.values()):
print(scheduler_form_post)
ScheduleBuilder().update_schedule_conf(scheduler_form_post)
sleep(1)
return redirect("settings", permanent=True)
return redirect("settings_application", permanent=True)
def post_process_updated(self, updated, config):
"""apply changes for config"""
@ -1080,6 +1111,57 @@ class SettingsView(MinView):
RedisArchivist().set_message(key, message=message, expire=True)
class SettingsSchedulingView(MinView):
"""resolves to /settings/scheduling/
handle the settings sub-page for scheduling settings,
display current settings,
take post request from the form to update settings
"""
def get(self, request):
"""read and display current settings"""
context = self.get_min_context(request)
context.update(
{
"title": "Scheduling Settings",
"config": AppConfig(request.user.id).config,
"scheduler_form": SchedulerSettingsForm(),
}
)
return render(request, "home/settings_scheduling.html", context)
def post(self, request):
"""handle form post to update settings"""
scheduler_form = SchedulerSettingsForm(request.POST)
if scheduler_form.is_valid():
scheduler_form_post = scheduler_form.cleaned_data
if any(scheduler_form_post.values()):
print(scheduler_form_post)
ScheduleBuilder().update_schedule_conf(scheduler_form_post)
sleep(1)
return redirect("settings_scheduling", permanent=True)
class SettingsActionsView(MinView):
"""resolves to /settings/actions/
handle the settings actions sub-page
"""
def get(self, request):
"""read and display current settings"""
context = self.get_min_context(request)
context.update(
{
"title": "Actions",
"available_backups": ElasticBackup().get_all_backup_files(),
}
)
return render(request, "home/settings_actions.html", context)
def process(request):
"""handle all the buttons calls via POST ajax"""
if request.method == "POST":

View File

@ -960,15 +960,15 @@ video:-webkit-full-screen {
transform: translateX(-30%);
}
.info-box-item.channel-nav {
.info-box-item.child-page-nav {
justify-content: center;
}
.info-box-item.channel-nav a {
.info-box-item.child-page-nav a {
padding: 0 1rem;
}
.info-box-item.channel-nav a:hover {
.info-box-item.child-page-nav a:hover {
text-decoration: underline;
}