]>
git.gir.st - subscriptionfeed.git/blob - app/dangerous/__init__.py
1 # this is an alternative to proxying through invidious. the search endpoint has only been (loosely) tested by
2 #17:50 < perflyst[m]> appears to be working
3 #, so i hopeā¢ this works. if not, that's why it's in the 'dangerous' blueprint
5 from flask
import Blueprint
, render_template
, request
, flash
, g
, url_for
, redirect
6 from werkzeug
. exceptions
import NotFound
8 from .. common
. common
import *
9 from .. common
. innertube
import *
11 from . protobuf
import make_sp
, make_channel_params
, make_playlist_params
13 frontend
= Blueprint ( 'dangerous' , __name__
,
14 template_folder
= 'templates' ,
15 static_folder
= 'static' ,
16 static_url_path
= '/static/ys' )
18 @frontend . route ( '/results' )
19 @frontend . route ( '/search' )
21 #token = getattr(current_user, 'token', 'guest')
22 q
= request
. args
. get ( 'q' ) or request
. args
. get ( 'search_query' )
23 page
= int ( request
. args
. get ( 'page' ) or 1 )
26 k
: v
for k
, v
in request
. args
. items ()
27 if k
in [ 'sort' , 'date' , 'type' , 'len' ]
28 }, extras
=[ 'dont_fix_spelling' ]* 0 ) # extras disabled
31 yt_results
= fetch_searchresults ( q
, page
, sp
)
33 results
, extras
= prepare_searchresults ( yt_results
)
40 return render_template ( 'search.html.j2' , rows
= results
, query
= q
, page
= page
)
42 # TODO: channels, playlists:
43 # https://github.com/iv-org/invidious/blob/452d1e8307d6344dd51c5437ccd032a566291c34/src/invidious/channels.cr#L399
45 @frontend . route ( '/channel/<channel_id>/' )
46 @frontend . route ( '/channel/<channel_id>/<subpage>' )
47 def channel ( channel_id
, subpage
= "videos" ):
48 if subpage
== "videos" :
49 page
= int ( request
. args
. get ( 'page' , 1 ))
50 sort_by
= request
. args
. get ( 'sort' ) or "newest" # XXX: using .get(x) or y instead of .get(x,y) works around "?sort=", which breaks inside protobuf.py later
52 elif subpage
== "playlists" :
53 page
= None # TODO: cursor
54 sort_by
= request
. args
. get ( 'sort' , "modified" )
56 elif subpage
== "search" :
57 query
= request
. args
. get ( 'q' )
58 page
= int ( request
. args
. get ( 'page' , 1 ))
60 else : # we don't support /home, /about, ..., so redirect to /videos.
61 return redirect ( url_for ( '.channel' , channel_id
= channel_id
))
63 # best effort; if it fails, it fails in the redirect.
64 if not re
. match ( r
"(UC[A-Za-z0-9_-] {22} )" , channel_id
):
65 return redirect ( url_for ( '.channel_redirect' , user
= channel_id
))
67 result
= fetch_ajax ( make_channel_params ( channel_id
, subpage
, page
, sort_by
, query
))
69 # Note: as of 2020-08-15, using the v1 format sometimes returns an error. if that's the case, try v3.
70 alert
= listget ( listget ( result
, 1 ,{}). get ( 'response' ,{}). get ( 'alerts' ,[]), 0 ,{}). get ( 'alertRenderer' ,{})
71 if alert
. get ( 'type' , '' ) == "ERROR" : # alert['text']['simpleText'] == "Unknown error."
72 result
= fetch_ajax ( make_channel_params ( channel_id
, subpage
, page
, sort_by
, query
, v3
= True ))
74 title
, descr
, thumb
, rows
, more
= prepare_channel ( result
, channel_id
)
75 # TODO: add is_pinned/is_hidden
77 if title
is None : # if both v1 and v3 failed, fall back to xmlfeed:
78 flash ( "unable to fetch results from ajax; displaying fallback results (15 newest)" , "error" )
79 return fallback_route ( channel_id
, subpage
)
81 return render_template ( 'channel.html.j2' ,
85 channel_id
= channel_id
,
91 @frontend . route ( '/user/<user>' )
92 @frontend . route ( '/user/<user>/<subpage>' )
93 @frontend . route ( '/c/<user>' )
94 @frontend . route ( '/c/<user>/<subpage>' )
95 def channel_redirect ( user
, subpage
= None ):
97 The browse_ajax 'API' needs the UCID. We can get that from the RSS feeds.
99 xmlfeed
= fetch_xml ( "user" , user
)
101 raise NotFound ( "channel appears to not exist" )
102 _
, _
, _
, channel_id
, _
= parse_xml ( xmlfeed
)
104 url_for ( '.channel' , channel_id
= channel_id
, subpage
= subpage
), 308
107 @frontend . route ( '/playlist' )
109 #TODO: if anything goes wrong, fall back to xmlfeed
110 playlist_id
= request
. args
. get ( 'list' )
112 return "bad list id" , 400 # todo
113 page
= int ( request
. args
. get ( 'page' , 1 ))
115 xmlfeed
= fetch_xml ( "playlist_id" , playlist_id
)
117 return "not found or something" , 404 # XXX
118 title
, author
, _
, channel_id
, _
= parse_xml ( xmlfeed
)
120 offset
= ( page
- 1 )* 100 # each call returns 100 items
121 result
= fetch_ajax ( make_playlist_params ( playlist_id
, offset
))
123 if not 'continuationContents' in result
[ 1 ][ 'response' ]: # XXX: this needs cleanup!
124 # code:"CONDITION_NOT_MET", debugInfo:"list type not viewable"
125 # on playlist https://www.youtube.com/watch?v=6y_NJg-xoeE&list=RDgohHV9ryp-A&index=24 (not openable on yt.com)
126 error
= result
[ 1 ][ 'response' ][ 'responseContext' ][ 'errors' ][ 'error' ][ 0 ]
127 flash ( f
"{error['code']}: {error['debugInfo'] or error['externalErrorMessage']}" , 'error' )
128 return fallback_route ()
129 rows
, more
= prepare_playlist ( result
)
131 return render_template ( 'playlist.html.j2' ,
134 channel_id
= channel_id
,
139 @frontend . before_app_request
141 if not 'header_items' in g
:
143 g
. header_items
. append ({
145 'url' : url_for ( 'dangerous.search' ),
146 'parent' : frontend
. name
,