2022-05-18 16:24:27 +02:00
from flask import (
Flask ,
render_template ,
request ,
redirect ,
Response ,
send_from_directory ,
url_for ,
send_file ,
make_response ,
jsonify ,
)
2022-03-07 13:52:39 +01:00
from flask_cors import CORS
2021-07-04 01:52:30 +02:00
import youtube_dl
2021-07-12 22:48:21 +02:00
import textwrap
2021-07-12 10:49:07 +02:00
import twitter
import pymongo
2022-01-18 00:36:43 +01:00
import requests
2021-07-04 11:40:22 +02:00
import json
2021-07-04 03:41:47 +02:00
import re
2021-07-11 22:48:17 +02:00
import os
2021-08-03 03:52:13 +02:00
import urllib . parse
2022-01-18 00:36:43 +01:00
import urllib . request
2022-03-07 13:52:39 +01:00
from datetime import date
2021-07-04 01:52:30 +02:00
2022-05-19 14:13:28 +02:00
2021-07-04 01:52:30 +02:00
app = Flask ( __name__ )
2022-03-07 13:52:39 +01:00
CORS ( app )
2021-10-01 23:59:20 +02:00
pathregex = re . compile ( " \\ w { 1,15} \\ /(status|statuses) \\ / \\ d { 2,20} " )
2022-02-20 23:13:18 +01:00
generate_embed_user_agents = [
2022-05-11 01:40:09 +02:00
" facebookexternalhit/1.1 " ,
" Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36 " ,
" Mozilla/5.0 (Windows; U; Windows NT 10.0; en-US; Valve Steam Client/default/1596241936; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 " ,
2022-05-18 16:24:27 +02:00
" Mozilla/5.0 (Windows; U; Windows NT 10.0; en-US; Valve Steam Client/default/0; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 " ,
" Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.4 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.4 facebookexternalhit/1.1 Facebot Twitterbot/1.0 " ,
2022-05-11 01:40:09 +02:00
" facebookexternalhit/1.1 " ,
2022-05-18 16:24:27 +02:00
" Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; Valve Steam FriendsUI Tenfoot/0; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 " ,
" Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots) " ,
" Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0 " ,
" Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com) " ,
" TelegramBot (like TwitterBot) " ,
" Mozilla/5.0 (compatible; January/1.0; +https://gitlab.insrt.uk/revolt/january) " ,
" test " ,
]
2021-07-04 01:52:30 +02:00
2021-07-13 19:18:50 +02:00
# Read config from config.json. If it does not exist, create new.
2021-07-11 22:48:17 +02:00
if not os . path . exists ( " config.json " ) :
with open ( " config.json " , " w " ) as outfile :
2022-02-20 23:13:18 +01:00
default_config = {
2022-05-18 16:24:27 +02:00
" config " : {
" link_cache " : " json " ,
" database " : " [url to mongo database goes here] " ,
" table " : " TwiFix " ,
" method " : " youtube-dl " ,
" color " : " #43B581 " ,
" appname " : " TwitFix " ,
" repo " : " https://github.com/robinuniverse/twitfix " ,
" url " : " https://fxtwitter.com " ,
} ,
" api " : {
" api_key " : " [api_key goes here] " ,
" api_secret " : " [api_secret goes here] " ,
" access_token " : " [access_token goes here] " ,
" access_secret " : " [access_secret goes here] " ,
} ,
2022-02-20 23:13:18 +01:00
}
2021-07-13 19:18:50 +02:00
json . dump ( default_config , outfile , indent = 4 , sort_keys = True )
2021-07-11 22:48:17 +02:00
2021-07-13 19:18:50 +02:00
config = default_config
else :
f = open ( " config.json " )
config = json . load ( f )
f . close ( )
2021-07-11 22:48:17 +02:00
2021-07-13 20:57:09 +02:00
# If method is set to API or Hybrid, attempt to auth with the Twitter API
2022-05-18 16:24:27 +02:00
if config [ " config " ] [ " method " ] in ( " api " , " hybrid " ) :
auth = twitter . oauth . OAuth (
config [ " api " ] [ " access_token " ] ,
config [ " api " ] [ " access_secret " ] ,
config [ " api " ] [ " api_key " ] ,
config [ " api " ] [ " api_secret " ] ,
)
2021-07-12 10:49:07 +02:00
twitter_api = twitter . Twitter ( auth = auth )
2022-05-18 16:24:27 +02:00
link_cache_system = config [ " config " ] [ " link_cache " ]
2021-07-08 05:17:23 +02:00
if link_cache_system == " json " :
link_cache = { }
2021-07-11 22:48:17 +02:00
if not os . path . exists ( " config.json " ) :
with open ( " config.json " , " w " ) as outfile :
2022-05-18 16:24:27 +02:00
default_link_cache = { " test " : " test " }
2021-07-13 19:18:50 +02:00
json . dump ( default_link_cache , outfile , indent = 4 , sort_keys = True )
2021-07-11 22:48:17 +02:00
2022-05-18 16:24:27 +02:00
f = open (
" links.json " ,
)
2021-07-08 05:17:23 +02:00
link_cache = json . load ( f )
f . close ( )
elif link_cache_system == " db " :
2022-05-18 16:24:27 +02:00
client = pymongo . MongoClient ( config [ " config " ] [ " database " ] , connect = False )
table = config [ " config " ] [ " table " ]
2022-03-07 13:52:39 +01:00
db = client [ table ]
2021-07-04 11:40:22 +02:00
2022-05-18 16:24:27 +02:00
@app.route ( " /bidoof/ " )
2022-02-20 23:13:18 +01:00
def bidoof ( ) :
2022-05-18 16:24:27 +02:00
return redirect (
" https://cdn.discordapp.com/attachments/291764448757284885/937343686927319111/IMG_20211226_202956_163.webp " ,
301 ,
)
2022-02-20 23:13:18 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /thisiswhatrunstwxtter/ " )
2022-05-11 01:40:09 +02:00
def discord ( ) :
2022-05-18 16:24:27 +02:00
return redirect (
" https://cdn.discordapp.com/attachments/932899721767448607/976472084974829568/unknown.png " ,
301 ,
)
2022-05-11 01:40:09 +02:00
2022-05-18 16:24:27 +02:00
@app.route ( " /stats/ " )
2022-03-07 13:52:39 +01:00
def statsPage ( ) :
today = str ( date . today ( ) )
stats = getStats ( today )
2022-05-18 16:24:27 +02:00
return render_template (
" stats.html " ,
embeds = stats [ " embeds " ] ,
downloadss = stats [ " downloads " ] ,
api = stats [ " api " ] ,
linksCached = stats [ " linksCached " ] ,
date = today ,
)
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /latest/ " )
2021-12-03 01:58:36 +01:00
def latest ( ) :
2022-05-18 16:24:27 +02:00
return render_template ( " latest.html " )
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /copy.svg " ) # Return a SVG needed for Latest
2022-03-07 13:52:39 +01:00
def icon ( ) :
2022-05-18 16:24:27 +02:00
return send_from_directory (
os . path . join ( app . root_path , " static " ) , " copy.svg " , mimetype = " image/svg+xml "
)
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /font.ttf " ) # Return a font needed for Latest
2022-03-07 13:52:39 +01:00
def font ( ) :
2022-05-18 16:24:27 +02:00
return send_from_directory (
os . path . join ( app . root_path , " static " ) ,
" NotoColorEmoji.ttf " ,
mimetype = " application/octet-stream " ,
)
2022-02-20 23:13:18 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /top/ " ) # Try to return the most hit video
2022-02-20 23:13:18 +01:00
def top ( ) :
2022-05-18 16:24:27 +02:00
vnf = db . linkCache . find_one ( sort = [ ( " hits " , pymongo . DESCENDING ) ] )
desc = re . sub ( r " http.*t \ .co \ S+ " , " " , vnf [ " description " ] )
urlUser = urllib . parse . quote ( vnf [ " uploader " ] )
2022-03-07 13:52:39 +01:00
urlDesc = urllib . parse . quote ( desc )
2022-05-18 16:24:27 +02:00
urlLink = urllib . parse . quote ( vnf [ " url " ] )
print ( " ➤ [ ✔ ] Top video page loaded: " + vnf [ " tweet " ] )
return render_template (
" inline.html " ,
page = " Top " ,
vidlink = vnf [ " url " ] ,
vidurl = vnf [ " url " ] ,
desc = desc ,
pic = vnf [ " thumbnail " ] ,
user = vnf [ " uploader " ] ,
video_link = vnf [ " url " ] ,
color = config [ " config " ] [ " color " ] ,
appname = config [ " config " ] [ " appname " ] ,
repo = config [ " config " ] [ " repo " ] ,
url = config [ " config " ] [ " url " ] ,
urlDesc = urlDesc ,
urlUser = urlUser ,
urlLink = urlLink ,
tweet = vnf [ " tweet " ] ,
)
@app.route ( " /api/latest/ " ) # Return some raw VNF data sorted by top tweets
2022-03-07 13:52:39 +01:00
def apiLatest ( ) :
2022-05-18 16:24:27 +02:00
bigvnf = [ ]
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
tweets = request . args . get ( " tweets " , default = 10 , type = int )
page = request . args . get ( " page " , default = 0 , type = int )
2022-03-07 13:52:39 +01:00
if tweets > 15 :
tweets = 1
2022-05-18 16:24:27 +02:00
vnf = (
db . linkCache . find ( sort = [ ( " _id " , pymongo . DESCENDING ) ] )
. skip ( tweets * page )
. limit ( tweets )
)
2022-03-07 13:52:39 +01:00
for r in vnf :
bigvnf . append ( r )
print ( " ➤ [ ✔ ] Latest video API called " )
2022-05-18 16:24:27 +02:00
addToStat ( " api " )
return Response (
response = json . dumps ( bigvnf , default = str ) ,
status = 200 ,
mimetype = " application/json " ,
)
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
@app.route ( " /api/top/ " ) # Return some raw VNF data sorted by top tweets
2022-03-07 13:52:39 +01:00
def apiTop ( ) :
2022-05-18 16:24:27 +02:00
bigvnf = [ ]
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
tweets = request . args . get ( " tweets " , default = 10 , type = int )
page = request . args . get ( " page " , default = 0 , type = int )
2022-03-07 13:52:39 +01:00
if tweets > 15 :
tweets = 1
2022-05-18 16:24:27 +02:00
vnf = (
db . linkCache . find ( sort = [ ( " hits " , pymongo . DESCENDING ) ] )
. skip ( tweets * page )
. limit ( tweets )
)
2022-03-07 13:52:39 +01:00
for r in vnf :
bigvnf . append ( r )
print ( " ➤ [ ✔ ] Top video API called " )
2022-05-18 16:24:27 +02:00
addToStat ( " api " )
return Response (
response = json . dumps ( bigvnf , default = str ) ,
status = 200 ,
mimetype = " application/json " ,
)
2022-03-07 13:52:39 +01:00
2022-05-18 16:24:27 +02:00
@app.route (
" /api/stats/ "
) # Return a json of a usage stats for a given date (defaults to today)
2022-03-07 13:52:39 +01:00
def apiStats ( ) :
try :
2022-05-18 16:24:27 +02:00
addToStat ( " api " )
2022-03-07 13:52:39 +01:00
today = str ( date . today ( ) )
desiredDate = request . args . get ( " date " , default = today , type = str )
stat = getStats ( desiredDate )
2022-05-18 16:24:27 +02:00
print ( " ➤ [ ✔ ] Stats API called " )
return Response (
response = json . dumps ( stat , default = str ) ,
status = 200 ,
mimetype = " application/json " ,
)
2022-03-07 13:52:39 +01:00
except :
2022-05-18 16:24:27 +02:00
print ( " ➤ [ ✔ ] Stats API failed " )
2021-12-03 01:58:36 +01:00
2022-05-18 16:24:27 +02:00
@app.route (
" / "
) # If the useragent is discord, return the embed, if not, redirect to configured repo directly
2021-07-06 05:30:16 +02:00
def default ( ) :
2022-05-18 16:24:27 +02:00
user_agent = request . headers . get ( " user-agent " )
2021-08-03 03:52:13 +02:00
if user_agent in generate_embed_user_agents :
2022-05-18 16:24:27 +02:00
return message (
" Twxtter is an attempt to fix twitter video embeds in discord! :) \n \n 💖 \n \n Click me to be redirected to the repo! "
)
2021-07-14 10:33:46 +02:00
else :
2022-05-18 16:24:27 +02:00
return redirect ( config [ " config " ] [ " repo " ] , 301 )
2021-07-06 05:30:16 +02:00
2022-05-18 16:24:27 +02:00
@app.route ( " /oembed.json " ) # oEmbed endpoint
2021-07-09 12:32:04 +02:00
def oembedend ( ) :
2022-05-18 16:24:27 +02:00
desc = request . args . get ( " desc " , None )
user = request . args . get ( " user " , None )
link = request . args . get ( " link " , None )
2022-02-20 23:13:18 +01:00
ttype = request . args . get ( " ttype " , None )
2022-05-18 16:24:27 +02:00
return oEmbedGen ( desc , user , link , ttype )
2021-07-09 12:32:04 +02:00
2022-05-18 16:24:27 +02:00
@app.route ( " /<path:sub_path> " ) # Default endpoint used by everything
2021-07-13 19:18:50 +02:00
def twitfix ( sub_path ) :
2022-05-18 16:24:27 +02:00
user_agent = request . headers . get ( " user-agent " )
2021-07-13 19:18:50 +02:00
match = pathregex . search ( sub_path )
2022-03-07 13:52:39 +01:00
print ( request . url )
2022-01-18 00:36:43 +01:00
2022-05-27 00:59:49 +02:00
if request . url . endswith ( " .mp4 " ) or request . url . endswith ( " %2E mp4 " ) :
2022-03-07 13:52:39 +01:00
twitter_url = " https://twitter.com/ " + sub_path
2022-05-18 16:24:27 +02:00
2022-01-18 00:36:43 +01:00
if " ? " not in request . url :
2022-03-07 13:52:39 +01:00
clean = twitter_url [ : - 4 ]
2022-01-18 00:36:43 +01:00
else :
2022-03-07 13:52:39 +01:00
clean = twitter_url
return dl ( clean )
2022-05-11 01:40:09 +02:00
elif request . url . endswith ( " .json " ) or request . url . endswith ( " %2E json " ) :
twitter_url = " https://twitter.com/ " + sub_path
2022-05-18 16:24:27 +02:00
2022-05-11 01:40:09 +02:00
if " ? " not in request . url :
clean = twitter_url [ : - 5 ]
else :
clean = twitter_url
2022-05-18 16:24:27 +02:00
print ( " ➤ [ API ] VNF Json api hit! " )
2022-05-11 01:40:09 +02:00
2022-05-18 16:24:27 +02:00
vnf = link_to_vnf_from_api ( clean . replace ( " .json " , " " ) )
2022-05-11 01:40:09 +02:00
if user_agent in generate_embed_user_agents :
2022-05-18 16:24:27 +02:00
return message (
" VNF Data: ( discord useragent preview ) \n \n "
+ json . dumps ( vnf , default = str )
)
2022-05-11 01:40:09 +02:00
else :
2022-05-18 16:24:27 +02:00
return Response (
response = json . dumps ( vnf , default = str ) ,
status = 200 ,
mimetype = " application/json " ,
)
elif (
2022-05-26 20:02:19 +02:00
request . url . endswith ( ( " /1 " , " /2 " , " /3 " , " /4 " , " %2F 1 " , " %2F 2 " , " %2F 3 " , " %2F 4 " ) )
2022-05-18 16:24:27 +02:00
) :
2022-03-07 13:52:39 +01:00
twitter_url = " https://twitter.com/ " + sub_path
2022-05-18 16:24:27 +02:00
2022-03-07 13:52:39 +01:00
if " ? " not in request . url :
clean = twitter_url [ : - 2 ]
else :
clean = twitter_url
2022-05-18 16:24:27 +02:00
image = int ( request . url [ - 1 ] ) - 1
2022-03-07 13:52:39 +01:00
return embed_video ( clean , image )
2022-05-18 16:24:27 +02:00
elif (
2022-05-26 20:12:36 +02:00
request . url . endswith ( ( " /1p " , " /2p " , " /3p " , " /4p " , " %2F 1p " , " %2F 2p " , " %2F 3p " , " %2F 4p " ) )
2022-05-18 16:24:27 +02:00
) :
twitter_url = " https://twitter.com/ " + sub_path
if " ? " not in request . url :
clean = twitter_url [ : - 3 ]
else :
clean = twitter_url
image = int ( request . url [ - 2 ] ) - 1
return embed_video ( clean , image , raw = True )
2021-07-13 08:32:25 +02:00
if match is not None :
2021-07-13 19:18:50 +02:00
twitter_url = sub_path
2021-07-04 03:41:47 +02:00
2021-07-13 08:32:25 +02:00
if match . start ( ) == 0 :
2021-07-13 19:18:50 +02:00
twitter_url = " https://twitter.com/ " + sub_path
2021-07-04 03:41:47 +02:00
2021-08-03 03:52:13 +02:00
if user_agent in generate_embed_user_agents :
2021-07-16 22:25:43 +02:00
res = embed_video ( twitter_url )
return res
2021-08-03 03:52:13 +02:00
2021-07-13 02:45:30 +02:00
else :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ R ] Redirect to " + twitter_url )
2021-07-13 08:32:25 +02:00
return redirect ( twitter_url , 301 )
2021-07-04 01:52:30 +02:00
else :
2021-07-21 23:36:05 +02:00
return message ( " This doesn ' t appear to be a twitter URL " )
2021-07-04 01:52:30 +02:00
2022-05-18 16:24:27 +02:00
@app.route (
" /other/<path:sub_path> "
) # Show all info that Youtube-DL can get about a video as a json
2021-07-13 19:18:50 +02:00
def other ( sub_path ) :
2022-05-18 16:24:27 +02:00
otherurl = request . url . split ( " /other/ " , 1 ) [ 1 ] . replace ( " :/ " , " :// " )
2022-01-18 00:36:43 +01:00
print ( " ➤ [ OTHER ] Other URL embed attempted: " + otherurl )
2021-12-03 03:14:01 +01:00
res = embed_video ( otherurl )
2021-07-05 18:02:17 +02:00
return res
2022-05-18 16:24:27 +02:00
@app.route (
" /info/<path:sub_path> "
) # Show all info that Youtube-DL can get about a video as a json
2021-07-13 19:18:50 +02:00
def info ( sub_path ) :
2022-05-18 16:24:27 +02:00
infourl = request . url . split ( " /info/ " , 1 ) [ 1 ] . replace ( " :/ " , " :// " )
2022-01-18 00:36:43 +01:00
print ( " ➤ [ INFO ] Info data requested: " + infourl )
2022-05-18 16:24:27 +02:00
with youtube_dl . YoutubeDL ( { " outtmpl " : " %(id)s . %(ext)s " } ) as ydl :
2021-12-03 03:14:01 +01:00
result = ydl . extract_info ( infourl , download = False )
2021-07-04 01:52:30 +02:00
return result
2022-05-18 16:24:27 +02:00
@app.route ( " /dl/<path:sub_path> " ) # Download the tweets video, and rehost it
2022-01-18 00:36:43 +01:00
def dl ( sub_path ) :
2022-05-18 16:24:27 +02:00
print ( " ➤ [[ !!! TRYING TO DOWNLOAD FILE !!! ]] Downloading file from " + sub_path )
url = sub_path
2022-01-18 00:36:43 +01:00
match = pathregex . search ( url )
if match is not None :
twitter_url = url
if match . start ( ) == 0 :
twitter_url = " https://twitter.com/ " + url
2022-05-18 16:24:27 +02:00
mp4link = direct_video_link ( twitter_url )
filename = sub_path . split ( " / " ) [ - 1 ] . split ( " .mp4 " ) [ 0 ] + " .mp4 "
PATH = " ./static/ " + filename
2022-01-18 00:36:43 +01:00
if os . path . isfile ( PATH ) and os . access ( PATH , os . R_OK ) :
print ( " ➤ [[ FILE EXISTS ]] " )
else :
print ( " ➤ [[ FILE DOES NOT EXIST, DOWNLOADING... ]] " )
2022-05-18 16:24:27 +02:00
addToStat ( " downloads " )
2022-01-18 00:36:43 +01:00
mp4file = urllib . request . urlopen ( mp4link )
2022-05-23 02:12:31 +02:00
with open ( ( " /home/twitfix/Twxtter-main/static/ " + filename ) , " wb " ) as output :
2022-01-18 00:36:43 +01:00
output . write ( mp4file . read ( ) )
2022-05-18 16:24:27 +02:00
print (
" ➤ [[ PRESENTING FILE: "
+ filename
2022-05-19 14:00:36 +02:00
+ " , URL: https://twxtter.com/static/ "
2022-05-18 16:24:27 +02:00
+ filename
+ " ]] "
)
r = make_response (
send_file ( ( " static/ " + filename ) , mimetype = " video/mp4 " , max_age = 100 )
)
r . headers [ " Content-Type " ] = " video/mp4 "
r . headers [ " Sec-Fetch-Site " ] = " none "
r . headers [ " Sec-Fetch-User " ] = " ?1 "
2022-01-18 00:36:43 +01:00
return r
2022-05-18 16:24:27 +02:00
@app.route (
" /dir/<path:sub_path> "
) # Try to return a direct link to the MP4 on twitters servers
2021-07-21 23:36:05 +02:00
def dir ( sub_path ) :
2022-05-18 16:24:27 +02:00
user_agent = request . headers . get ( " user-agent " )
url = sub_path
2021-07-21 23:36:05 +02:00
match = pathregex . search ( url )
if match is not None :
twitter_url = url
if match . start ( ) == 0 :
twitter_url = " https://twitter.com/ " + url
2021-08-03 03:52:13 +02:00
if user_agent in generate_embed_user_agents :
2021-10-01 23:59:20 +02:00
res = embed_video ( twitter_url )
2021-07-21 23:36:05 +02:00
return res
2021-08-03 03:52:13 +02:00
2021-07-21 23:36:05 +02:00
else :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ R ] Redirect to direct MP4 URL " )
2021-07-21 23:36:05 +02:00
return direct_video ( twitter_url )
else :
return redirect ( url , 301 )
2022-05-18 16:24:27 +02:00
def direct_video ( video_link ) : # Just get a redirect to a MP4 link from any tweet link
2022-02-20 23:13:18 +01:00
cached_vnf = getVnfFromLinkCache ( video_link )
2021-07-21 23:36:05 +02:00
if cached_vnf == None :
try :
vnf = link_to_vnf ( video_link )
2022-02-20 23:13:18 +01:00
addVnfToLinkCache ( video_link , vnf )
2022-05-18 16:24:27 +02:00
return redirect ( vnf [ " url " ] , 301 )
print ( " ➤ [ D ] Redirecting to direct URL: " + vnf [ " url " ] )
2021-07-21 23:36:05 +02:00
except Exception as e :
print ( e )
return message ( " Failed to scan your link! " )
else :
2022-05-18 16:24:27 +02:00
return redirect ( cached_vnf [ " url " ] , 301 )
print ( " ➤ [ D ] Redirecting to direct URL: " + vnf [ " url " ] )
2022-01-18 00:36:43 +01:00
2022-05-18 16:24:27 +02:00
def direct_video_link (
video_link ,
) : # Just get a redirect to a MP4 link from any tweet link
2022-02-20 23:13:18 +01:00
cached_vnf = getVnfFromLinkCache ( video_link )
2022-01-18 00:36:43 +01:00
if cached_vnf == None :
try :
vnf = link_to_vnf ( video_link )
2022-02-20 23:13:18 +01:00
addVnfToLinkCache ( video_link , vnf )
2022-05-18 16:24:27 +02:00
return vnf [ " url " ]
print ( " ➤ [ D ] Redirecting to direct URL: " + vnf [ " url " ] )
2022-01-18 00:36:43 +01:00
except Exception as e :
print ( e )
return message ( " Failed to scan your link! " )
else :
2022-05-18 16:24:27 +02:00
return cached_vnf [ " url " ]
print ( " ➤ [ D ] Redirecting to direct URL: " + vnf [ " url " ] )
2021-07-21 23:36:05 +02:00
2022-03-07 13:52:39 +01:00
def addToStat ( stat ) :
2022-05-18 16:24:27 +02:00
# print(stat)
2022-03-07 13:52:39 +01:00
today = str ( date . today ( ) )
try :
2022-05-18 16:24:27 +02:00
collection = db . stats . find_one ( { " date " : today } )
delta = collection [ stat ] + 1
query = { " date " : today }
change = { " $set " : { stat : delta } }
out = db . stats . update_one ( query , change )
2022-03-07 13:52:39 +01:00
except :
2022-05-18 16:24:27 +02:00
collection = db . stats . insert_one (
{ " date " : today , " embeds " : 1 , " linksCached " : 1 , " api " : 1 , " downloads " : 1 }
)
2022-03-07 13:52:39 +01:00
def getStats ( day ) :
2022-05-18 16:24:27 +02:00
collection = db . stats . find_one ( { " date " : day } )
2022-03-07 13:52:39 +01:00
return collection
2022-05-18 16:24:27 +02:00
def embed_video ( video_link , image = 0 , raw = False ) : # Return Embed from any tweet link
2022-02-20 23:13:18 +01:00
cached_vnf = getVnfFromLinkCache ( video_link )
2021-07-08 05:17:23 +02:00
2021-07-12 10:49:07 +02:00
if cached_vnf == None :
2021-07-08 05:17:23 +02:00
try :
2021-07-13 19:19:24 +02:00
vnf = link_to_vnf ( video_link )
2022-02-20 23:13:18 +01:00
addVnfToLinkCache ( video_link , vnf )
2022-05-18 16:24:27 +02:00
return embed ( video_link , vnf , image , raw )
2021-07-21 23:36:05 +02:00
2021-07-12 10:49:07 +02:00
except Exception as e :
print ( e )
2021-07-14 10:33:46 +02:00
return message ( " Failed to scan your link! " )
2021-07-12 10:49:07 +02:00
else :
2022-05-18 16:24:27 +02:00
return embed ( video_link , cached_vnf , image , raw )
def tweetInfo (
2022-05-20 20:41:49 +02:00
url = " " ,
2022-05-18 16:24:27 +02:00
tweet = " " ,
desc = " " ,
thumb = " " ,
uploader = " " ,
screen_name = " " ,
pfp = " " ,
tweetType = " " ,
images = " " ,
hits = 0 ,
likes = 0 ,
rts = 0 ,
time = " " ,
qrt = { } ,
nsfw = False ,
) : # Return a dict of video info with default values
2021-07-05 18:02:17 +02:00
vnf = {
2022-05-18 16:24:27 +02:00
" tweet " : tweet ,
" url " : url ,
" description " : desc ,
" thumbnail " : thumb ,
" uploader " : uploader ,
" screen_name " : screen_name ,
" pfp " : pfp ,
" type " : tweetType ,
" images " : images ,
" hits " : hits ,
" likes " : likes ,
" rts " : rts ,
" time " : time ,
" qrt " : qrt ,
" nsfw " : nsfw ,
2021-07-05 18:02:17 +02:00
}
return vnf
2022-05-18 16:24:27 +02:00
2021-07-13 19:19:24 +02:00
def link_to_vnf_from_api ( video_link ) :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ + ] Attempting to download tweet info from Twitter API " )
2022-05-18 16:24:27 +02:00
twid = int (
re . sub ( r " \ ?.*$ " , " " , video_link . rsplit ( " / " , 1 ) [ - 1 ] )
) # gets the tweet ID as a int from the passed url
2021-07-12 20:49:19 +02:00
tweet = twitter_api . statuses . show ( _id = twid , tweet_mode = " extended " )
2022-05-11 01:40:09 +02:00
# For when I need to poke around and see what a tweet looks like
2022-05-18 16:24:27 +02:00
# print(tweet)
2022-05-19 14:13:28 +02:00
text = tweet [ " full_text " ]
nsfw = tweet . get ( " possibly_sensitive " , False )
qrt = { }
2022-05-18 16:24:27 +02:00
imgs = [ " " , " " , " " , " " , " " ]
2022-02-20 23:13:18 +01:00
print ( " ➤ [ + ] Tweet Type: " + tweetType ( tweet ) )
2021-07-15 02:15:06 +02:00
# Check to see if tweet has a video, if not, make the url passed to the VNF the first t.co link in the tweet
2022-02-20 23:13:18 +01:00
if tweetType ( tweet ) == " Video " :
2022-05-18 16:24:27 +02:00
if tweet [ " extended_entities " ] [ " media " ] [ 0 ] [ " video_info " ] [ " variants " ] :
2022-03-20 01:37:28 +01:00
best_bitrate = 0
2022-05-18 16:24:27 +02:00
thumb = tweet [ " extended_entities " ] [ " media " ] [ 0 ] [ " media_url " ]
for video in tweet [ " extended_entities " ] [ " media " ] [ 0 ] [ " video_info " ] [
" variants "
] :
if (
video [ " content_type " ] == " video/mp4 "
and video [ " bitrate " ] > best_bitrate
) :
url = video [ " url " ]
2022-05-26 21:15:50 +02:00
best_bitrate = video [ ' bitrate ' ]
2022-02-20 23:13:18 +01:00
elif tweetType ( tweet ) == " Text " :
2022-05-18 16:24:27 +02:00
url = " "
2022-02-20 23:13:18 +01:00
thumb = " "
2021-07-12 20:49:19 +02:00
else :
2022-05-18 16:24:27 +02:00
imgs = [ " " , " " , " " , " " , " " ]
2022-03-07 13:52:39 +01:00
i = 0
2022-05-18 16:24:27 +02:00
for media in tweet [ " extended_entities " ] [ " media " ] :
imgs [ i ] = media [ " media_url_https " ]
2022-03-07 13:52:39 +01:00
i = i + 1
2022-05-18 16:24:27 +02:00
# print(imgs)
2022-05-11 01:40:09 +02:00
imgs [ 4 ] = str ( i )
2022-05-18 16:24:27 +02:00
url = " "
images = imgs
thumb = tweet [ " extended_entities " ] [ " media " ] [ 0 ] [ " media_url_https " ]
2021-07-12 23:41:07 +02:00
2022-05-18 16:24:27 +02:00
if " quoted_status " in tweet :
qrt [ " desc " ] = tweet [ " quoted_status " ] [ " full_text " ]
qrt [ " handle " ] = tweet [ " quoted_status " ] [ " user " ] [ " name " ]
qrt [ " screen_name " ] = tweet [ " quoted_status " ] [ " user " ] [ " screen_name " ]
2022-02-20 23:13:18 +01:00
2022-05-18 16:24:27 +02:00
text = tweet [ " full_text " ]
2022-02-20 23:13:18 +01:00
2022-05-18 16:24:27 +02:00
if " possibly_sensitive " in tweet :
nsfw = tweet [ " possibly_sensitive " ]
2022-05-11 01:40:09 +02:00
else :
nsfw = False
vnf = tweetInfo (
2022-05-18 16:24:27 +02:00
url ,
video_link ,
text ,
thumb ,
tweet [ " user " ] [ " name " ] ,
tweet [ " user " ] [ " screen_name " ] ,
tweet [ " user " ] [ " profile_image_url " ] ,
tweetType ( tweet ) ,
likes = tweet [ " favorite_count " ] ,
rts = tweet [ " retweet_count " ] ,
time = tweet [ " created_at " ] ,
qrt = qrt ,
2022-05-11 01:40:09 +02:00
images = imgs ,
2022-05-18 16:24:27 +02:00
nsfw = nsfw ,
)
2021-07-12 20:49:19 +02:00
return vnf
2022-05-18 16:24:27 +02:00
2021-07-13 19:19:24 +02:00
def link_to_vnf_from_youtubedl ( video_link ) :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] Attempting to download tweet info via YoutubeDL: " + video_link )
2022-05-18 16:24:27 +02:00
with youtube_dl . YoutubeDL ( { " outtmpl " : " %(id)s . %(ext)s " } ) as ydl :
2021-07-13 19:19:24 +02:00
result = ydl . extract_info ( video_link , download = False )
2022-05-18 16:24:27 +02:00
vnf = tweetInfo (
result [ " url " ] ,
video_link ,
result [ " description " ] . rsplit ( " " , 1 ) [ 0 ] ,
result [ " thumbnail " ] ,
result [ " uploader " ] ,
)
2021-07-12 10:49:07 +02:00
return vnf
2021-07-12 20:49:19 +02:00
2022-05-18 16:24:27 +02:00
def link_to_vnf ( video_link ) : # Return a VideoInfo object or die trying
if config [ " config " ] [ " method " ] == " hybrid " :
2021-07-12 20:49:19 +02:00
try :
2021-07-13 19:19:24 +02:00
return link_to_vnf_from_api ( video_link )
2021-07-12 21:49:29 +02:00
except Exception as e :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ !!! ] API Failed " )
2021-07-12 21:49:29 +02:00
print ( e )
2021-07-13 19:19:24 +02:00
return link_to_vnf_from_youtubedl ( video_link )
2022-05-18 16:24:27 +02:00
elif config [ " config " ] [ " method " ] == " api " :
2021-07-12 20:49:19 +02:00
try :
2021-07-13 19:19:24 +02:00
return link_to_vnf_from_api ( video_link )
2021-07-12 20:49:19 +02:00
except Exception as e :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] API Failed " )
2021-07-12 20:49:19 +02:00
print ( e )
return None
2022-05-18 16:24:27 +02:00
elif config [ " config " ] [ " method " ] == " youtube-dl " :
2021-07-12 20:49:19 +02:00
try :
2021-07-13 19:19:24 +02:00
return link_to_vnf_from_youtubedl ( video_link )
2021-07-12 20:49:19 +02:00
except Exception as e :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] Youtube-DL Failed " )
2021-07-12 20:49:19 +02:00
print ( e )
return None
else :
2022-05-18 16:24:27 +02:00
print (
" Please set the method key in your config file to ' api ' ' youtube-dl ' or ' hybrid ' "
)
2021-07-12 20:49:19 +02:00
return None
2022-05-18 16:24:27 +02:00
2022-02-20 23:13:18 +01:00
def getVnfFromLinkCache ( video_link ) :
2021-07-12 10:49:07 +02:00
if link_cache_system == " db " :
collection = db . linkCache
2022-05-18 16:24:27 +02:00
vnf = collection . find_one ( { " tweet " : video_link } )
2022-02-20 23:13:18 +01:00
# print(vnf)
2022-05-18 16:24:27 +02:00
if vnf != None :
hits = vnf [ " hits " ] + 1
print (
" ➤ [ ✔ ] Link located in DB cache. "
+ " hits on this link so far: [ "
+ str ( hits )
+ " ] "
)
query = { " tweet " : video_link }
change = { " $set " : { " hits " : hits } }
out = db . linkCache . update_one ( query , change )
addToStat ( " embeds " )
2021-07-12 10:49:07 +02:00
return vnf
else :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] Link not in DB cache " )
2021-07-12 10:49:07 +02:00
return None
elif link_cache_system == " json " :
2021-07-13 19:18:50 +02:00
if video_link in link_cache :
2021-07-12 10:49:07 +02:00
print ( " Link located in json cache " )
2021-07-13 19:18:50 +02:00
vnf = link_cache [ video_link ]
2021-07-12 10:49:07 +02:00
return vnf
else :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] Link not in json cache " )
2021-07-12 10:49:07 +02:00
return None
2022-05-18 16:24:27 +02:00
2022-02-20 23:13:18 +01:00
def addVnfToLinkCache ( video_link , vnf ) :
2021-07-12 10:49:07 +02:00
if link_cache_system == " db " :
try :
out = db . linkCache . insert_one ( vnf )
2022-01-18 00:36:43 +01:00
print ( " ➤ [ + ] Link added to DB cache " )
2022-05-18 16:24:27 +02:00
addToStat ( " linksCached " )
2021-07-12 10:49:07 +02:00
return True
except Exception :
2022-01-18 00:36:43 +01:00
print ( " ➤ [ X ] Failed to add link to DB cache " )
2021-07-12 10:49:07 +02:00
return None
elif link_cache_system == " json " :
2021-07-13 19:18:50 +02:00
link_cache [ video_link ] = vnf
2022-05-18 16:24:27 +02:00
with open ( " links.json " , " w " ) as outfile :
2021-07-12 10:49:07 +02:00
json . dump ( link_cache , outfile , indent = 4 , sort_keys = True )
return None
2022-05-18 16:24:27 +02:00
2021-07-14 10:33:46 +02:00
def message ( text ) :
2022-02-20 23:13:18 +01:00
return render_template (
2022-05-18 16:24:27 +02:00
" default.html " ,
message = text ,
color = config [ " config " ] [ " color " ] ,
appname = config [ " config " ] [ " appname " ] ,
repo = config [ " config " ] [ " repo " ] ,
url = config [ " config " ] [ " url " ] ,
)
def embed ( video_link , vnf , image , raw = False ) :
print ( " ➤ [ E ] Embedding " + vnf [ " type " ] + " : " + vnf [ " url " ] )
desc = re . sub ( r " http.*t \ .co \ S+ " , " " , vnf [ " description " ] )
urlUser = urllib . parse . quote ( vnf [ " uploader " ] )
2022-02-20 23:13:18 +01:00
urlDesc = urllib . parse . quote ( desc )
urlLink = urllib . parse . quote ( video_link )
2022-05-18 16:24:27 +02:00
likeDisplay = " \n \n 💖 " + str ( vnf [ " likes " ] ) + " 🔁 " + str ( vnf [ " rts " ] ) + " \n "
2022-05-27 00:54:48 +02:00
imagecount = " Twitter "
2022-05-18 16:24:27 +02:00
2022-02-20 23:13:18 +01:00
try :
2022-05-18 16:24:27 +02:00
if vnf [ " type " ] == " " :
2022-05-11 01:40:09 +02:00
desc = desc
2022-05-18 16:24:27 +02:00
elif vnf [ " type " ] == " Video " :
2022-02-20 23:13:18 +01:00
desc = desc
2022-05-18 16:24:27 +02:00
elif vnf [ " qrt " ] == { } : # Check if this is a QRT and modify the description
desc = desc + likeDisplay
2022-02-20 23:13:18 +01:00
else :
2022-05-18 16:24:27 +02:00
qrtDisplay = (
" \n ───────────── \n ➤ QRT of "
+ vnf [ " qrt " ] [ " handle " ]
+ " (@ "
+ vnf [ " qrt " ] [ " screen_name " ]
+ " ): \n ───────────── \n ' "
+ vnf [ " qrt " ] [ " desc " ]
+ " ' "
)
desc = desc + qrtDisplay + likeDisplay
2022-02-20 23:13:18 +01:00
except :
2022-05-18 16:24:27 +02:00
vnf [ " likes " ] = 0
vnf [ " rts " ] = 0
vnf [ " time " ] = 0
print ( " ➤ [ X ] Failed QRT check - old VNF object " )
2022-05-27 00:54:48 +02:00
2022-05-18 16:24:27 +02:00
if vnf [ " type " ] == " Text " : # Change the template based on tweet type
template = " text.html "
if vnf [ " type " ] == " Image " :
image = vnf [ " images " ] [ image ]
2022-05-27 00:54:48 +02:00
if vnf [ " images " ] [ 4 ] != " 1 " :
imagecount = " Twitter ( " + vnf [ " images " ] [ 4 ] + " images in post) "
2022-05-18 16:24:27 +02:00
if raw == True :
template = " img.html "
else :
template = " image.html "
if vnf [ " type " ] == " Video " :
urlDesc = urllib . parse . quote (
textwrap . shorten ( desc , width = 220 , placeholder = " ... " )
)
template = " video.html "
if vnf [ " type " ] == " " :
urlDesc = urllib . parse . quote (
textwrap . shorten ( desc , width = 220 , placeholder = " ... " )
)
template = " video.html "
2022-05-18 22:53:30 +02:00
color = config [ ' config ' ] [ ' color ' ]
2022-05-18 16:24:27 +02:00
if vnf [ " nsfw " ] == True :
2022-05-19 14:19:06 +02:00
color = " #800020 " # Red
2022-05-27 00:54:48 +02:00
2022-02-20 23:13:18 +01:00
return render_template (
2022-05-18 16:24:27 +02:00
template ,
likes = vnf [ " likes " ] ,
rts = vnf [ " rts " ] ,
time = vnf [ " time " ] ,
screenName = vnf [ " screen_name " ] ,
vidlink = vnf [ " url " ] ,
pfp = vnf [ " pfp " ] ,
vidurl = vnf [ " url " ] ,
desc = desc ,
pic = image ,
2022-05-27 00:54:48 +02:00
imagecount = imagecount ,
2022-05-18 16:24:27 +02:00
user = vnf [ " uploader " ] ,
video_link = video_link ,
color = color ,
2022-05-27 00:54:48 +02:00
appname = config [ ' config ' ] [ ' appname ' ] ,
2022-05-18 16:24:27 +02:00
repo = config [ " config " ] [ " repo " ] ,
url = config [ " config " ] [ " url " ] ,
urlDesc = urlDesc ,
urlUser = urlUser ,
urlLink = urlLink ,
2022-05-26 21:15:50 +02:00
tweetLink = vnf [ ' tweet ' ]
2022-05-18 16:24:27 +02:00
)
def tweetType ( tweet ) : # Are we dealing with a Video, Image, or Text tweet?
if " extended_entities " in tweet :
if " video_info " in tweet [ " extended_entities " ] [ " media " ] [ 0 ] :
2022-02-20 23:13:18 +01:00
out = " Video "
else :
out = " Image "
2021-07-15 02:15:06 +02:00
else :
2022-02-20 23:13:18 +01:00
out = " Text "
return out
2021-07-12 10:49:07 +02:00
2022-02-20 23:13:18 +01:00
def oEmbedGen ( description , user , video_link , ttype ) :
2021-07-09 12:32:04 +02:00
out = {
2022-05-18 16:24:27 +02:00
" type " : ttype ,
" version " : " 1.0 " ,
" provider_name " : config [ " config " ] [ " appname " ] ,
" provider_url " : config [ " config " ] [ " repo " ] ,
" title " : description ,
" author_name " : user ,
" author_url " : video_link ,
}
2021-07-09 12:32:04 +02:00
return out
2022-05-18 16:24:27 +02:00
2021-07-04 01:52:30 +02:00
if __name__ == " __main__ " :
2022-08-02 05:49:10 +02:00
port = " 80 "
if " PORT " in os . environ :
port = os . environ [ " PORT " ]
app . config [ " SERVER_NAME " ] = " localhost: " + port
app . run ( host = " 0.0.0.0 " , port = port )