__init__.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. from youtube import util
  2. from .get_app_version import app_version
  3. import flask
  4. from flask import request
  5. import jinja2
  6. import settings
  7. import traceback
  8. import re
  9. from sys import exc_info
  10. yt_app = flask.Flask(__name__)
  11. yt_app.config['TEMPLATES_AUTO_RELOAD'] = True
  12. yt_app.url_map.strict_slashes = False
  13. # yt_app.jinja_env.trim_blocks = True
  14. # yt_app.jinja_env.lstrip_blocks = True
  15. yt_app.add_url_rule('/settings', 'settings_page', settings.settings_page, methods=['POST', 'GET'])
  16. @yt_app.route('/')
  17. def homepage():
  18. return flask.render_template('home.html', title="YT Local")
  19. @yt_app.route('/licenses')
  20. def licensepage():
  21. return flask.render_template(
  22. 'licenses.html',
  23. title="Licenses - YT Local"
  24. )
  25. theme_names = {
  26. 0: 'light_theme',
  27. 1: 'gray_theme',
  28. 2: 'dark_theme',
  29. }
  30. @yt_app.context_processor
  31. def inject_theme_preference():
  32. return {
  33. 'theme_path': '/youtube.com/static/' + theme_names[settings.theme] + '.css',
  34. 'settings': settings,
  35. # Detect version
  36. 'current_version': app_version()['version'],
  37. 'current_branch': app_version()['branch'],
  38. 'current_commit': app_version()['commit'],
  39. }
  40. @yt_app.template_filter('commatize')
  41. def commatize(num):
  42. if num is None:
  43. return ''
  44. if isinstance(num, str):
  45. try:
  46. num = int(num)
  47. except ValueError:
  48. return num
  49. return '{:,}'.format(num)
  50. def timestamp_replacement(match):
  51. time_seconds = 0
  52. for part in match.group(0).split(':'):
  53. time_seconds = 60*time_seconds + int(part)
  54. return (
  55. """
  56. <a href="#" id="timestamp%s">%s</a>
  57. <script>
  58. // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
  59. (function main() {
  60. 'use strict';
  61. const player = document.getElementById('js-video-player');
  62. const a = document.getElementById('timestamp%s');
  63. a.addEventListener('click', function(event) {
  64. player.currentTime = %s
  65. });
  66. }());
  67. // @license-end
  68. </script>
  69. """ % (
  70. str(time_seconds),
  71. match.group(0),
  72. str(time_seconds),
  73. str(time_seconds)
  74. )
  75. )
  76. TIMESTAMP_RE = re.compile(r'\b(\d?\d:)?\d?\d:\d\d\b')
  77. @yt_app.template_filter('timestamps')
  78. def timestamps(text):
  79. return TIMESTAMP_RE.sub(timestamp_replacement, text)
  80. @yt_app.errorhandler(500)
  81. def error_page(e):
  82. slim = request.args.get('slim', False) # whether it was an ajax request
  83. if (exc_info()[0] == util.FetchError
  84. and exc_info()[1].code == '429'
  85. and settings.route_tor
  86. ):
  87. error_message = ('Error: YouTube blocked the request because the Tor'
  88. ' exit node is overutilized. Try getting a new exit node by'
  89. ' using the New Identity button in the Tor Browser.')
  90. if exc_info()[1].error_message:
  91. error_message += '\n\n' + exc_info()[1].error_message
  92. if exc_info()[1].ip:
  93. error_message += '\n\nExit node IP address: ' + exc_info()[1].ip
  94. return flask.render_template('error.html', error_message=error_message, slim=slim), 502
  95. elif exc_info()[0] == util.FetchError and exc_info()[1].error_message:
  96. return (flask.render_template(
  97. 'error.html',
  98. error_message=exc_info()[1].error_message,
  99. slim=slim
  100. ), 502)
  101. elif (exc_info()[0] == util.FetchError
  102. and exc_info()[1].code == '404'
  103. ):
  104. error_message = ('Error: The page you are looking for isn\'t here. ¯\_(ツ)_/¯')
  105. return flask.render_template('error.html',
  106. error_code=exc_info()[1].code,
  107. error_message=error_message,
  108. slim=slim), 404
  109. return flask.render_template('error.html', traceback=traceback.format_exc(),
  110. error_code=exc_info()[1].code,
  111. slim=slim), 500
  112. # return flask.render_template('error.html', traceback=traceback.format_exc(), slim=slim), 500
  113. font_choices = {
  114. 0: 'initial',
  115. 1: '"liberation serif", "times new roman", calibri, carlito, serif',
  116. 2: 'arial, "liberation sans", sans-serif',
  117. 3: 'verdana, sans-serif',
  118. 4: 'tahoma, sans-serif',
  119. }
  120. @yt_app.route('/shared.css')
  121. def get_css():
  122. return flask.Response(
  123. flask.render_template(
  124. 'shared.css',
  125. font_family=font_choices[settings.font]
  126. ),
  127. mimetype='text/css',
  128. )
  129. # This is okay because the flask urlize function puts the href as the first
  130. # property
  131. YOUTUBE_LINK_RE = re.compile(r'<a href="(' + util.YOUTUBE_URL_RE_STR + ')"')
  132. old_urlize = jinja2.filters.urlize
  133. def prefix_urlize(*args, **kwargs):
  134. result = old_urlize(*args, **kwargs)
  135. return YOUTUBE_LINK_RE.sub(r'<a href="/\1"', result)
  136. jinja2.filters.urlize = prefix_urlize