aboutsummaryrefslogtreecommitdiffstats
path: root/youtube/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'youtube/__init__.py')
-rw-r--r--youtube/__init__.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/youtube/__init__.py b/youtube/__init__.py
new file mode 100644
index 0000000..a8a725d
--- /dev/null
+++ b/youtube/__init__.py
@@ -0,0 +1,203 @@
+from youtube import util
+from .get_app_version import app_version
+import flask
+from flask import request
+import jinja2
+import settings
+import traceback
+import re
+from sys import exc_info
+from flask_babel import Babel
+
+yt_app = flask.Flask(__name__)
+yt_app.config['TEMPLATES_AUTO_RELOAD'] = True
+yt_app.url_map.strict_slashes = False
+# yt_app.jinja_env.trim_blocks = True
+# yt_app.jinja_env.lstrip_blocks = True
+
+# Configure Babel for i18n
+import os
+yt_app.config['BABEL_DEFAULT_LOCALE'] = 'en'
+# Use absolute path for translations directory to avoid issues with package structure changes
+_app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+yt_app.config['BABEL_TRANSLATION_DIRECTORIES'] = os.path.join(_app_root, 'translations')
+
+def get_locale():
+ """Determine the best locale based on user preference or browser settings"""
+ # Check if user has a language preference in settings
+ if hasattr(settings, 'language') and settings.language:
+ locale = settings.language
+ print(f'[i18n] Using user preference: {locale}')
+ return locale
+ # Otherwise, use browser's Accept-Language header
+ # Only match languages with available translations
+ locale = request.accept_languages.best_match(['en', 'es'])
+ print(f'[i18n] Using browser language: {locale}')
+ return locale or 'en'
+
+babel = Babel(yt_app, locale_selector=get_locale)
+
+
+yt_app.add_url_rule('/settings', 'settings_page', settings.settings_page, methods=['POST', 'GET'])
+
+
+@yt_app.route('/')
+def homepage():
+ return flask.render_template('home.html', title="YT Local")
+
+
+@yt_app.route('/licenses')
+def licensepage():
+ return flask.render_template(
+ 'licenses.html',
+ title="Licenses - YT Local"
+ )
+
+
+theme_names = {
+ 0: 'light_theme',
+ 1: 'gray_theme',
+ 2: 'dark_theme',
+}
+
+
+@yt_app.context_processor
+def inject_theme_preference():
+ return {
+ 'theme_path': '/youtube.com/static/' + theme_names[settings.theme] + '.css',
+ 'settings': settings,
+ # Detect version
+ 'current_version': app_version()['version'],
+ 'current_branch': app_version()['branch'],
+ 'current_commit': app_version()['commit'],
+ }
+
+
+@yt_app.template_filter('commatize')
+def commatize(num):
+ if num is None:
+ return ''
+ if isinstance(num, str):
+ try:
+ num = int(num)
+ except ValueError:
+ return num
+ return '{:,}'.format(num)
+
+
+def timestamp_replacement(match):
+ time_seconds = 0
+ for part in match.group(0).split(':'):
+ time_seconds = 60*time_seconds + int(part)
+ return (
+ """
+ <a href="#" id="timestamp%s">%s</a>
+ <script>
+ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
+ (function main() {
+ 'use strict';
+ const player = document.getElementById('js-video-player');
+ const a = document.getElementById('timestamp%s');
+ a.addEventListener('click', function(event) {
+ player.currentTime = %s
+ });
+ }());
+ // @license-end
+ </script>
+ """ % (
+ str(time_seconds),
+ match.group(0),
+ str(time_seconds),
+ str(time_seconds)
+ )
+ )
+
+
+TIMESTAMP_RE = re.compile(r'\b(\d?\d:)?\d?\d:\d\d\b')
+
+
+@yt_app.template_filter('timestamps')
+def timestamps(text):
+ return TIMESTAMP_RE.sub(timestamp_replacement, text)
+
+
+@yt_app.errorhandler(500)
+def error_page(e):
+ slim = request.args.get('slim', False) # whether it was an ajax request
+ if (exc_info()[0] == util.FetchError
+ and exc_info()[1].code == '429'
+ and settings.route_tor
+ ):
+ error_message = ('Error: YouTube blocked the request because the Tor'
+ ' exit node is overutilized. Try getting a new exit node by'
+ ' using the New Identity button in the Tor Browser.')
+ if exc_info()[1].error_message:
+ error_message += '\n\n' + exc_info()[1].error_message
+ if exc_info()[1].ip:
+ error_message += '\n\nExit node IP address: ' + exc_info()[1].ip
+ return flask.render_template('error.html', error_message=error_message, slim=slim), 502
+ elif exc_info()[0] == util.FetchError and exc_info()[1].error_message:
+ # Handle specific error codes with user-friendly messages
+ error_code = exc_info()[1].code
+ error_msg = exc_info()[1].error_message
+
+ if error_code == '400':
+ error_message = (f'Error: Bad Request (400)\n\n{error_msg}\n\n'
+ 'This usually means the URL or parameters are invalid. '
+ 'Try going back and trying a different option.')
+ elif error_code == '404':
+ error_message = 'Error: The page you are looking for isn\'t here.'
+ else:
+ error_message = f'Error: {error_code} - {error_msg}'
+
+ return (flask.render_template(
+ 'error.html',
+ error_message=error_message,
+ slim=slim
+ ), 502)
+ elif (exc_info()[0] == util.FetchError
+ and exc_info()[1].code == '404'
+ ):
+ error_message = ('Error: The page you are looking for isn\'t here.')
+ return flask.render_template('error.html',
+ error_code=exc_info()[1].code,
+ error_message=error_message,
+ slim=slim), 404
+ return flask.render_template('error.html', traceback=traceback.format_exc(),
+ error_code=exc_info()[1].code,
+ slim=slim), 500
+ # return flask.render_template('error.html', traceback=traceback.format_exc(), slim=slim), 500
+
+
+font_choices = {
+ 0: 'initial',
+ 1: '"liberation serif", "times new roman", calibri, carlito, serif',
+ 2: 'arial, "liberation sans", sans-serif',
+ 3: 'verdana, sans-serif',
+ 4: 'tahoma, sans-serif',
+}
+
+
+@yt_app.route('/shared.css')
+def get_css():
+ return flask.Response(
+ flask.render_template(
+ 'shared.css',
+ font_family=font_choices[settings.font]
+ ),
+ mimetype='text/css',
+ )
+
+
+# This is okay because the flask urlize function puts the href as the first
+# property
+YOUTUBE_LINK_RE = re.compile(r'<a href="(' + util.YOUTUBE_URL_RE_STR + ')"')
+old_urlize = jinja2.filters.urlize
+
+
+def prefix_urlize(*args, **kwargs):
+ result = old_urlize(*args, **kwargs)
+ return YOUTUBE_LINK_RE.sub(r'<a href="/\1"', result)
+
+
+jinja2.filters.urlize = prefix_urlize