2021-01-19 11:13:21 +00:00
from quart import Quart , request , render_template , redirect
2021-01-21 10:22:09 +00:00
from datetime import datetime
2021-07-29 15:06:28 +00:00
from dateutil import parser as dateutil
2021-01-23 14:20:10 +00:00
from math import ceil
2021-01-17 15:04:24 +00:00
import peertube
2021-03-24 17:43:50 +00:00
import html2text
2021-07-28 23:43:30 +00:00
import sys
2021-03-24 17:43:50 +00:00
h2t = html2text . HTML2Text ( )
h2t . ignore_links = True
2021-01-17 15:04:24 +00:00
# Wrapper, only containing information that's important for us, and in some cases provides simplified ways to get information
class VideoWrapper :
def __init__ ( self , a , quality ) :
self . name = a [ " name " ]
2021-07-28 23:43:34 +00:00
self . uuid = a [ " uuid " ]
2021-01-17 15:04:24 +00:00
self . channel = a [ " channel " ]
self . description = a [ " description " ]
self . thumbnailPath = a [ " thumbnailPath " ]
self . category = a [ " category " ]
self . licence = a [ " licence " ]
self . language = a [ " language " ]
2021-07-28 23:43:34 +00:00
self . captions = a [ " captions " ]
2021-01-17 15:04:24 +00:00
self . privacy = a [ " privacy " ]
self . tags = a [ " tags " ]
self . views = a [ " views " ]
self . likes = a [ " likes " ]
self . dislikes = a [ " dislikes " ]
2021-01-19 14:24:28 +00:00
self . embedPath = a [ " embedPath " ]
2021-01-21 09:30:26 +00:00
self . commentsEnabled = a [ " commentsEnabled " ]
2021-01-19 14:24:28 +00:00
2021-01-17 15:04:24 +00:00
self . resolutions = [ ]
self . video = None
2021-01-19 14:24:28 +00:00
self . files = a [ " files " ]
if len ( self . files ) == 0 :
self . files = ( ( a [ " streamingPlaylists " ] ) [ 0 ] ) [ " files " ]
2021-07-18 09:13:45 +00:00
self . default_res = None
2021-01-19 14:24:28 +00:00
for entry in self . files :
2021-01-17 15:04:24 +00:00
resolution = ( entry [ " resolution " ] ) [ " id " ]
self . resolutions . append ( entry [ " resolution " ] )
2021-07-18 09:13:45 +00:00
# chose the default quality
if resolution != 0 and quality == None :
if self . default_res == None :
self . default_res = resolution
self . video = entry [ " fileUrl " ]
elif abs ( 720 - resolution ) < abs ( 720 - self . default_res ) :
self . default_res = resolution
self . video = entry [ " fileUrl " ]
2021-01-17 15:04:24 +00:00
if str ( resolution ) == str ( quality ) :
self . video = entry [ " fileUrl " ]
2021-07-18 09:13:45 +00:00
if quality == None :
self . quality = self . default_res
else :
self . quality = quality
2021-01-17 15:04:24 +00:00
self . no_quality_selected = not self . video
2021-01-21 22:26:31 +00:00
# Helper Class for using caches
class Cache :
2021-07-29 15:06:27 +00:00
def __init__ ( self , criteria = lambda diff : diff . days > 0 ) :
2021-01-21 22:26:31 +00:00
self . dict = { }
2021-07-29 15:06:27 +00:00
self . criteria = criteria
2021-01-21 22:26:31 +00:00
def get ( self , arg , func ) :
if arg in self . dict :
last_time_updated = ( self . dict [ arg ] ) [ 1 ]
time_diff = datetime . now ( ) - last_time_updated
2021-07-29 15:06:27 +00:00
if self . criteria ( time_diff ) :
2021-01-21 22:26:31 +00:00
self . dict [ arg ] = [
func ( arg ) ,
datetime . now ( )
]
else :
self . dict [ arg ] = [
func ( arg ) ,
datetime . now ( )
]
return ( self . dict [ arg ] ) [ 0 ]
cached_instance_names = Cache ( )
cached_account_infos = Cache ( )
cached_video_channel_infos = Cache ( )
2021-01-21 10:14:20 +00:00
2021-07-29 16:49:17 +00:00
cached_subscriptions = Cache ( criteria = lambda diff : diff . total_seconds ( ) > 60 )
2021-07-29 15:06:28 +00:00
cached_account_videos = Cache ( criteria = lambda diff : diff . total_seconds ( ) > 1800 )
2021-07-29 16:49:17 +00:00
cached_channel_videos = Cache ( criteria = lambda diff : diff . total_seconds ( ) > 1800 )
2021-07-29 15:06:28 +00:00
2021-01-21 10:14:20 +00:00
# cache the instance names so we don't have to send a request to the domain every time someone
# loads any site
def get_instance_name ( domain ) :
2021-01-21 22:26:31 +00:00
return cached_instance_names . get ( domain , peertube . get_instance_name )
2021-01-21 10:22:09 +00:00
2021-01-21 22:26:31 +00:00
# simple wrapper that is used inside the cached_account_infos
def get_account ( info ) :
info = info . split ( " @ " )
return peertube . account ( info [ 1 ] , info [ 0 ] )
2021-01-21 10:22:09 +00:00
2021-01-21 22:26:31 +00:00
def get_account_info ( name ) :
return cached_account_infos . get ( name , get_account )
2021-01-21 10:22:09 +00:00
2021-01-21 22:26:31 +00:00
# simple wrapper that is used inside the cached_video_channel_infos
def get_video_channel ( info ) :
info = info . split ( " @ " )
return peertube . video_channel ( info [ 1 ] , info [ 0 ] )
def get_video_channel_info ( name ) :
return cached_video_channel_infos . get ( name , get_video_channel )
2021-07-29 16:49:17 +00:00
# Get latest remote videos from channel name
def get_latest_channel_videos ( name ) :
return cached_channel_videos . get ( name , latest_channel_videos )
# Refresh latest remote videos from channel name
def latest_channel_videos ( name ) :
print ( " [CACHE] Refreshing channel videos for %s " % name )
( name , domain ) = name . split ( ' @ ' )
return peertube . video_channel_videos ( domain , name , 0 )
# Get latest remote videos from account name
2021-07-29 15:06:28 +00:00
def get_latest_account_videos ( name ) :
return cached_account_videos . get ( name , latest_account_videos )
2021-07-29 16:49:17 +00:00
# Refresh latest remote videos from account name
2021-07-29 15:06:28 +00:00
def latest_account_videos ( name ) :
2021-07-29 16:49:17 +00:00
print ( " [CACHE] Refreshing account videos for %s " % name )
2021-07-29 15:06:28 +00:00
( name , domain ) = name . split ( ' @ ' )
return peertube . account_videos ( domain , name , 0 )
# Get local accounts subscriptions, as specified in accounts.list
def get_subscriptions_accounts ( ) :
2021-07-29 16:49:17 +00:00
return cached_subscriptions . get ( " accounts " , load_subscriptions_accounts )
2021-07-29 15:06:28 +00:00
# Refresh local accounts subscriptions
def load_subscriptions_accounts ( _ ) :
2021-07-29 16:49:18 +00:00
return load_subscriptions ( " accounts " )
2021-07-29 15:06:28 +00:00
2021-07-29 16:49:17 +00:00
# Get the latest videos from local accounts subscriptions, ordered by most recent; only return `limit` number of videos
2021-07-29 15:06:28 +00:00
def get_subscriptions_accounts_videos ( limit = 12 ) :
latest = [ ]
for sub in get_subscriptions_accounts ( ) :
account_latest = get_latest_account_videos ( sub ) [ " data " ]
latest . extend ( account_latest )
latest . sort ( key = lambda vid : dateutil . isoparse ( vid [ " createdAt " ] ) , reverse = True )
return latest [ 0 : limit ]
2021-01-19 11:13:21 +00:00
2021-07-29 16:49:17 +00:00
# Get local channels subscriptions, as specified in channel.list
def get_subscriptions_channels ( ) :
return cached_subscriptions . get ( " channels " , load_subscriptions_channels )
# Refresh local channels subscriptions
def load_subscriptions_channels ( _ ) :
2021-07-29 16:49:18 +00:00
return load_subscriptions ( " channels " )
# Load subscriptions from a file called `kind`.list (60s cache)
def load_subscriptions ( kind ) :
print ( " [CACHE] Refreshing subscriptions %s from %s .list " % ( kind , kind ) )
2021-07-29 16:49:17 +00:00
try :
2021-07-29 16:49:18 +00:00
with open ( kind + ' .list ' , ' r ' ) as f :
subscriptions = map ( find_subscription , f . read ( ) . splitlines ( ) )
2021-07-29 16:49:17 +00:00
except Exception as e :
print ( " No `channels.list` file to load for local subscriptions " )
subscriptions = [ ]
2021-07-29 16:49:18 +00:00
# Remove comment entries and empty lines
2021-07-29 17:04:55 +00:00
return list ( filter ( lambda entry : entry != ' ' , subscriptions ) )
2021-07-29 16:49:18 +00:00
# Builds a unified id@server from one of those syntaxes, additionally stripping extra whitespace and ignoring `#` as comments:
# - id@server
# - @id@server
# - http(s)://server/c/id
# - http(s)://server/a/id
def find_subscription ( request ) :
identifier = request
identifier = identifier . split ( ' # ' ) [ 0 ] . strip ( )
# Comment line is returned as empty string
if identifier == ' ' : return ' '
if identifier . startswith ( ' @ ' ) :
# Strip @ from identifier
return identifier [ 1 : ]
2021-07-31 10:05:28 +00:00
2021-07-29 16:49:18 +00:00
if identifier . startswith ( ' http ' ) :
identifier = identifier [ 4 : ]
# HTTPS?
if identifier . startswith ( ' s ' ) : identifier = identifier [ 1 : ]
# Remove ://
identifier = identifier [ 3 : ]
parts = identifier . split ( ' / ' )
domain = parts [ 0 ]
if parts [ 1 ] == ' a ' or parts [ 1 ] == ' c ' :
# Account or channel found, take the next part
return parts [ 2 ] + ' @ ' + domain
else :
# Just check there's an @ in there and it should be fine
if ' @ ' in identifier :
return identifier
2021-07-31 10:05:28 +00:00
2021-07-29 16:49:18 +00:00
# No match was found, we don't understand this URL
print ( " [WARN] Identifier not understood from local subscriptions: \n %s " % request )
return ' '
2021-07-29 16:49:17 +00:00
# Get the latest videos from local channels subscriptions, ordered by most recent; only return `limit` number of videos
def get_subscriptions_channels_videos ( limit = 12 ) :
latest = [ ]
for sub in get_subscriptions_channels ( ) :
2021-07-31 10:05:28 +00:00
result = get_latest_channel_videos ( sub )
if result [ " status " ] == 200 :
channel_latest = get_latest_channel_videos ( sub ) [ " data " ]
latest . extend ( channel_latest )
else :
print ( " [WARN] Unable to get content from " + sub )
2021-07-29 16:49:17 +00:00
latest . sort ( key = lambda vid : dateutil . isoparse ( vid [ " createdAt " ] ) , reverse = True )
return latest [ 0 : limit ]
# Get the latest videos from local channels and accounts subscriptions combined, ordered by most recent; only return `limit` number of videos; NOTE: duplicates are not handled, why would you add both an account and the corresponding channel?
def get_subscriptions_videos ( limit = 12 ) :
latest = get_subscriptions_channels_videos ( limit = limit )
latest . extend ( get_subscriptions_accounts_videos ( limit = limit ) )
# TODO: maybe refactor so we don't have to reorder twice? Or maybe the get_ functions can take a ordered=True argument? In this case here, it would be false, because we sort after
latest . sort ( key = lambda vid : dateutil . isoparse ( vid [ " createdAt " ] ) , reverse = True )
return latest [ 0 : limit ]
# Get the info about local accounts subscriptions
def get_subscriptions_accounts_info ( ) :
return map ( lambda sub : get_account_info ( sub ) , get_subscriptions_accounts ( ) )
# Get the info about local channels subscriptions
def get_subscriptions_channels_info ( ) :
return map ( lambda sub : get_video_channel_info ( sub ) , get_subscriptions_channels ( ) )
# Get the info about local subscriptions for accounts and channels, as a tuple of lists
def get_subscriptions_info ( ) :
list = [ ]
list . extend ( get_subscriptions_accounts_info ( ) )
list . extend ( get_subscriptions_channels_info ( ) )
return list
2021-01-21 10:14:20 +00:00
app = Quart ( __name__ )
2021-01-19 11:13:21 +00:00
@app.route ( " / " )
2021-01-17 15:04:24 +00:00
async def main ( ) :
2021-07-29 16:49:17 +00:00
videos = get_subscriptions_videos ( limit = 12 )
# Inside subscriptions variable, you may find either an account info structure, or a channel info structure. Channels may be recognized due to `ownerAccount` property.
subscriptions = get_subscriptions_info ( )
2021-01-22 04:08:04 +00:00
return await render_template (
" index.html " ,
2021-07-29 16:49:17 +00:00
videos = videos ,
subscriptions = subscriptions ,
2021-01-22 04:08:04 +00:00
)
@app.route ( " /search " , methods = [ " POST " ] )
async def simpleer_search_redirect ( ) :
query = ( await request . form ) [ " query " ]
return redirect ( " /search/ " + query )
2021-07-29 00:38:28 +00:00
@app.route ( " /search " , methods = [ " GET " ] )
async def simpleer_search_get_redirect ( ) :
query = request . args . get ( " query " )
return redirect ( " /search/ " + query )
2021-01-22 04:08:04 +00:00
2021-01-23 14:20:10 +00:00
@app.route ( " /search/<string:query> " , defaults = { " page " : 1 } )
@app.route ( " /search/<string:query>/<int:page> " )
async def simpleer_search ( query , page ) :
results = peertube . sepia_search ( query , ( page - 1 ) * 10 )
2021-01-22 04:08:04 +00:00
return await render_template (
" simpleer_search_results.html " ,
2021-01-23 14:20:10 +00:00
results = results ,
query = query ,
# details for pagination
page = page ,
pages_total = ceil ( results [ " total " ] / 10 ) ,
2021-01-22 04:08:04 +00:00
)
2021-01-19 11:13:21 +00:00
2021-01-17 15:04:24 +00:00
2021-01-19 11:13:21 +00:00
@app.route ( " /<string:domain>/ " )
2021-01-22 03:17:02 +00:00
async def instance ( domain ) :
2021-07-28 23:43:33 +00:00
# favicon.ico is not a domain name
2021-07-28 23:43:35 +00:00
if domain == " favicon.ico " : return await favicon ( )
2021-01-22 03:17:02 +00:00
return redirect ( " / " + domain + " /videos/trending " )
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/videos/local " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/videos/local/<int:page> " )
async def instance_videos_local ( domain , page ) :
vids = peertube . get_videos_local ( domain , ( page - 1 ) * 10 )
2021-01-19 11:13:21 +00:00
return await render_template (
2021-01-22 03:17:02 +00:00
" instance/local.html " ,
2021-01-19 11:13:21 +00:00
domain = domain ,
2021-01-21 10:14:20 +00:00
instance_name = get_instance_name ( domain ) ,
2021-01-22 03:17:02 +00:00
2021-01-23 14:20:10 +00:00
videos = vids ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /videos/local/ " ,
pages_total = ceil ( vids [ " total " ] / 10 ) ,
2021-01-22 03:17:02 +00:00
)
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/videos/trending " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/videos/trending/<int:page> " )
async def instance_videos_trending ( domain , page ) :
vids = peertube . get_videos_trending ( domain , ( page - 1 ) * 10 )
2021-01-22 03:17:02 +00:00
return await render_template (
" instance/trending.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
2021-01-23 14:20:10 +00:00
videos = vids ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /videos/trending/ " ,
pages_total = ceil ( vids [ " total " ] / 10 ) ,
2021-01-22 03:17:02 +00:00
)
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/videos/most-liked " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/videos/most-liked/<int:page> " )
async def instance_videos_most_liked ( domain , page ) :
vids = peertube . get_videos_most_liked ( domain , ( page - 1 ) * 10 )
2021-01-22 03:17:02 +00:00
return await render_template (
" instance/most-liked.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
2021-01-23 14:20:10 +00:00
videos = vids ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /videos/most-liked/ " ,
pages_total = ceil ( vids [ " total " ] / 10 ) ,
2021-01-22 03:17:02 +00:00
)
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/videos/recently-added " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/videos/recently-added/<int:page> " )
async def instance_videos_recently_added ( domain , page ) :
vids = peertube . get_videos_recently_added ( domain , ( page - 1 ) * 10 )
2021-01-22 03:17:02 +00:00
return await render_template (
" instance/recently-added.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
2021-01-23 14:20:10 +00:00
videos = vids ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /videos/recently-added/ " ,
pages_total = ceil ( vids [ " total " ] / 10 ) ,
2021-01-19 11:13:21 +00:00
)
2021-01-17 15:04:24 +00:00
2021-01-20 16:10:23 +00:00
2021-01-23 14:20:10 +00:00
2021-01-20 16:10:23 +00:00
@app.route ( " /<string:domain>/search " , methods = [ " POST " ] )
2021-01-19 11:13:21 +00:00
async def search_redirect ( domain ) :
query = ( await request . form ) [ " query " ]
return redirect ( " / " + domain + " /search/ " + query )
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/search/<string:term> " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/search/<string:term>/<int:page> " )
async def search ( domain , term , page ) :
results = peertube . search ( domain , term , ( page - 1 ) * 10 )
2021-01-19 11:13:21 +00:00
return await render_template (
2021-01-20 16:10:23 +00:00
" search_results.html " ,
domain = domain ,
2021-01-21 10:14:20 +00:00
instance_name = get_instance_name ( domain ) ,
2021-01-20 16:10:23 +00:00
results = results ,
search_term = term ,
2021-01-23 14:20:10 +00:00
# details for pagination
page = page ,
pagination_url = " / " + domain + " /search/ " + term + " / " ,
pages_total = ( results [ " total " ] / 10 )
2021-01-19 11:13:21 +00:00
)
2021-01-17 15:04:24 +00:00
2021-01-21 11:20:39 +00:00
@app.route ( " /<string:domain>/videos/watch/<string:id>/ " )
2021-01-17 15:04:24 +00:00
async def video ( domain , id ) :
data = peertube . video ( domain , id )
2021-07-28 23:43:34 +00:00
data [ " captions " ] = peertube . video_captions ( domain , id )
2021-01-17 15:04:24 +00:00
quality = request . args . get ( " quality " )
2021-01-19 14:24:28 +00:00
embed = request . args . get ( " embed " )
2021-01-17 15:04:24 +00:00
vid = VideoWrapper ( data , quality )
2021-07-18 09:13:45 +00:00
quality = int ( vid . quality )
2021-01-17 15:04:24 +00:00
2021-01-21 09:30:26 +00:00
# only make a request for the comments if commentsEnabled
comments = " "
if data [ " commentsEnabled " ] :
comments = peertube . get_comments ( domain , id )
2021-03-24 17:43:50 +00:00
# Strip the HTML from the comments and convert them to plain text
new_comments = { " total " : comments [ " total " ] , " data " : [ ] }
for comment in comments [ " data " ] :
text = h2t . handle ( comment [ " text " ] ) . strip ( ) . strip ( " \n " )
comment [ " text " ] = text
new_comments [ " data " ] . append ( comment )
comments = new_comments
2021-01-20 16:10:23 +00:00
return await render_template (
" video.html " ,
domain = domain ,
2021-01-21 10:14:20 +00:00
instance_name = get_instance_name ( domain ) ,
2021-01-20 16:10:23 +00:00
video = vid ,
2021-01-21 09:30:26 +00:00
comments = comments ,
2021-01-20 16:10:23 +00:00
quality = quality ,
embed = embed ,
)
2021-01-19 11:13:21 +00:00
2021-01-21 22:26:31 +00:00
def build_channel_or_account_name ( domain , name ) :
if ' @ ' in name :
return name
return name + " @ " + domain
# --- Accounts ---
@app.route ( " /<string:domain>/accounts/<string:name> " )
2021-01-23 14:20:10 +00:00
async def accounts_redirect ( domain , name ) :
2021-01-21 22:26:31 +00:00
return redirect ( " / " + domain + " /accounts/ " + name + " /video-channels " )
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/accounts/<string:name>/video-channels " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/accounts/<string:name>/video-channels/<int:page> " )
async def account__video_channels ( domain , name , page ) :
video_channels = peertube . account_video_channels ( domain , name , ( page - 1 ) * 10 )
2021-01-21 22:26:31 +00:00
return await render_template (
" accounts/video_channels.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
account = get_account_info ( build_channel_or_account_name ( domain , name ) ) ,
2021-01-23 14:20:10 +00:00
video_channels = video_channels ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /accounts/ " + name + " /video-channels/ " ,
pages_total = ceil ( video_channels [ " total " ] / 10 )
2021-01-21 22:26:31 +00:00
)
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/accounts/<string:name>/videos " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/accounts/<string:name>/videos/<int:page> " )
async def account__videos ( domain , name , page ) :
vids = peertube . account_videos ( domain , name , ( page - 1 ) * 10 )
2021-01-21 22:26:31 +00:00
return await render_template (
" accounts/videos.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
account = get_account_info ( build_channel_or_account_name ( domain , name ) ) ,
2021-01-23 14:20:10 +00:00
videos = vids ,
# details for pagination
page = page ,
pagination_url = " / " + domain + " /accounts/ " + name + " /videos/ " ,
pages_total = ceil ( vids [ " total " ] / 10 )
2021-01-21 22:26:31 +00:00
)
@app.route ( " /<string:domain>/accounts/<string:name>/about " )
async def account__about ( domain , name ) :
return await render_template (
" accounts/about.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
account = get_account_info ( build_channel_or_account_name ( domain , name ) ) ,
about = peertube . account ( domain , name )
)
# --- Video-Channels ---
@app.route ( " /<string:domain>/video-channels/<string:name> " )
2021-01-23 14:20:10 +00:00
async def video_channels_redirect ( domain , name ) :
2021-01-21 22:26:31 +00:00
return redirect ( " / " + domain + " /video-channels/ " + name + " /videos " )
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/video-channels/<string:name>/videos " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/video-channels/<string:name>/videos/<int:page> " )
async def video_channels__videos ( domain , name , page ) :
vids = peertube . video_channel_videos ( domain , name , ( page - 1 ) * 10 )
2021-01-21 22:26:31 +00:00
return await render_template (
" video_channels/videos.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
video_channel = get_video_channel_info ( build_channel_or_account_name ( domain , name ) ) ,
2021-01-23 14:20:10 +00:00
page = page ,
pagination_url = " / " + domain + " /video-channels/ " + name + " /videos/ " ,
pages_total = ceil ( vids [ " total " ] / 10 ) ,
videos = vids ,
2021-01-21 22:26:31 +00:00
)
2021-01-23 14:20:10 +00:00
@app.route ( " /<string:domain>/video-channels/<string:name>/video-playlists " , defaults = { " page " : 1 } )
@app.route ( " /<string:domain>/video-channels/<string:name>/video-playlists/<int:page> " )
async def video_channels__video_playlists ( domain , name , page ) :
video_playlists = peertube . video_channel_video_playlists ( domain , name , ( page - 1 ) * 10 )
2021-01-21 22:26:31 +00:00
return await render_template (
" video_channels/video_playlists.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
video_channel = get_video_channel_info ( build_channel_or_account_name ( domain , name ) ) ,
2021-01-23 14:20:10 +00:00
video_playlists = video_playlists ,
page = page ,
pagination_url = " / " + domain + " /video-channels/ " + name + " /video-playlists/ " ,
pages_total = ceil ( video_playlists [ " total " ] / 10 )
2021-01-21 22:26:31 +00:00
)
@app.route ( " /<string:domain>/video-channels/<string:name>/about " )
async def video_channels__about ( domain , name ) :
return await render_template (
" video_channels/about.html " ,
domain = domain ,
instance_name = get_instance_name ( domain ) ,
name = name ,
video_channel = get_video_channel_info ( build_channel_or_account_name ( domain , name ) ) ,
about = peertube . video_channel ( domain , name )
)
2021-07-28 23:43:34 +00:00
# --- Subtitles/captions proxying ---
@app.route ( " /<string:domain>/videos/watch/<string:id>/<string:lang>.vtt " )
async def subtitles ( domain , id , lang ) :
try :
2021-07-29 01:21:08 +00:00
captions = peertube . video_captions ( domain , id )
for entry in captions [ " data " ] :
if entry [ " language " ] [ " id " ] == lang : return peertube . video_captions_download ( domain , entry [ " captionPath " ] . split ( ' / ' ) [ - 1 ] )
return await render_template (
" error.html " ,
error_number = " 404 " ,
error_reason = " This video has no subtitles/captions inthe requested language "
) , 404
2021-07-28 23:43:34 +00:00
except Exception as e :
return await render_template (
" error.html " ,
error_number = " 500 " ,
error_reason = e
) , 500
2021-07-28 23:43:35 +00:00
# --- Favicon ---
@app.route ( " /favicon.ico " )
async def favicon ( ) :
return await render_template (
" error.html " ,
error_number = " 404 " ,
error_reason = " We don ' t have a favicon yet. If you would like to contribute one, please send it to ~metalune/public-inbox@lists.sr.ht "
) , 404
2021-07-29 00:38:29 +00:00
# --- OpenSearch ---
@app.route ( " /opensearch.xml " )
async def opensearch ( ) :
try :
with open ( ' opensearch.xml ' , ' r ' ) as f :
return f . read ( ) . replace ( ' $BASEURL ' , request . headers [ " Host " ] )
except Exception as e :
return await render_template (
" error.html " ,
error_number = " 500 " ,
error_reason = e
) , 500
2021-01-17 15:04:24 +00:00
if __name__ == " __main__ " :
2021-07-28 23:43:30 +00:00
if len ( sys . argv ) == 3 :
interface = sys . argv [ 1 ]
port = sys . argv [ 2 ]
elif len ( sys . argv ) == 2 :
interface = " 127.0.0.1 "
port = sys . argv [ 1 ]
else :
interface = " 127.0.0.1 "
port = " 5000 "
app . run ( host = interface , port = port )