# this is an alternative to proxying through invidious. the search endpoint has only been (loosely) tested by #17:50 < perflyst[m]> appears to be working #, so i hope™ this works. if not, that's why it's in the 'dangerous' blueprint import requests from flask import Blueprint, render_template, request, flash, g, url_for, redirect from ..common.common import * from ..common.innertube import * from .lib import * from .protobuf import make_sp, make_channel_params, make_playlist_params frontend = Blueprint('dangerous', __name__, template_folder='templates', static_folder='static', static_url_path='/static/ys') @frontend.route('/search') def search(): #token = getattr(current_user, 'token', 'guest') q = request.args.get('q') page = int(request.args.get('page', 1)) sp = make_sp(**{ k:v for k,v in request.args.items() if k in ['sort','date','type','len'] }, extras=['dont_fix_spelling']*0) # extras disabled if q: yt_results = fetch_searchresults(q, page, sp) results, extras = prepare_searchresults(yt_results) for extra in extras: flash(extra, 'info') else: results = None return render_template('search.html.j2', rows=results, query=q, page=page) # TODO: channels, playlists: # https://github.com/iv-org/invidious/blob/452d1e8307d6344dd51c5437ccd032a566291c34/src/invidious/channels.cr#L399 @frontend.route('/channel//') @frontend.route('/channel//') def channel(channel_id, subpage="videos"): if subpage == "videos": page = int(request.args.get('page', 1)) sort_by = request.args.get('sort', "newest") query = None elif subpage == "playlists": page = None # TODO: cursor sort_by = request.args.get('sort', "modified") query = None elif subpage == "search": query = request.args.get('q') page = int(request.args.get('page', 1)) sort_by = None else: # we don't support /home, /about, ..., so redirect to /videos. return redirect(url_for('.channel', channel_id=channel_id)) try: result = fetch_ajax(make_channel_params(channel_id, subpage, page, sort_by, query)) except RuntimeError: try: result = fetch_ajax(make_channel_params(channel_id, subpage, page, sort_by, query, v2=True)) except RuntimeError: # XXX: this should never happen™ -- log to somewhere flash("unable to fetch results from 'classic' ajax; displaying fallback results (15 newest)", "error") return fallback_route(channel_id, subpage) title, descr, thumb, rows, more = prepare_channel(result, channel_id) # TODO: add is_pinned/is_hidden return render_template('channel.html.j2', title=title, subpage=subpage, rows=rows, channel_id=channel_id, channel_img=thumb, channel_desc=descr, page=page, has_more=more) @frontend.route('/user/') @frontend.route('/user//') @frontend.route('/c/') @frontend.route('/c//') def channel_redirect(user, subpage=None): """ The browse_ajax 'API' needs the UCID. We can get that from the RSS feeds. """ xmlfeed = fetch_xml("user", user) _, _, _, channel_id, _ = parse_xml(xmlfeed) return redirect( url_for('.channel', channel_id=channel_id, subpage=subpage), 308 ) @frontend.route('/playlist') def playlist(): #TODO: if anything goes wrong, fall back to xmlfeed playlist_id = request.args.get('list') if not playlist_id: return "bad list id", 400 # todo page = int(request.args.get('page', 1)) xmlfeed = fetch_xml("playlist_id", playlist_id) if not xmlfeed: return "not found or something", 404 # XXX title, author, _, channel_id, _ = parse_xml(xmlfeed) offset = (page-1)*100 # each call returns 100 items result = fetch_ajax(make_playlist_params(playlist_id, offset)) rows, more = prepare_playlist(result) return render_template('playlist.html.j2', title=title, author=author, channel_id=channel_id, rows=rows, page=page, has_more=more) @frontend.before_app_request def inject_button(): if not 'header_items' in g: g.header_items = [] g.header_items.append({ 'name': 'search', 'url': url_for('dangerous.search'), 'parent': frontend.name, 'priority': 15, })