diff options
-rw-r--r-- | settings.py | 118 | ||||
-rw-r--r-- | youtube/templates/settings.html | 64 |
2 files changed, 159 insertions, 23 deletions
diff --git a/settings.py b/settings.py index d02a1fd..3e2e1d0 100644 --- a/settings.py +++ b/settings.py @@ -1,12 +1,17 @@ +from youtube import util, yt_app import ast import re import os import collections +import flask +from flask import request + settings_info = collections.OrderedDict([ ('route_tor', { 'type': bool, 'default': False, + 'label': 'Route Tor', 'comment': '', }), @@ -21,6 +26,7 @@ settings_info = collections.OrderedDict([ 'default': False, 'comment': '''This will allow others to connect to your Youtube Local instance as a website. For security reasons, enabling this is not recommended.''', + 'hidden': True, }), ('subtitles_mode', { @@ -29,6 +35,12 @@ For security reasons, enabling this is not recommended.''', 'comment': '''0 - off by default 1 - only manually created subtitles on by default 2 - enable even if automatically generated is all that's available''', + 'label': 'Default subtitles mode', + 'options': [ + (0, 'Off'), + (1, 'Manually created only'), + (2, 'Automatic if manual unavailable'), + ], }), ('subtitles_language', { @@ -42,7 +54,12 @@ For security reasons, enabling this is not recommended.''', 'default': 1, 'comment': '''0 - Related videos disabled 1 - Related videos always shown -2 - Related videos hidden; shown by clicking a button''' +2 - Related videos hidden; shown by clicking a button''', + 'options': [ + (0, 'Disabled'), + (1, 'Always shown'), + (2, 'Shown by clicking button'), + ], }), ('comments_mode', { @@ -51,6 +68,11 @@ For security reasons, enabling this is not recommended.''', 'comment': '''0 - Video comments disabled 1 - Video comments always shown 2 - Video comments hidden; shown by clicking a button''', + 'options': [ + (0, 'Disabled'), + (1, 'Always shown'), + (2, 'Shown by clicking button'), + ], }), ('enable_comment_avatars', { @@ -64,6 +86,10 @@ For security reasons, enabling this is not recommended.''', 'default': 0, 'comment': '''0 to sort by top 1 to sort by newest''', + 'options': [ + (0, 'Top'), + (1, 'Newest'), + ], }), ('theater_mode', { @@ -88,18 +114,21 @@ For security reasons, enabling this is not recommended.''', 'type': bool, 'default': False, 'comment': '''Developer use to debug 403s''', + 'hidden': True, }), ('debugging_save_responses', { 'type': bool, 'default': False, 'comment': '''Save all responses from youtube for debugging''', + 'hidden': True, }), ('settings_version', { 'type': int, 'default': 2, - 'comment': '''Do not change, remove, or comment out this value, or else your settings may be lost or corrupted''' + 'comment': '''Do not change, remove, or comment out this value, or else your settings may be lost or corrupted''', + 'hidden': True, }), ]) @@ -112,6 +141,11 @@ def comment_string(comment): result += '# ' + line + '\n' return result +def save_settings(settings): + with open(settings_file_path, 'w', encoding='utf-8') as file: + for setting_name, default_setting_dict in settings_info.items(): + file.write(comment_string(default_setting_dict['comment']) + setting_name + ' = ' + repr(settings[setting_name]) + '\n\n') + def create_missing_settings_string(current_settings): result = '' @@ -123,6 +157,11 @@ def create_missing_settings_string(current_settings): def create_default_settings_string(): return settings_to_string({}) +def add_missing_settings(settings): + result = default_settings() + result.update(settings) + return result + def default_settings(): return {key: setting_info['default'] for key, setting_info in settings_info.items()} @@ -166,21 +205,19 @@ else: settings_file_path = os.path.join(settings_dir, 'settings.txt') -locals().update(default_settings()) - try: with open(settings_file_path, 'r', encoding='utf-8') as file: settings_text = file.read() except FileNotFoundError: - with open(settings_file_path, 'w', encoding='utf-8') as file: - file.write(create_default_settings_string()) + settings = default_settings() + save_settings(settings) else: if re.fullmatch(r'\s*', settings_text): # blank file - with open(settings_file_path, 'w', encoding='utf-8') as file: - file.write(create_default_settings_string()) + settings = default_settings() + save_settings(settings) else: # parse settings in a safe way, without exec - current_settings = {} + settings = {} attributes = { ast.NameConstant: 'value', ast.Num: 'n', @@ -209,26 +246,21 @@ else: log_ignored_line(node.lineno, "only literals allowed for values") continue - current_settings[target.id] = node.value.__getattribute__(attributes[type(node.value)]) + settings[target.id] = node.value.__getattribute__(attributes[type(node.value)]) - if 'settings_version' not in current_settings: + if 'settings_version' not in settings: print('Upgrading settings.txt') - new_settings = upgrade_to_2(current_settings) - locals().update(new_settings) - new_settings_string = settings_to_string(new_settings) - with open(settings_file_path, 'w', encoding='utf-8') as file: - file.write(new_settings_string) + settings = add_missing_settings(upgrade_to_2(settings)) + save_settings(settings) # some settings not in the file, add those missing settings to the file - elif len(settings_info.keys() - current_settings.keys()) != 0: + elif not settings.keys() >= settings_info.keys(): print('Adding missing settings to settings.txt') - append_text = create_missing_settings_string(current_settings) - with open(settings_file_path, 'a', encoding='utf-8') as file: - file.write('\n\n' + append_text) - locals().update(current_settings) - else: - locals().update(current_settings) + settings = add_missing_settings(settings) + save_settings(settings) + +locals().update(settings) @@ -237,3 +269,43 @@ if route_tor: print("Tor routing is ON") else: print("Tor routing is OFF - your Youtube activity is NOT anonymous") + + + +@yt_app.route('/settings', methods=['POST', 'GET']) +def settings_page(): + if request.method == 'GET': + return flask.render_template('settings.html', + settings = [(setting_name, setting_info, settings[setting_name]) for setting_name, setting_info in settings_info.items()] + ) + elif request.method == 'POST': + for key, value in request.values.items(): + if key in settings_info: + if settings_info[key]['type'] is bool and value == 'on': + settings[key] = True + else: + settings[key] = settings_info[key]['type'](value) + else: + flask.abort(400) + + # need this bullshit because browsers don't send anything when an input is unchecked + expected_inputs = {setting_name for setting_name, setting_info in settings_info.items() if not settings_info[setting_name].get('hidden', False)} + missing_inputs = expected_inputs - set(request.values.keys()) + for setting_name in missing_inputs: + assert settings_info[setting_name]['type'] is bool, missing_inputs + settings[setting_name] = False + + globals().update(settings) + save_settings(settings) + return flask.redirect(util.URL_ORIGIN + '/settings', 303) + else: + flask.abort(400) + + + + + + + + + diff --git a/youtube/templates/settings.html b/youtube/templates/settings.html new file mode 100644 index 0000000..bac3ef9 --- /dev/null +++ b/youtube/templates/settings.html @@ -0,0 +1,64 @@ +{% set page_title = 'Settings' %} +{% extends "base.html" %} +{% import "common_elements.html" as common_elements %} +{% block style %} + .settings-form { + margin: auto; + padding: 10px; + display: inline-block; + background-color: #dadada; + } + .settings-list{ + list-style: none; + padding: 0px; + } + .setting-item{ + margin-bottom: 10px; + background-color: #eeeeee; + padding: 5px; + } + .setting-item label{ + display: inline-block; + width: 250px; + } + +{% endblock style %} + +{% block main %} + <form method="POST" class="settings-form"> + <ul class="settings-list"> + {% for setting_name, setting_info, value in settings %} + {% if not setting_info.get('hidden', false) %} + <li class="setting-item"> + {% if 'label' is in(setting_info) %} + <label for="{{ 'setting_' + setting_name }}">{{ setting_info['label'] }}</label> + {% else %} + <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> + {% endif %} + + {% if setting_info['type'].__name__ == 'bool' %} + <input type="checkbox" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" {{ 'checked' if value else '' }}> + {% elif setting_info['type'].__name__ == 'int' %} + {% if 'options' is in(setting_info) %} + <select id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}"> + {% for option in setting_info['options'] %} + <option value="{{ option[0] }}" {{ 'selected' if option[0] == value else '' }}>{{ option[1] }}</option> + {% endfor %} + </select> + {% else %} + <input type="number" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}" step="1"> + {% endif %} + {% elif setting_info['type'].__name__ == 'float' %} + + {% elif setting_info['type'].__name__ == 'str' %} + <input type="text" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}"> + {% else %} + <span>Error: Unknown setting type: setting_info['type'].__name__</span> + {% endif %} + </li> + {% endif %} + {% endfor %} + </ul> + <input type="submit" value="Save settings"> + </form> +{% endblock main %} |