2022-05-04 23:09:46 +00:00
from files . mail import *
2022-10-08 00:44:02 +00:00
from files . __main__ import app , limiter
2022-05-04 23:09:46 +00:00
from files . helpers . alerts import *
from files . helpers . const import *
2022-09-09 09:13:50 +00:00
from files . helpers . actions import *
2022-05-04 23:09:46 +00:00
from files . classes . award import AWARDS
2022-10-01 22:27:51 +00:00
from sqlalchemy import func , nullslast
2022-05-18 19:29:07 +00:00
import os
2022-05-04 23:09:46 +00:00
from files . classes . mod_logs import ACTIONTYPES , ACTIONTYPES2
from files . classes . badges import BadgeDef
2022-06-07 12:31:24 +00:00
import files . helpers . stats as statshelper
2022-09-10 01:09:33 +00:00
from shutil import move , copyfile
2022-05-04 23:09:46 +00:00
2022-09-29 04:26:50 +00:00
2022-05-04 23:09:46 +00:00
@app.get ( " /r/drama/comments/<id>/<title> " )
@app.get ( " /r/Drama/comments/<id>/<title> " )
def rdrama ( id , title ) :
id = ' ' . join ( f ' { x } / ' for x in id )
return redirect ( f ' /archives/drama/comments/ { id } { title } .html ' )
@app.get ( " /marseys " )
@auth_required
def marseys ( v ) :
2022-09-02 23:58:55 +00:00
if SITE == ' rdrama.net ' :
2022-09-09 09:13:50 +00:00
marseys = g . db . query ( Marsey , User ) . join ( User , Marsey . author_id == User . id ) . filter ( Marsey . submitter_id == None )
2022-05-04 23:09:46 +00:00
sort = request . values . get ( " sort " , " usage " )
2022-10-01 22:27:51 +00:00
if sort == " usage " :
marseys = marseys . order_by ( Marsey . count . desc ( ) , User . username ) . all ( )
elif sort == " added " :
marseys = marseys . order_by ( nullslast ( Marsey . created_utc . desc ( ) ) , User . username ) . all ( )
else : # implied sort == "author"
marseys = marseys . order_by ( User . username , Marsey . count . desc ( ) ) . all ( )
2022-09-14 10:55:35 +00:00
2022-09-27 01:44:34 +00:00
original = os . listdir ( " /asset_submissions/marseys/original " )
2022-09-14 11:02:46 +00:00
for marsey , user in marseys :
2022-09-14 10:55:35 +00:00
if f ' { marsey . name } .png ' in original :
marsey . og = f ' { marsey . name } .png '
elif f ' { marsey . name } .webp ' in original :
marsey . og = f ' { marsey . name } .webp '
elif f ' { marsey . name } .gif ' in original :
marsey . og = f ' { marsey . name } .gif '
elif f ' { marsey . name } .jpeg ' in original :
marsey . og = f ' { marsey . name } .jpeg '
2022-05-04 23:09:46 +00:00
else :
2022-09-09 09:13:50 +00:00
marseys = g . db . query ( Marsey ) . filter ( Marsey . submitter_id == None ) . order_by ( Marsey . count . desc ( ) )
2022-05-04 23:09:46 +00:00
return render_template ( " marseys.html " , v = v , marseys = marseys )
2022-05-05 21:12:07 +00:00
@app.get ( " /marsey_list.json " )
@cache.memoize ( timeout = 600 )
2022-05-04 23:09:46 +00:00
def marsey_list ( ) :
2022-07-09 04:32:48 +00:00
emojis = [ ]
2022-05-05 21:12:07 +00:00
2022-07-09 04:32:48 +00:00
# From database
if EMOJI_MARSEYS :
emojis = [ {
" name " : emoji . name ,
2022-09-02 23:58:55 +00:00
" author " : author if SITE == ' rdrama.net ' or author == " anton-d " else None ,
2022-07-09 04:32:48 +00:00
# yikes, I don't really like this DB schema. Next time be better
" tags " : emoji . tags . split ( " " ) + [ emoji . name [ len ( " marsey " ) : ] \
if emoji . name . startswith ( " marsey " ) else emoji . name ] ,
" count " : emoji . count ,
" class " : " Marsey "
2022-09-09 09:13:50 +00:00
} for emoji , author in g . db . query ( Marsey , User . username ) . join ( User , Marsey . author_id == User . id ) . filter ( Marsey . submitter_id == None ) \
2022-07-09 04:32:48 +00:00
. order_by ( Marsey . count . desc ( ) ) ]
2022-05-05 21:12:07 +00:00
2022-07-09 04:32:48 +00:00
# Static shit
for src in EMOJI_SRCS :
with open ( src , " r " , encoding = " utf-8 " ) as f :
emojis = emojis + json . load ( f )
2022-05-04 23:09:46 +00:00
2022-05-05 21:12:07 +00:00
return jsonify ( emojis )
2022-05-04 23:09:46 +00:00
@app.get ( ' /sidebar ' )
2022-10-06 06:26:10 +00:00
@auth_desired
2022-05-04 23:09:46 +00:00
def sidebar ( v ) :
return render_template ( ' sidebar.html ' , v = v )
@app.get ( " /stats " )
@auth_required
def participation_stats ( v ) :
2022-10-15 09:11:36 +00:00
if v . client : return stats_cached ( )
2022-09-05 20:23:35 +00:00
return render_template ( " stats.html " , v = v , title = " Content Statistics " , data = stats_cached ( ) )
2022-05-04 23:09:46 +00:00
@cache.memoize ( timeout = 86400 )
2022-06-10 09:47:41 +00:00
def stats_cached ( ) :
return statshelper . stats ( SITE_NAME )
2022-05-04 23:09:46 +00:00
@app.get ( " /chart " )
def chart ( ) :
return redirect ( ' /weekly_chart ' )
@app.get ( " /weekly_chart " )
@auth_required
def weekly_chart ( v ) :
2022-06-07 12:31:24 +00:00
return send_file ( statshelper . chart_path ( kind = " weekly " , site = SITE ) )
2022-05-04 23:09:46 +00:00
@app.get ( " /daily_chart " )
@auth_required
def daily_chart ( v ) :
2022-06-07 12:31:24 +00:00
return send_file ( statshelper . chart_path ( kind = " daily " , site = SITE ) )
2022-05-04 23:09:46 +00:00
@app.get ( " /patrons " )
@app.get ( " /paypigs " )
2022-10-06 05:48:55 +00:00
@admin_level_required ( PERMS [ ' VIEW_PATRONS ' ] )
2022-05-04 23:09:46 +00:00
def patrons ( v ) :
2022-09-30 21:07:53 +00:00
if AEVANN_ID and v . id not in ( AEVANN_ID , CARP_ID , SNAKES_ID ) : abort ( 404 )
2022-09-17 22:06:29 +00:00
2022-05-04 23:09:46 +00:00
users = g . db . query ( User ) . filter ( User . patron > 0 ) . order_by ( User . patron . desc ( ) , User . id ) . all ( )
2022-10-02 19:37:07 +00:00
return render_template ( " patrons.html " , v = v , users = users , benefactor_def = AWARDS [ ' benefactor ' ] )
2022-05-04 23:09:46 +00:00
@app.get ( " /admins " )
@app.get ( " /badmins " )
@auth_required
def admins ( v ) :
2022-10-06 05:16:09 +00:00
if v . admin_level > = PERMS [ ' VIEW_SORTED_ADMIN_LIST ' ] :
2022-05-04 23:09:46 +00:00
admins = g . db . query ( User ) . filter ( User . admin_level > 1 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
admins + = g . db . query ( User ) . filter ( User . admin_level == 1 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
else : admins = g . db . query ( User ) . filter ( User . admin_level > 0 ) . order_by ( User . truecoins . desc ( ) ) . all ( )
return render_template ( " admins.html " , v = v , admins = admins )
@app.get ( " /log " )
@app.get ( " /modlog " )
@auth_required
def log ( v ) :
try : page = max ( int ( request . values . get ( " page " , 1 ) ) , 1 )
except : page = 1
admin = request . values . get ( " admin " )
2022-10-01 05:15:56 +00:00
if admin : admin_id = get_id ( admin )
2022-05-04 23:09:46 +00:00
else : admin_id = 0
kind = request . values . get ( " kind " )
2022-10-06 05:56:19 +00:00
if v and v . admin_level > = PERMS [ ' USER_SHADOWBAN ' ] : types = ACTIONTYPES
2022-05-04 23:09:46 +00:00
else : types = ACTIONTYPES2
2022-06-13 16:41:46 +00:00
if kind and kind not in types :
kind = None
actions = [ ]
else :
actions = g . db . query ( ModAction )
2022-10-06 02:24:37 +00:00
if not ( v and v . admin_level > = PERMS [ ' USER_SHADOWBAN ' ] ) :
2022-06-13 16:41:46 +00:00
actions = actions . filter ( ModAction . kind . notin_ ( [ " shadowban " , " unshadowban " ] ) )
if admin_id :
actions = actions . filter_by ( user_id = admin_id )
kinds = set ( [ x . kind for x in actions ] )
2022-10-02 07:50:59 +00:00
kinds . add ( kind )
2022-06-13 16:41:46 +00:00
types2 = { }
for k , val in types . items ( ) :
if k in kinds : types2 [ k ] = val
types = types2
if kind : actions = actions . filter_by ( kind = kind )
actions = actions . order_by ( ModAction . id . desc ( ) ) . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
2022-05-04 23:09:46 +00:00
next_exists = len ( actions ) > 25
actions = actions [ : 25 ]
2022-10-06 05:25:45 +00:00
admins = [ x [ 0 ] for x in g . db . query ( User . username ) . filter ( User . admin_level > = PERMS [ ' ADMIN_MOP_VISIBLE ' ] ) . order_by ( User . username ) . all ( ) ]
2022-05-04 23:09:46 +00:00
return render_template ( " log.html " , v = v , admins = admins , types = types , admin = admin , type = kind , actions = actions , next_exists = next_exists , page = page )
@app.get ( " /log/<id> " )
@auth_required
def log_item ( id , v ) :
try : id = int ( id )
except : abort ( 404 )
2022-06-18 00:57:23 +00:00
action = g . db . get ( ModAction , id )
2022-05-04 23:09:46 +00:00
if not action : abort ( 404 )
2022-10-06 05:25:45 +00:00
admins = [ x [ 0 ] for x in g . db . query ( User . username ) . filter ( User . admin_level > = PERMS [ ' ADMIN_MOP_VISIBLE ' ] ) . all ( ) ]
2022-05-04 23:09:46 +00:00
2022-10-06 05:25:45 +00:00
if v and v . admin_level > = PERMS [ ' USER_SHADOWBAN ' ] : types = ACTIONTYPES
2022-05-04 23:09:46 +00:00
else : types = ACTIONTYPES2
return render_template ( " log.html " , v = v , actions = [ action ] , next_exists = False , page = 1 , action = action , admins = admins , types = types )
2022-06-15 07:27:04 +00:00
@app.get ( " /directory " )
2022-09-20 00:54:10 +00:00
@auth_required
2022-06-15 07:27:04 +00:00
def static_megathread_index ( v ) :
return render_template ( " megathread_index.html " , v = v )
2022-05-04 23:09:46 +00:00
@app.get ( " /api " )
2022-05-22 16:14:32 +00:00
@auth_required
def api ( v ) :
return render_template ( " api.html " , v = v )
2022-05-04 23:09:46 +00:00
@app.get ( " /contact " )
2022-09-09 09:13:50 +00:00
@app.get ( " /contactus " )
@app.get ( " /contact_us " )
2022-05-04 23:09:46 +00:00
@app.get ( " /press " )
@app.get ( " /media " )
2022-10-01 10:14:46 +00:00
@auth_desired
2022-05-04 23:09:46 +00:00
def contact ( v ) :
return render_template ( " contact.html " , v = v )
@app.post ( " /send_admin " )
@limiter.limit ( " 1/second;2/minute;6/hour;10/day " )
2022-07-13 18:14:37 +00:00
@limiter.limit ( " 1/second;2/minute;6/hour;10/day " , key_func = lambda : f ' { SITE } - { session . get ( " lo_user " ) } ' )
2022-05-04 23:09:46 +00:00
@auth_required
def submit_contact ( v ) :
body = request . values . get ( " message " )
if not body : abort ( 400 )
2022-09-01 21:29:27 +00:00
if v . is_muted :
abort ( 403 )
2022-05-04 23:09:46 +00:00
body = f ' This message has been sent automatically to all admins via [/contact](/contact) \n \n Message: \n \n ' + body
2022-06-18 17:41:24 +00:00
body + = process_files ( )
2022-05-04 23:09:46 +00:00
2022-06-18 17:41:24 +00:00
body = body . strip ( )
body_html = sanitize ( body )
2022-05-04 23:09:46 +00:00
new_comment = Comment ( author_id = v . id ,
2022-09-04 23:15:37 +00:00
parent_submission = None ,
level = 1 ,
body_html = body_html ,
sentto = 2
)
2022-05-04 23:09:46 +00:00
g . db . add ( new_comment )
g . db . flush ( )
new_comment . top_comment_id = new_comment . id
2022-10-06 05:07:38 +00:00
admins = g . db . query ( User ) . filter ( User . admin_level > = PERMS [ ' NOTIFICATIONS_MODMAIL ' ] )
2022-09-21 19:38:29 +00:00
if SITE == ' watchpeopledie.co ' :
admins = admins . filter ( User . id != AEVANN_ID )
for admin in admins . all ( ) :
2022-05-04 23:09:46 +00:00
notif = Notification ( comment_id = new_comment . id , user_id = admin . id )
g . db . add ( notif )
2022-09-12 05:23:08 +00:00
return render_template ( " contact.html " , v = v , msg = " Your message has been sent to the admins! " )
2022-05-04 23:09:46 +00:00
@app.get ( ' /archives ' )
def archivesindex ( ) :
return redirect ( " /archives/index.html " )
@app.get ( ' /archives/<path:path> ' )
def archives ( path ) :
resp = make_response ( send_from_directory ( ' /archives ' , path ) )
if request . path . endswith ( ' .css ' ) : resp . headers . add ( " Content-Type " , " text/css " )
return resp
@app.get ( ' /e/<emoji> ' )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-04 23:09:46 +00:00
def emoji ( emoji ) :
if not emoji . endswith ( ' .webp ' ) : abort ( 404 )
resp = make_response ( send_from_directory ( ' assets/images/emojis ' , emoji ) )
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
2022-06-22 15:54:25 +00:00
@app.get ( ' /i/<path:path> ' )
2022-06-22 15:51:19 +00:00
@limiter.exempt
2022-06-22 15:54:25 +00:00
def image ( path ) :
resp = make_response ( send_from_directory ( ' assets/images ' , path ) )
2022-06-22 15:51:19 +00:00
if request . path . endswith ( ' .webp ' ) or request . path . endswith ( ' .gif ' ) or request . path . endswith ( ' .ttf ' ) or request . path . endswith ( ' .woff2 ' ) :
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
if request . path . endswith ( ' .webp ' ) :
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
2022-05-04 23:09:46 +00:00
@app.get ( ' /assets/<path:path> ' )
@app.get ( ' /static/assets/<path:path> ' )
2022-05-22 17:23:52 +00:00
@limiter.exempt
2022-05-04 23:09:46 +00:00
def static_service ( path ) :
resp = make_response ( send_from_directory ( ' assets ' , path ) )
if request . path . endswith ( ' .webp ' ) or request . path . endswith ( ' .gif ' ) or request . path . endswith ( ' .ttf ' ) or request . path . endswith ( ' .woff2 ' ) :
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
if request . path . endswith ( ' .webp ' ) :
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
2022-10-04 19:35:02 +00:00
### BEGIN FALLBACK ASSET SERVING
# In production, we have nginx serve these locations now.
# These routes stay for local testing. Requests don't reach them on prod.
@app.get ( ' /images/<path> ' )
@app.get ( ' /hostedimages/<path> ' )
@app.get ( " /static/images/<path> " )
@limiter.exempt
def images ( path ) :
resp = make_response ( send_from_directory ( ' /images ' , path ) )
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
resp . headers . remove ( " Content-Type " )
resp . headers . add ( " Content-Type " , " image/webp " )
return resp
@app.get ( ' /videos/<path> ' )
@limiter.exempt
def videos ( path ) :
resp = make_response ( send_from_directory ( ' /videos ' , path ) )
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
return resp
@app.get ( ' /audio/<path> ' )
@limiter.exempt
def audio ( path ) :
resp = make_response ( send_from_directory ( ' /audio ' , path ) )
resp . headers . remove ( " Cache-Control " )
resp . headers . add ( " Cache-Control " , " public, max-age=3153600 " )
return resp
### END FALLBACK ASSET SERVING
2022-05-04 23:09:46 +00:00
@app.get ( " /robots.txt " )
def robots_txt ( ) :
2022-09-11 01:56:47 +00:00
return send_file ( " assets/robots.txt " )
2022-05-04 23:09:46 +00:00
no = ( 21 , 22 , 23 , 24 , 25 , 26 , 27 )
@cache.memoize ( timeout = 3600 )
def badge_list ( site ) :
badges = g . db . query ( BadgeDef ) . filter ( BadgeDef . id . notin_ ( no ) ) . order_by ( BadgeDef . id ) . all ( )
counts_raw = g . db . query ( Badge . badge_id , func . count ( ) ) . group_by ( Badge . badge_id ) . all ( )
2022-05-09 11:21:49 +00:00
users = g . db . query ( User ) . count ( )
2022-05-04 23:09:46 +00:00
counts = { }
for c in counts_raw :
counts [ c [ 0 ] ] = ( c [ 1 ] , float ( c [ 1 ] ) * 100 / max ( users , 1 ) )
return badges , counts
@app.get ( " /badges " )
@auth_required
2022-10-11 07:29:24 +00:00
@feature_required ( ' BADGES ' )
2022-05-04 23:09:46 +00:00
def badges ( v ) :
2022-10-11 07:29:24 +00:00
2022-10-10 09:51:29 +00:00
2022-05-04 23:09:46 +00:00
badges , counts = badge_list ( SITE )
return render_template ( " badges.html " , v = v , badges = badges , counts = counts )
@app.get ( " /blocks " )
2022-07-18 07:17:45 +00:00
@admin_level_required ( PERMS [ ' USER_BLOCKS_VISIBLE ' ] )
2022-05-04 23:09:46 +00:00
def blocks ( v ) :
blocks = g . db . query ( UserBlock ) . all ( )
users = [ ]
targets = [ ]
for x in blocks :
2022-06-13 03:03:36 +00:00
acc_user = get_account ( x . user_id )
2022-09-04 23:15:37 +00:00
acc_tgt = get_account ( x . target_id )
2022-06-13 03:03:36 +00:00
if acc_user . shadowbanned or acc_tgt . shadowbanned : continue
users . append ( acc_user )
targets . append ( acc_tgt )
2022-05-04 23:09:46 +00:00
return render_template ( " blocks.html " , v = v , users = users , targets = targets )
@app.get ( " /banned " )
@auth_required
def banned ( v ) :
2022-05-25 20:16:26 +00:00
users = g . db . query ( User ) . filter ( User . is_banned > 0 , User . unban_utc == 0 ) . all ( )
2022-05-04 23:09:46 +00:00
return render_template ( " banned.html " , v = v , users = users )
@app.get ( " /formatting " )
@auth_required
def formatting ( v ) :
return render_template ( " formatting.html " , v = v )
2022-06-10 22:14:03 +00:00
@app.get ( " /service-worker.js " )
2022-05-04 23:09:46 +00:00
def serviceworker ( ) :
2022-06-10 22:14:03 +00:00
with open ( " files/assets/js/service-worker.js " , " r " , encoding = " utf-8 " ) as f :
return Response ( f . read ( ) , mimetype = ' application/javascript ' )
2022-05-27 19:23:01 +00:00
2022-05-04 23:09:46 +00:00
@app.get ( " /settings/security " )
@auth_required
def settings_security ( v ) :
return render_template ( " settings_security.html " ,
2022-09-04 23:15:37 +00:00
v = v ,
mfa_secret = pyotp . random_base32 ( ) if not v . mfa_secret else None
)
2022-05-04 23:09:46 +00:00
@app.post ( " /dismiss_mobile_tip " )
def dismiss_mobile_tip ( ) :
session [ " tooltip_last_dismissed " ] = int ( time . time ( ) )
return " " , 204
2022-07-13 15:20:10 +00:00
@app.get ( " /transfers/<id> " )
@auth_required
def transfers_id ( id , v ) :
try : id = int ( id )
except : abort ( 404 )
transfer = g . db . get ( Comment , id )
if not transfer : abort ( 404 )
return render_template ( " transfers.html " , v = v , page = 1 , comments = [ transfer ] , standalone = True , next_exists = False )
@app.get ( " /transfers " )
@auth_required
def transfers ( v ) :
comments = g . db . query ( Comment ) . filter ( Comment . author_id == AUTOJANNY_ID , Comment . parent_submission == None , Comment . body_html . like ( " % </a> has transferred % " ) ) . order_by ( Comment . id . desc ( ) )
try : page = max ( int ( request . values . get ( " page " , 1 ) ) , 1 )
except : page = 1
comments = comments . offset ( 25 * ( page - 1 ) ) . limit ( 26 ) . all ( )
next_exists = len ( comments ) > 25
comments = comments [ : 25 ]
2022-09-10 00:30:25 +00:00
2022-10-15 09:11:36 +00:00
if v . client :
2022-09-10 00:30:25 +00:00
return { " data " : [ x . json for x in comments ] }
else :
return render_template ( " transfers.html " , v = v , page = page , comments = comments , standalone = True , next_exists = next_exists )
2022-07-18 08:39:21 +00:00
2022-09-19 23:59:24 +00:00
if not os . path . exists ( f ' files/templates/donate_ { SITE_NAME } .html ' ) :
copyfile ( f ' files/templates/donate_rDrama.html ' , f ' files/templates/donate_ { SITE_NAME } .html ' )
@app.get ( ' /donate ' )
2022-09-20 00:54:10 +00:00
@auth_required
2022-09-19 23:59:24 +00:00
def donate ( v ) :
2022-09-22 02:17:02 +00:00
return render_template ( f ' donate_ { SITE_NAME } .html ' , v = v )
2022-09-25 04:11:06 +00:00
if SITE == ' pcmemes.net ' :
2022-09-22 21:40:32 +00:00
from files . classes . streamers import *
2022-09-22 02:17:02 +00:00
2022-09-25 06:27:58 +00:00
id_regex = re . compile ( ' " externalId " : " ([^ " ]*?) " ' , flags = re . A )
2022-09-22 02:17:02 +00:00
live_regex = re . compile ( ' playerOverlayVideoDetailsRenderer " : \ { " title " : \ { " simpleText " : " (.*?) " \ }, " subtitle " : \ { " runs " : \ [ \ { " text " : " (.*?) " \ }, \ { " text " : " • " \ }, \ { " text " : " (.*?) " \ } ' , flags = re . A )
live_thumb_regex = re . compile ( ' \ { " thumbnail " : \ { " thumbnails " : \ [ \ { " url " : " (.*?) " ' , flags = re . A )
offline_regex = re . compile ( ' " , " title " : " (.*?) " .*? " width " :48, " height " :48 \ }, \ { " url " : " (.*?) " ' , flags = re . A )
2022-09-26 05:08:29 +00:00
offline_details_regex = re . compile ( ' simpleText " : " Μεταδόθηκε πριν από ([0-9]*?) ([^ " ]*?) " \ }, " viewCountText " : \ { " simpleText " : " ([0-9.]*?) προβολές " ' , flags = re . A )
2022-09-25 04:11:06 +00:00
2022-09-25 05:24:59 +00:00
def process_streamer ( id , live = ' live ' ) :
url = f ' https://www.youtube.com/channel/ { id } / { live } '
2022-09-26 04:01:25 +00:00
req = requests . get ( url , cookies = { ' CONSENT ' : ' YES+1 ' } , timeout = 5 )
2022-09-25 04:11:06 +00:00
text = req . text
if ' " videoDetails " : { " videoId " ' in text :
y = live_regex . search ( text )
2022-09-25 05:24:59 +00:00
count = y . group ( 3 )
2022-09-29 18:25:01 +00:00
if count == ' 1 παρακολουθεί τώρα ' :
2022-09-30 12:19:48 +00:00
count = " 1 "
2022-09-29 18:25:01 +00:00
2022-09-26 05:08:29 +00:00
if ' περιμένει ' in count :
2022-10-08 02:32:33 +00:00
if live != ' ' :
return process_streamer ( id , ' ' )
else :
return None
2022-09-25 05:24:59 +00:00
2022-09-29 18:25:01 +00:00
count = int ( count . replace ( ' . ' , ' ' ) )
2022-09-25 05:24:59 +00:00
t = live_thumb_regex . search ( text )
thumb = t . group ( 1 )
name = y . group ( 2 )
title = y . group ( 1 )
return ( True , ( id , req . url , thumb , name , title , count ) )
2022-09-25 04:11:06 +00:00
else :
t = offline_regex . search ( text )
2022-10-08 02:32:33 +00:00
if not t :
if live != ' ' :
return process_streamer ( id , ' ' )
else :
return None
2022-09-29 18:29:44 +00:00
2022-09-25 04:11:06 +00:00
y = offline_details_regex . search ( text )
2022-09-25 04:37:16 +00:00
if y :
2022-09-25 05:45:02 +00:00
views = y . group ( 3 ) . replace ( ' . ' , ' ' )
2022-09-25 04:37:16 +00:00
quantity = int ( y . group ( 1 ) )
unit = y . group ( 2 )
2022-10-01 09:43:16 +00:00
if unit . startswith ( ' δε ' ) :
unit = ' second '
modifier = 1 / 60
elif unit . startswith ( ' λεπτ ' ) :
2022-09-25 04:37:16 +00:00
unit = ' minute '
modifier = 1
2022-10-01 09:43:16 +00:00
elif unit . startswith ( ' ώρ ' ) :
2022-09-25 04:37:16 +00:00
unit = ' hour '
modifier = 60
2022-10-01 09:43:16 +00:00
elif unit . startswith ( ' ημέρ ' ) :
2022-09-25 04:38:43 +00:00
unit = ' day '
modifier = 1440
2022-10-01 09:43:16 +00:00
elif unit . startswith ( ' εβδομάδ ' ) :
2022-09-25 04:37:16 +00:00
unit = ' week '
modifier = 10080
2022-09-26 05:08:29 +00:00
elif unit . startswith ( ' μήν ' ) :
2022-09-25 04:37:16 +00:00
unit = ' month '
modifier = 43800
2022-09-26 05:08:29 +00:00
elif unit . startswith ( ' έτ ' ) :
2022-09-25 04:37:16 +00:00
unit = ' year '
modifier = 525600
2022-10-01 09:43:16 +00:00
minutes = quantity * modifier
2022-09-25 04:37:16 +00:00
actual = f ' { quantity } { unit } '
if quantity > 1 : actual + = ' s '
2022-09-25 04:33:55 +00:00
else :
2022-10-06 22:50:36 +00:00
minutes = 9999999999
2022-09-25 04:37:16 +00:00
actual = ' ??? '
2022-09-25 04:38:43 +00:00
views = 0
2022-09-25 04:11:06 +00:00
2022-09-29 18:29:44 +00:00
thumb = t . group ( 2 )
2022-09-26 02:46:56 +00:00
2022-09-25 05:24:59 +00:00
name = t . group ( 1 )
return ( False , ( id , req . url . rstrip ( ' /live ' ) , thumb , name , minutes , actual , views ) )
2022-09-25 04:11:06 +00:00
2022-09-22 02:17:02 +00:00
2022-09-23 19:41:04 +00:00
def live_cached ( ) :
2022-09-22 02:17:02 +00:00
live = [ ]
offline = [ ]
2022-09-22 21:40:32 +00:00
db = db_session ( )
streamers = [ x [ 0 ] for x in db . query ( Streamer . id ) . all ( ) ]
db . close ( )
2022-09-25 04:11:06 +00:00
for id in streamers :
processed = process_streamer ( id )
if processed :
if processed [ 0 ] : live . append ( processed [ 1 ] )
else : offline . append ( processed [ 1 ] )
2022-09-22 02:17:02 +00:00
2022-09-23 21:09:19 +00:00
live = sorted ( live , key = lambda x : x [ 5 ] , reverse = True )
2022-09-25 04:11:06 +00:00
offline = sorted ( offline , key = lambda x : x [ 4 ] )
2022-09-25 11:28:53 +00:00
if live : cache . set ( ' live ' , live )
if offline : cache . set ( ' offline ' , offline )
2022-09-22 02:33:45 +00:00
@app.get ( ' /live ' )
@app.get ( ' /logged_out/live ' )
@auth_desired_with_logingate
2022-09-25 03:08:44 +00:00
def live_list ( v ) :
live = cache . get ( ' live ' ) or [ ]
offline = cache . get ( ' offline ' ) or [ ]
2022-09-23 20:58:10 +00:00
2022-09-25 03:08:44 +00:00
return render_template ( ' live.html ' , v = v , live = live , offline = offline )
2022-09-22 21:40:32 +00:00
@app.post ( ' /live/add ' )
2022-10-06 06:22:42 +00:00
@admin_level_required ( PERMS [ ' STREAMERS_MODERATION ' ] )
2022-09-22 21:40:32 +00:00
def live_add ( v ) :
2022-09-25 06:45:19 +00:00
link = request . values . get ( ' link ' ) . strip ( )
2022-09-22 22:02:15 +00:00
2022-09-25 06:45:19 +00:00
if ' youtube.com/channel/ ' in link :
id = link . split ( ' youtube.com/channel/ ' ) [ 1 ] . rstrip ( ' / ' )
else :
2022-09-26 04:01:25 +00:00
text = requests . get ( link , cookies = { ' CONSENT ' : ' YES+1 ' } , timeout = 5 ) . text
2022-09-25 06:27:58 +00:00
try : id = id_regex . search ( text ) . group ( 1 )
2022-10-12 15:55:42 +00:00
except : abort ( 400 , " Invalid ID " )
2022-09-25 06:14:21 +00:00
2022-09-25 03:08:44 +00:00
live = cache . get ( ' live ' ) or [ ]
offline = cache . get ( ' offline ' ) or [ ]
2022-09-23 20:58:10 +00:00
2022-09-22 22:02:15 +00:00
if not id or len ( id ) != 24 :
2022-10-12 15:55:42 +00:00
abort ( 400 , " Invalid ID " )
2022-09-22 22:02:15 +00:00
2022-09-22 21:40:32 +00:00
existing = g . db . get ( Streamer , id )
if not existing :
streamer = Streamer ( id = id )
g . db . add ( streamer )
g . db . flush ( )
if v . id != KIPPY_ID :
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( KIPPY_ID , f " @ { v . username } (Admin) has added a [new YouTube channel](https://www.youtube.com/channel/ { streamer . id } ) " )
2022-09-22 22:27:38 +00:00
2022-09-25 04:11:06 +00:00
processed = process_streamer ( id )
if processed :
if processed [ 0 ] : live . append ( processed [ 1 ] )
else : offline . append ( processed [ 1 ] )
live = sorted ( live , key = lambda x : x [ 5 ] , reverse = True )
offline = sorted ( offline , key = lambda x : x [ 4 ] )
2022-09-25 11:28:53 +00:00
if live : cache . set ( ' live ' , live )
if offline : cache . set ( ' offline ' , offline )
2022-09-25 03:08:44 +00:00
2022-09-25 03:21:41 +00:00
return redirect ( ' /live ' )
2022-09-22 21:40:32 +00:00
@app.post ( ' /live/remove ' )
2022-10-06 06:22:42 +00:00
@admin_level_required ( PERMS [ ' STREAMERS_MODERATION ' ] )
2022-09-22 21:40:32 +00:00
def live_remove ( v ) :
2022-09-24 00:21:32 +00:00
id = request . values . get ( ' id ' ) . strip ( )
2022-09-22 21:40:32 +00:00
if not id : abort ( 400 )
streamer = g . db . get ( Streamer , id )
if streamer :
if v . id != KIPPY_ID :
2022-10-10 06:11:17 +00:00
send_repeatable_notification ( KIPPY_ID , f " @ { v . username } (Admin) has removed a [YouTube channel](https://www.youtube.com/channel/ { streamer . id } ) " )
2022-09-22 21:40:32 +00:00
g . db . delete ( streamer )
2022-09-23 20:58:10 +00:00
2022-09-25 03:08:44 +00:00
live = cache . get ( ' live ' ) or [ ]
offline = cache . get ( ' offline ' ) or [ ]
live = [ x for x in live if x [ 0 ] != id ]
offline = [ x for x in offline if x [ 0 ] != id ]
2022-09-25 11:28:53 +00:00
if live : cache . set ( ' live ' , live )
if offline : cache . set ( ' offline ' , offline )
2022-09-25 03:08:44 +00:00
2022-09-28 08:14:40 +00:00
return redirect ( ' /live ' )