From 971b1004297e446316eadde3131d120564f8871d Mon Sep 17 00:00:00 2001 From: girst Date: Mon, 15 Jun 2020 21:14:23 +0200 Subject: [PATCH] implement shadowing invidious routes --- README.md | 8 ++++++ app/invidious/__init__.py | 37 +++++++++++++++++++++++-- app/invidious/templates/channel.html.j2 | 28 +++++++++++++++++++ app/invidious/templates/search.html.j2 | 2 ++ app/youtube/templates/xmlfeed.html.j2 | 2 ++ config/config.ini | 4 +-- 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 app/invidious/templates/channel.html.j2 diff --git a/README.md b/README.md index af07f50..a265360 100644 --- a/README.md +++ b/README.md @@ -90,4 +90,12 @@ UYtF is trivially extensible using [Flask Blueprints]. Just drop a new blueprint in `app/` and register it in the `[frontend]modules` section of `config.ini`. The Blueprint inside a module is expected to be named `frontend`. +The default `youtube` blueprint provides unblockable but minimal versions of +the /channel and /playlist endpoints. A custom blueprint (or, as an example, +the `invidious` blueprint) can overwrite existing routes by being listed before +the one to be overridden in `config.ini`. +A blueprint may also delegate a request to another (lower precedence) blueprint +using `return fallback_route(*args, **kwargs)`. This can be useful if the +endpoint might not be as reliable (e.g. invidious-api being blocked) + [Flask Blueprints]: https://flask.palletsprojects.com/en/1.1.x/blueprints/ diff --git a/app/invidious/__init__.py b/app/invidious/__init__.py index facc86a..d4a6d57 100644 --- a/app/invidious/__init__.py +++ b/app/invidious/__init__.py @@ -1,7 +1,7 @@ import requests -from flask import Blueprint, render_template, request +from flask import Blueprint, render_template, request, flash -#from ..common.common import * +from ..common.common import fallback_route frontend = Blueprint('invidious', __name__, template_folder='templates', @@ -10,7 +10,7 @@ frontend = Blueprint('invidious', __name__, # TODO: minimal implementation (invidio.us hardcoded, videos only, no optional_params) @frontend.route('/search') -def feed(): +def search(): #token = getattr(current_user, 'token', 'guest') q = request.args.get('q') page = int(request.args.get('page', 1)) @@ -45,3 +45,34 @@ def feed(): # XXX: should transform invidious-json to our naming scheme return render_template('search.html.j2', rows=r.json(), query=q, page=page, optional_params=optional_params) + +@frontend.route('/channel/') +@frontend.route('/user/') +def channel(channel_id): + page = int(request.args.get('page', 1)) + sort_by = request.args.get('sort_by', 'newest') + provider = request.args.get('provider', 'invidio.us:443') + + r = requests.get(f"https://invidio.us/api/v1/channels/{channel_id}/videos", { + 'sort_by': sort_by, + 'page': page, + }, allow_redirects=False) + if not r.ok or r.status_code != 200: + flash("invidious did not find the channel or is blocked. showing minimal response", "error") + return fallback_route(channel_id) + + return render_template('channel.html.j2', rows=r.json(), sort_by=sort_by, page=page) + +@frontend.route('/playlist/') +def playlist(playlist_id): + page = int(request.args.get('page', 1)) + provider = request.args.get('provider', 'invidio.us:443') + + r = requests.get(f"https://invidio.us/api/v1/playlists/{playlist_id}", { + 'page': page, + }, allow_redirects=False) + if not r.ok or r.status_code != 200: + flash("invidious did not find the channel or is blocked. showing minimal response", "error") + return fallback_route(playlist_id) + + return render_template('channel.html.j2', rows=r.json().get('videos',[]), page=page) diff --git a/app/invidious/templates/channel.html.j2 b/app/invidious/templates/channel.html.j2 new file mode 100644 index 0000000..ea25db9 --- /dev/null +++ b/app/invidious/templates/channel.html.j2 @@ -0,0 +1,28 @@ + +{{ title | e }} + + + +{% import 'macros.imp.j2' as macros with context %} + +{{ macros.headerbar(search_query=query) }} + +{% include 'messages.inc.j2' %} + +
+

{{ title | e }}

+{% for row in rows %}{# TODO: switch-case for result type (video/playlist/channel); publishedText->published|format + #}{% call macros.card(row.videoId, row.title, row.publishedText) %} + {{ macros.infobar_subscriptions(row.videoId, row.authorId, row.author) }} + {% endcall %}{# +#}{% else %} + no more results. +{% endfor %} +
+ +{% if page > 1 %} +previous | +{% endif %} +next + +
diff --git a/app/invidious/templates/search.html.j2 b/app/invidious/templates/search.html.j2 index 9c7dab6..3a1be50 100644 --- a/app/invidious/templates/search.html.j2 +++ b/app/invidious/templates/search.html.j2 @@ -7,6 +7,8 @@ {{ macros.headerbar(search_query=query) }} +{% include 'messages.inc.j2' %} +

{{ query | e }}

{% for row in rows %}{# TODO: switch-case for result type (video/playlist/channel); publishedText->published|format diff --git a/app/youtube/templates/xmlfeed.html.j2 b/app/youtube/templates/xmlfeed.html.j2 index 1d98bbc..f8f89a7 100644 --- a/app/youtube/templates/xmlfeed.html.j2 +++ b/app/youtube/templates/xmlfeed.html.j2 @@ -7,6 +7,8 @@ {{ macros.headerbar() }} +{% include 'messages.inc.j2' %} +

{{ title }}

{% for row in rows %}{# diff --git a/config/config.ini b/config/config.ini index b916fa3..8016ea0 100644 --- a/config/config.ini +++ b/config/config.ini @@ -7,8 +7,8 @@ database = /opt/yt/subscriptions.sqlite secret_key = # a message to show anonymous users when they navigate to /. note that you may not put newlines in it, as the perl ini parser can't handle them. welcome_message = This is the test instance of unnamed-youtube-frontend. Contributors welcome! -# comma seperated list of blueprints to load. you may put your own blueprints in a subdirectory of app/ and list them here. -modules = youtube,reddit,invidious +# comma seperated list of blueprints to load. you may put your own blueprints in a subdirectory of app/ and list them here. modules mentioned first will shadow routes of those mentioned last. +modules = invidious,youtube,reddit [websub] # how long to ask websub-provider for updates (cargo-culted from invidious; 5 * 24 * 60 * 60 = 5days): -- 2.39.3