diff --git a/nyaa/routes.py b/nyaa/routes.py index 24c79ce..dcba183 100644 --- a/nyaa/routes.py +++ b/nyaa/routes.py @@ -3,17 +3,15 @@ import math import os.path import re from datetime import datetime, timedelta -from email.utils import formatdate from urllib.parse import quote import flask from flask_paginate import Pagination -from werkzeug import url_encode from werkzeug.datastructures import CombinedMultiDict from sqlalchemy.orm import joinedload -from nyaa import api_handler, app, backend, db, forms, models, torrents, views +from nyaa import api_handler, app, backend, db, forms, models, template_utils, torrents, views from nyaa.search import (DEFAULT_MAX_SEARCH_RESULT, DEFAULT_PER_PAGE, SERACH_PAGINATE_DISPLAY_MSG, _generate_query_string, search_db, search_elastic) from nyaa.utils import cached_function, chain_get @@ -21,51 +19,6 @@ from nyaa.utils import cached_function, chain_get DEBUG_API = False -# For static_cachebuster -_static_cache = {} - - -@app.template_global() -def static_cachebuster(filename): - ''' Adds a ?t= cachebuster to the given path, if the file exists. - Results are cached in memory and persist until app restart! ''' - # Instead of timestamps, we could use commit hashes (we already load it in __init__) - # But that'd mean every static resource would get cache busted. This lets unchanged items - # stay in the cache. - - if app.debug: - # Do not bust cache on debug (helps debugging) - return flask.url_for('static', filename=filename) - - # Get file mtime if not already cached. - if filename not in _static_cache: - file_path = os.path.join(app.static_folder, filename) - file_mtime = None - if os.path.exists(file_path): - file_mtime = int(os.path.getmtime(file_path)) - - _static_cache[filename] = file_mtime - - return flask.url_for('static', filename=filename, t=_static_cache[filename]) - - -@app.template_global() -def modify_query(**new_values): - args = flask.request.args.copy() - - for key, value in new_values.items(): - args[key] = value - - return '{}?{}'.format(flask.request.path, url_encode(args)) - - -@app.template_global() -def filter_truthy(input_list): - ''' Jinja2 can't into list comprehension so this is for - the search_results.html template ''' - return [item for item in input_list if item] - - @app.template_global() def category_name(cat_id): ''' Given a category id (eg. 1_2), returns a category name (eg. Anime - English-translated) ''' @@ -96,18 +49,6 @@ def before_request(): return 'You are banned.', 403 -@app.template_filter('utc_time') -def get_utc_timestamp(datetime_str): - ''' Returns a UTC POSIX timestamp, as seconds ''' - UTC_EPOCH = datetime.utcfromtimestamp(0) - return int((datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S') - UTC_EPOCH).total_seconds()) - - -@app.template_filter('display_time') -def get_display_time(datetime_str): - return datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d %H:%M') - - @cached_function def get_category_id_map(): ''' Reads database for categories and turns them into a dict with @@ -261,16 +202,6 @@ def home(rss): special_results=special_results) -@app.template_filter('rfc822') -def _jinja2_filter_rfc822(date, fmt=None): - return formatdate(date.timestamp()) - - -@app.template_filter('rfc822_es') -def _jinja2_filter_rfc822_es(datestr, fmt=None): - return formatdate(datetime.strptime(datestr, '%Y-%m-%dT%H:%M:%S').timestamp()) - - def render_rss(label, query, use_elastic, magnet_links=False): rss_xml = flask.render_template('rss.xml', use_elastic=use_elastic, @@ -544,40 +475,13 @@ def _get_cached_torrent_file(torrent): return open(cached_torrent, 'rb'), os.path.getsize(cached_torrent) -@app.template_filter() -def timesince(dt, default='just now'): - """ - Returns string representing "time since" e.g. - 3 minutes ago, 5 hours ago etc. - Date and time (UTC) are returned if older than 1 day. - """ - - now = datetime.utcnow() - diff = now - dt - - periods = ( - (diff.days, 'day', 'days'), - (diff.seconds / 3600, 'hour', 'hours'), - (diff.seconds / 60, 'minute', 'minutes'), - (diff.seconds, 'second', 'seconds'), - ) - - if diff.days >= 1: - return dt.strftime('%Y-%m-%d %H:%M UTC') - else: - for period, singular, plural in periods: - - if period >= 1: - return '%d %s ago' % (period, singular if int(period) == 1 else plural) - - return default - - # #################################### BLUEPRINTS #################################### def register_blueprints(flask_app): """ Register the blueprints using the flask_app object """ + # Template filters and globals + flask_app.register_blueprint(template_utils.bp) # API routes flask_app.register_blueprint(api_handler.api_blueprint, url_prefix='/api') # Site routes diff --git a/nyaa/template_utils.py b/nyaa/template_utils.py new file mode 100644 index 0000000..ea89b6b --- /dev/null +++ b/nyaa/template_utils.py @@ -0,0 +1,107 @@ +import os.path +from datetime import datetime +from email.utils import formatdate + +import flask +from werkzeug.urls import url_encode + +from nyaa import app + +bp = flask.Blueprint('template-utils', __name__) +_static_cache = {} # For static_cachebuster + + +# ######################### TEMPLATE GLOBALS ######################### + +@bp.app_template_global() +def static_cachebuster(filename): + """ Adds a ?t= cachebuster to the given path, if the file exists. + Results are cached in memory and persist until app restart! """ + # Instead of timestamps, we could use commit hashes (we already load it in __init__) + # But that'd mean every static resource would get cache busted. This lets unchanged items + # stay in the cache. + + if app.debug: + # Do not bust cache on debug (helps debugging) + return flask.url_for('static', filename=filename) + + # Get file mtime if not already cached. + if filename not in _static_cache: + file_path = os.path.join(app.static_folder, filename) + file_mtime = None + if os.path.exists(file_path): + file_mtime = int(os.path.getmtime(file_path)) + + _static_cache[filename] = file_mtime + + return flask.url_for('static', filename=filename, t=_static_cache[filename]) + + +@bp.app_template_global() +def modify_query(**new_values): + args = flask.request.args.copy() + + for key, value in new_values.items(): + args[key] = value + + return '{}?{}'.format(flask.request.path, url_encode(args)) + + +@bp.app_template_global() +def filter_truthy(input_list): + """ Jinja2 can't into list comprehension so this is for + the search_results.html template """ + return [item for item in input_list if item] + + +# ######################### TEMPLATE FILTERS ######################### + +@bp.app_template_filter('utc_time') +def get_utc_timestamp(datetime_str): + """ Returns a UTC POSIX timestamp, as seconds """ + UTC_EPOCH = datetime.utcfromtimestamp(0) + return int((datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S') - UTC_EPOCH).total_seconds()) + + +@bp.app_template_filter('display_time') +def get_display_time(datetime_str): + return datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%S').strftime('%Y-%m-%d %H:%M') + + +@bp.app_template_filter('rfc822') +def _jinja2_filter_rfc822(date, fmt=None): + return formatdate(date.timestamp()) + + +@bp.app_template_filter('rfc822_es') +def _jinja2_filter_rfc822_es(datestr, fmt=None): + return formatdate(datetime.strptime(datestr, '%Y-%m-%dT%H:%M:%S').timestamp()) + + +@bp.app_template_filter() +def timesince(dt, default='just now'): + """ + Returns string representing "time since" e.g. + 3 minutes ago, 5 hours ago etc. + Date and time (UTC) are returned if older than 1 day. + """ + + now = datetime.utcnow() + diff = now - dt + + periods = ( + (diff.days, 'day', 'days'), + (diff.seconds / 3600, 'hour', 'hours'), + (diff.seconds / 60, 'minute', 'minutes'), + (diff.seconds, 'second', 'seconds'), + ) + + if diff.days >= 1: + return dt.strftime('%Y-%m-%d %H:%M UTC') + else: + for period, singular, plural in periods: + + if period >= 1: + return '%d %s ago' % (period, singular if int(period) == 1 else plural) + + return default diff --git a/tests/test_filters.py b/tests/test_template_utils.py similarity index 92% rename from tests/test_filters.py rename to tests/test_template_utils.py index 58f3e64..bc5cdcf 100644 --- a/tests/test_filters.py +++ b/tests/test_template_utils.py @@ -4,11 +4,12 @@ import datetime from email.utils import formatdate from tests import NyaaTestCase -from nyaa.routes import (_jinja2_filter_rfc822, _jinja2_filter_rfc822_es, get_utc_timestamp, - get_display_time, timesince, filter_truthy, category_name) +from nyaa.routes import category_name +from nyaa.template_utils import (_jinja2_filter_rfc822, _jinja2_filter_rfc822_es, get_utc_timestamp, + get_display_time, timesince, filter_truthy) -class TestFilters(NyaaTestCase): +class TestTemplateUtils(NyaaTestCase): # def setUp(self): # self.db, nyaa.app.config['DATABASE'] = tempfile.mkstemp()