BetterTwitFix/twitfix.py
Dylan 10ec46a0cf Squashed commit of the following:
commit 12bb37ef0a430ab30d0ec3e06d7f58d42b1332b9
Author: Dylan <dylanpdx@gmail.com>
Date:   Mon Sep 12 17:31:22 2022 +0100

    Increase memory to 1GB

commit bbdd0f85c500413f765b44cdb8643539a01b3968
Author: Dylan <dylanpdx@gmail.com>
Date:   Thu Sep 8 23:19:41 2022 +0100

    Reduced memory to 256mb

commit 5fa80151a09092a7206ffd294ef464650b776ba8
Author: Dylan <dylanpdx@gmail.com>
Date:   Thu Sep 8 23:10:57 2022 +0100

    Updated serverless code

commit 42f826908ac82a91e67312ae6270ce6ac9bfd216
Author: Dylan <dylanpdx@gmail.com>
Date:   Thu Sep 8 19:33:39 2022 +0100

    Squashed commit of the following:

    commit 27accd7a5a601bd77a3c13832e92ebbd0571b7b5
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Sep 1 23:54:15 2022 +0100

        Documented apiMirrors

    commit bf46166b027133719ebbd5d1ed46dcae8d242190
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Sep 1 22:58:53 2022 +0100

        Fixed incorrect log print 😅

    commit a05ff596e057c774a77fa20381370a1baa60cf20
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Sep 1 22:52:20 2022 +0100

        Fix #14

    commit 1954a72bd070d213f539c8a8260b6de9c98a0a59
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Sep 1 22:33:07 2022 +0100

        API Mirror

    commit c58e9b27554d00c4545a70f62a9b5a7c132563d5
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Sep 1 21:38:42 2022 +0100

        Updated requirements

    commit a51876a82943567107ef9c777cfa5ffd9c89f060
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sat Jul 23 16:34:36 2022 +0100

        Fix twitter api failing for direct video linking

    commit d97f70b22494899417cc5fc8744e1d4ea6d22f9e
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sat Jul 23 16:29:39 2022 +0100

        Added option to disable cache

    commit 2da2cd52005ac7e7a2827a56cfb894b2294a769c
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Fri Jul 22 00:57:07 2022 +0100

        Removed image scale-down at the end of processing

    commit b0547ce8107979bac0b32946490f1a4e578cb8fb
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun Jul 17 14:00:19 2022 +0100

        Same fix as last commit

    commit 1ec59bce2ea8a6543854fe24e4eeb738f4499794
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun Jul 17 13:54:59 2022 +0100

        Fixed direct linking on Discord

    commit 2a77ff96b05dabc916ad4b516897d68477c5d41f
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed Jul 6 19:43:22 2022 +0100

        Separated code into different files

    commit 973c31f705f8e5f86416e3607c8c75fe6c0cc1c1
    Merge: e28789a b27296e
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Jun 9 14:35:33 2022 +0100

        Merge pull request #7 from flifloo/main

        Add Docker image and compose with documentation

    commit b27296e317e0eec87245fba2a58a38fdcb09e8ec
    Author: flifloo <flifloo@gmail.com>
    Date:   Tue Jun 7 11:46:39 2022 +0200

        Add Docker image and compose with documentation

    commit 40f4770e177e4003f12758b9f75c3373e56562c2
    Author: flifloo <flifloo@gmail.com>
    Date:   Tue Jun 7 10:44:52 2022 +0200

        Fix links.json creation

    commit e28789a09dbb6efd36827a3fd47e0132666a9609
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun Jun 5 13:36:18 2022 +0100

        Added og:description on videos for Telegram users

    commit faed7482f4b3d09a5656bf5d2a078018bd223e94
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun Jun 5 02:10:54 2022 +0100

        Update requirements.txt

    commit a5b2eb6cd0472d83a8b0f37ddfa2b358cb5d31f4
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sat Jun 4 13:53:14 2022 +0100

        Restore old behavior of showing error message

    commit 16dfc509d32fe6667a43f9008daf9031bf93a1c4
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Fri Jun 3 23:27:33 2022 +0100

        Small log message when an unofficial API call succeeds

    commit 174a20d8961728f45041de00ae1b8e3c30fb2d5d
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Jun 2 17:07:48 2022 +0100

        Use unofficial API when official API fails

    commit e64dee93498620b0c95b305d72178f23e506dfb6
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu Jun 2 01:09:04 2022 +0100

        Temporary redirect for failing links due to API ratelimiting

    commit f0f709f62ec40dd3138bde5cff7b027f82d82eb6
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed Jun 1 21:52:47 2022 +0100

        use yt-dlp

    commit 0f77b2e0a466a09dabbc4eff33b4ce8517fc8148
    Merge: 285c8f7 ffcde09
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed Jun 1 19:26:22 2022 +0000

        Merge branch 'main' of github.com:dylanpdx/BetterTwitFix

    commit 285c8f70f84d5efe96d50a9e239c7e460be5cd38
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed Jun 1 19:25:23 2022 +0000

        Service auto-restart

    commit ffcde09b976dd85630da73cb4c804b86c63fb760
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Mon May 30 01:25:53 2022 +0100

        Return HTTP 400 if missing query parameters

    commit 891db049af0de4d40df3eaabe67d12952a036224
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Fri May 27 23:18:58 2022 +0100

        Fixed incorrect TTL being set

    commit 797b670d0e968075cb98de37b1439c59c4470caa
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 18:11:30 2022 +0100

        Do not combine single image tweets

    commit 728785278b549d467e51e6f8fb4492bc5db71cf9
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 16:48:24 2022 +0100

        Fixed direct video linking

    commit ec1f4dc2845956e4237610e29ebad65b544ea095
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 16:13:31 2022 +0100

        Image check in lambda handler

    commit 77b972a1b2069e039c5067eb7dd2e95bcb2acea1
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 16:10:29 2022 +0100

        Switched to using ThreadPoolExecutor

    commit 4feb1b65d6ec0fcff2854b7080f8995e6ae77fe1
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 15:01:12 2022 +0100

        Optimized image downloads

    commit 2819b504a36f7b39d61640b87cb0a81e804e36a7
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 02:01:43 2022 +0100

        Add quality argument

    commit 2399788fdc0d8a10063f30c448462e9caa79997f
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 01:29:47 2022 +0100

        Misc code cleanup; Fixed Discord not embedding 301 redirects issue

    commit 7a021916f0c9e8404646104258394e764080bffc
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Thu May 26 00:58:28 2022 +0100

        Use proper mime type

    commit a06a89825cf29d07787286d4837dfcb7a0151760
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 23:56:48 2022 +0100

        Fixed formatting

    commit 6717fd464aaa934724ea9c9f6022402013c7238b
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 23:55:41 2022 +0100

        Documented combination_method

    commit 022edb5122ddef8a8b3f84ffe84b1b2e4cf8130f
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 23:48:55 2022 +0100

        Moved combineImg to own module; created AWS dockerfile; added combination_method config

    commit d7e0cb90898ac1aacfbe600f6dd1d8137185791a
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 17:28:03 2022 +0100

        Changed scaling method

    commit 55ea554357aca026926e1d296fb5ac259458c1d3
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 16:30:51 2022 +0100

        Re-scale image down

    commit b457760fae648c3cad5e383717f4df28c4539303
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 01:20:27 2022 +0100

        Use JPEG for final combined img

    commit 39a9ffd5120f7d28711e071290ee00d495a477ef
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 25 00:49:22 2022 +0100

        Method to embed combined tweet images. WIP & may be CPU intensive

    commit a6efd26447efa9b54e02c48bda9b58b038236ae0
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Mon May 23 20:33:11 2022 +0100

        Oops- Updated other two error msgs

    commit e81beb975ce319ccc95fa8db9e3e39927e205929
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Mon May 23 19:02:54 2022 +0100

        Improved Fail link message

    commit 55c956ad4ec7fe0ef0cbfb0e740bfcb1783aa88f
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 22 15:19:28 2022 +0100

        Error checking for loading cache json file

    commit 014f9d0e8d94da562f834665741adda744c0b42c
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 22 15:19:17 2022 +0100

        TTL for cache entries

    commit f640ac69a1ba7f19ad69f6fea2199bec696d5eaf
    Merge: 7eff745 08d686c
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sat May 21 16:13:26 2022 +0100

        Merge pull request #2 from cabiste69/patch-1

        Minor cleaning

    commit 08d686c652a5edbf8252771c30a53743b5541f33
    Author: cabiste <ahmedjj354@gmail.com>
    Date:   Fri May 20 23:14:58 2022 +0100

        minor cleaning

    commit 7eff745550631e2d0ad014de9e941151bb5a8b61
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Wed May 18 18:23:11 2022 +0100

        Clarifications in readme

    commit 8df211090c4d73c812b76dba65c6dc180c848300
    Merge: 5a2a4e6 681f191
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Tue May 17 17:14:30 2022 +0100

        Merge pull request #1 from Fam0r/main

        Use the best bitrate video for twitter embeds

    commit 5a2a4e6316dfd1369b7a428ed4c9fcb9fe794de5
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Tue May 17 14:55:49 2022 +0100

        Display how many total images in the post

    commit 681f19150219c22a22999ac1e41dc5d393d5b9b5
    Author: Fam0r <fam0r@mailbox.org>
    Date:   Tue May 17 09:40:40 2022 +0300

        Use the best bitrate video for twitter embeds

    commit 82fe66af48c5dc4f8d933f2874f8bae815e7dd05
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Mon May 16 20:18:18 2022 +0100

        Work in progress code for combining up to 4 images

    commit fba1256efd3ce539f716e2f23b45c122c8422759
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 15 01:42:22 2022 +0100

        Fixed infinite redirect(?)

    commit 85c682ab111f575c8c517117f456cf354e9f5bf7
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 15 01:15:15 2022 +0100

        Updated gitignore

commit f2eefbe5a542f1a6fa8404228c2a9425054765a8
Author: Dylan <dylanpdx@gmail.com>
Date:   Wed May 18 18:21:00 2022 +0100

    Fixed DynamoDB integration

commit 32e5376b987c732147ab3289c2c42a8afde19e2f
Author: Dylan <dylanpdx@gmail.com>
Date:   Mon May 16 19:28:31 2022 +0100

    DynamoDB support & Serverless config update

commit dee62f812a6c8da443b111c7f3dd24bcb4d76525
Author: Dylan <dylanpdx@gmail.com>
Date:   Sun May 15 17:06:42 2022 +0100

    Updated serverless.yml to dockerize pip packages

commit 43d69a1d872c293a4b70d2a330261245783eb924
Author: Dylan <dylanpdx@gmail.com>
Date:   Sun May 15 14:30:18 2022 +0100

    serverless wsgi files

commit 96246aa9217774ddc88275ffba5bf1790317eda9
Author: Dylan <dylanpdx@gmail.com>
Date:   Sun May 15 01:47:20 2022 +0100

    Squashed commit of the following:

    commit fba1256efd3ce539f716e2f23b45c122c8422759
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 15 01:42:22 2022 +0100

        Fixed infinite redirect(?)

    commit 85c682ab111f575c8c517117f456cf354e9f5bf7
    Author: Dylan <dylanpdx@gmail.com>
    Date:   Sun May 15 01:15:15 2022 +0100

        Updated gitignore

commit 2c9563fafe97bfe7782386aaa04372dcac5f8136
Author: Dylan <dylanpdx@gmail.com>
Date:   Sun May 15 01:14:41 2022 +0100

    Serverless config
2022-09-12 17:36:11 +01:00

566 lines
22 KiB
Python

from random import Random, random
from weakref import finalize
from flask import Flask, render_template, request, redirect, abort, Response, send_from_directory, url_for, send_file, make_response, jsonify
from flask_cors import CORS
import yt_dlp
import textwrap
import twitter
import requests
import re
import os
import urllib.parse
import urllib.request
import combineImg
from datetime import date,datetime, timedelta
from io import BytesIO
import msgs
import twExtract as twExtract
from configHandler import config
from cache import addVnfToLinkCache,getVnfFromLinkCache
import random
app = Flask(__name__)
CORS(app)
pathregex = re.compile("\\w{1,15}\\/(status|statuses)\\/\\d{2,20}")
generate_embed_user_agents = [
"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",
"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",
"facebookexternalhit/1.1",
"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"]
# If method is set to API or Hybrid, attempt to auth with the Twitter API
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'])
twitter_api = twitter.Twitter(auth=auth)
@app.route('/') # If the useragent is discord, return the embed, if not, redirect to configured repo directly
def default():
user_agent = request.headers.get('user-agent')
if user_agent in generate_embed_user_agents:
return message("TwitFix is an attempt to fix twitter video embeds in discord! created by Robin Universe :)\n\n💖\n\nClick me to be redirected to the repo!")
else:
return redirect(config['config']['repo'], 301)
@app.route('/oembed.json') #oEmbed endpoint
def oembedend():
desc = request.args.get("desc", None)
user = request.args.get("user", None)
link = request.args.get("link", None)
ttype = request.args.get("ttype", None)
return oEmbedGen(desc, user, link, ttype)
@app.route('/<path:sub_path>') # Default endpoint used by everything
def twitfix(sub_path):
user_agent = request.headers.get('user-agent')
match = pathregex.search(sub_path)
print(request.url)
if request.url.startswith("https://d.vx"): # Matches d.fx? Try to give the user a direct link
if match.start() == 0:
twitter_url = "https://twitter.com/" + sub_path
if user_agent in generate_embed_user_agents:
print( " ➤ [ D ] d.vx link shown to discord user-agent!")
if request.url.endswith(".mp4") and "?" not in request.url:
# TODO: Cache this, but not for too long as disk space can fill up
if "?" not in request.url:
clean = twitter_url[:-4]
else:
clean = twitter_url
vid = requests.get(direct_video_link(clean))
return Response(vid.content,mimetype="video/mp4")
else:
return message("To use a direct MP4 link in discord, remove anything past '?' and put '.mp4' at the end")
else:
print(" ➤ [ R ] Redirect to MP4 using d.fxtwitter.com")
return dir(sub_path)
elif request.url.startswith("https://c.vx"):
twitter_url = sub_path
if match.start() == 0:
twitter_url = "https://twitter.com/" + sub_path
if user_agent in generate_embed_user_agents:
return embedCombined(twitter_url)
else:
print(" ➤ [ R ] Redirect to " + twitter_url)
return redirect(twitter_url, 301)
elif request.url.endswith(".mp4") or request.url.endswith("%2Emp4"):
twitter_url = "https://twitter.com/" + sub_path
if "?" not in request.url:
clean = twitter_url[:-4]
else:
clean = twitter_url
# TODO: Cache this, but not for too long as disk space can fill up
vid = requests.get(direct_video_link(clean))
return Response(vid.content,mimetype="video/mp4")
# elif request.url.endswith(".json") or request.url.endswith("%2Ejson"):
# twitter_url = "https://twitter.com/" + sub_path
# if "?" not in request.url:
# clean = twitter_url[:-5]
# else:
# clean = twitter_url
# print( " ➤ [ API ] VNF Json api hit!")
# vnf = link_to_vnf_from_api(clean.replace(".json",""))
# if user_agent in generate_embed_user_agents:
# return message("VNF Data: ( discord useragent preview )\n\n"+ json.dumps(vnf, default=str))
# else:
# return Response(response=json.dumps(vnf, default=str), status=200, mimetype="application/json")
elif request.url.endswith("/1") or request.url.endswith("/2") or request.url.endswith("/3") or request.url.endswith("/4") or request.url.endswith("%2F1") or request.url.endswith("%2F2") or request.url.endswith("%2F3") or request.url.endswith("%2F4"):
twitter_url = "https://twitter.com/" + sub_path
if "?" not in request.url:
clean = twitter_url[:-2]
else:
clean = twitter_url
image = ( int(request.url[-1]) - 1 )
return embed_video(clean, image)
if match is not None:
twitter_url = sub_path
if match.start() == 0:
twitter_url = "https://twitter.com/" + sub_path
if user_agent in generate_embed_user_agents:
res = embed_video(twitter_url)
return res
else:
print(" ➤ [ R ] Redirect to " + twitter_url)
return redirect(twitter_url, 301)
else:
return message("This doesn't appear to be a twitter URL")
@app.route('/dir/<path:sub_path>') # Try to return a direct link to the MP4 on twitters servers
def dir(sub_path):
user_agent = request.headers.get('user-agent')
url = sub_path
match = pathregex.search(url)
if match is not None:
twitter_url = url
if match.start() == 0:
twitter_url = "https://twitter.com/" + url
if user_agent in generate_embed_user_agents:
res = embed_video(twitter_url)
return res
else:
print(" ➤ [ R ] Redirect to direct MP4 URL")
return direct_video(twitter_url)
else:
return redirect(url, 301)
@app.route('/favicon.ico') # This shit don't work
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico',mimetype='image/vnd.microsoft.icon')
@app.route("/rendercombined.jpg")
def rendercombined():
# get "imgs" from request arguments
imgs = request.args.get("imgs", "")
if 'combination_method' in config['config'] and config['config']['combination_method'] != "local":
url = config['config']['combination_method'] + "/rendercombined.jpg?imgs=" + imgs
return redirect(url, 302)
# Redirecting here instead of setting the embed URL directly to this because if the config combination_method changes in the future, old URLs will still work
imgs = imgs.split(",")
if (len(imgs) == 0 or len(imgs)>4):
abort(400)
#check that each image starts with "https://pbs.twimg.com"
for img in imgs:
if not img.startswith("https://pbs.twimg.com"):
abort(400)
finalImg= combineImg.genImageFromURL(imgs)
imgIo = BytesIO()
finalImg = finalImg.convert("RGB")
finalImg.save(imgIo, 'JPEG',quality=70)
imgIo.seek(0)
return send_file(imgIo, mimetype='image/jpeg')
def getDefaultTTL():
return datetime.today().replace(microsecond=0) + timedelta(days=1)
def direct_video(video_link): # Just get a redirect to a MP4 link from any tweet link
cached_vnf = getVnfFromLinkCache(video_link)
if cached_vnf == None:
try:
vnf = link_to_vnf(video_link)
addVnfToLinkCache(video_link, vnf)
return redirect(vnf['url'], 301)
print(" ➤ [ D ] Redirecting to direct URL: " + vnf['url'])
except Exception as e:
print(e)
return message(msgs.failedToScan)
else:
return redirect(cached_vnf['url'], 301)
print(" ➤ [ D ] Redirecting to direct URL: " + vnf['url'])
def direct_video_link(video_link): # Just get a redirect to a MP4 link from any tweet link
cached_vnf = getVnfFromLinkCache(video_link)
if cached_vnf == None:
try:
vnf = link_to_vnf(video_link)
addVnfToLinkCache(video_link, vnf)
return vnf['url']
print(" ➤ [ D ] Redirecting to direct URL: " + vnf['url'])
except Exception as e:
print(e)
return message(msgs.failedToScan)
else:
return cached_vnf['url']
print(" ➤ [ D ] Redirecting to direct URL: " + vnf['url'])
def embed_video(video_link, image=0): # Return Embed from any tweet link
cached_vnf = getVnfFromLinkCache(video_link)
if cached_vnf == None:
try:
vnf = link_to_vnf(video_link)
addVnfToLinkCache(video_link, vnf)
return embed(video_link, vnf, image)
except Exception as e:
print(e)
return message(msgs.failedToScan)
else:
return embed(video_link, cached_vnf, image)
def tweetInfo(url, tweet="", desc="", thumb="", uploader="", screen_name="", pfp="", tweetType="", images="", hits=0, likes=0, rts=0, time="", qrt={}, nsfw=False,ttl=None): # Return a dict of video info with default values
if (ttl==None):
ttl = getDefaultTTL()
vnf = {
"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,
"ttl" : ttl
}
return vnf
def get_tweet_data_from_api(video_link):
print(" ➤ [ + ] Attempting to download tweet info from Twitter API")
twid = int(re.sub(r'\?.*$','',video_link.rsplit("/", 1)[-1])) # gets the tweet ID as a int from the passed url
tweet = twitter_api.statuses.show(_id=twid, tweet_mode="extended")
#print(tweet) # For when I need to poke around and see what a tweet looks like
return tweet
def link_to_vnf_from_tweet_data(tweet,video_link):
imgs = ["","","","", ""]
print(" ➤ [ + ] Tweet Type: " + tweetType(tweet))
# 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
if tweetType(tweet) == "Video":
if tweet['extended_entities']['media'][0]['video_info']['variants']:
best_bitrate = 0
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']
best_bitrate = video['bitrate']
elif tweetType(tweet) == "Text":
url = ""
thumb = ""
else:
imgs = ["","","","", ""]
i = 0
for media in tweet['extended_entities']['media']:
imgs[i] = media['media_url_https']
i = i + 1
#print(imgs)
imgs[4] = str(i)
url = ""
images= imgs
thumb = tweet['extended_entities']['media'][0]['media_url_https']
qrt = {}
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']
text = tweet['full_text']
if 'possibly_sensitive' in tweet:
nsfw = tweet['possibly_sensitive']
else:
nsfw = False
if 'entities' in tweet and 'urls' in tweet['entities']:
for eurl in tweet['entities']['urls']:
text = text.replace(eurl["url"],eurl["expanded_url"])
vnf = tweetInfo(
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,
images=imgs,
nsfw=nsfw
)
return vnf
def link_to_vnf_from_unofficial_api(video_link):
print(" ➤ [ + ] Attempting to download tweet info from UNOFFICIAL Twitter API")
try:
tweet = twExtract.extractStatus(video_link)
except Exception as e:
print(' ➤ [ !!! ] Local UNOFFICIAL API Failed')
if ('apiMirrors' in config['config'] and len(config['config']['apiMirrors']) > 0):
mirror = random.choice(config['config']['apiMirrors'])
print(" ➤ [ + ] Using API Mirror: "+mirror)
tweet = requests.get(mirror+"?url="+video_link).json()
print (" ➤ [ ✔ ] Unofficial API Success")
return link_to_vnf_from_tweet_data(tweet,video_link)
def link_to_vnf_from_api(video_link):
tweet = get_tweet_data_from_api(video_link)
return link_to_vnf_from_tweet_data(tweet,video_link)
def link_to_vnf_from_youtubedl(video_link):
print(" ➤ [ X ] Attempting to download tweet info via YoutubeDL: " + video_link)
with yt_dlp.YoutubeDL({'outtmpl': '%(id)s.%(ext)s'}) as ydl:
result = ydl.extract_info(video_link, download=False)
vnf = tweetInfo(result['url'], video_link, result['description'].rsplit(' ',1)[0], result['thumbnail'], result['uploader'])
return vnf
def link_to_vnf(video_link): # Return a VideoInfo object or die trying
if config['config']['method'] == 'hybrid':
#try:
# return link_to_vnf_from_api(video_link)
#except Exception as e:
# print(" ➤ [ !!! ] API Failed")
#print(e)
try:
return link_to_vnf_from_unofficial_api(video_link)
except Exception as e:
print(" ➤ [ !!! ] UNOFFICIAL API Failed")
print(e)
return link_to_vnf_from_youtubedl(video_link) # This is the last resort, will only work for videos
elif config['config']['method'] == 'api':
try:
return link_to_vnf_from_api(video_link)
except Exception as e:
print(" ➤ [ X ] API Failed")
print(e)
return None
elif config['config']['method'] == 'youtube-dl':
try:
return link_to_vnf_from_youtubedl(video_link)
except Exception as e:
print(" ➤ [ X ] Youtube-DL Failed")
print(e)
return None
else:
print("Please set the method key in your config file to 'api' 'youtube-dl' or 'hybrid'")
return None
def message(text):
return render_template(
'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):
print(" ➤ [ E ] Embedding " + vnf['type'] + ": " + vnf['url'])
desc = re.sub(r' http.*t\.co\S+', '', vnf['description'])
urlUser = urllib.parse.quote(vnf['uploader'])
urlDesc = urllib.parse.quote(desc)
urlLink = urllib.parse.quote(video_link)
likeDisplay = ("\n\n💖 " + str(vnf['likes']) + " 🔁 " + str(vnf['rts']) + "\n")
try:
if vnf['type'] == "":
desc = desc
elif vnf['type'] == "Video":
desc = desc
elif vnf['qrt'] == {}: # Check if this is a QRT and modify the description
desc = (desc + likeDisplay)
else:
qrtDisplay = ("\n─────────────\n ➤ QRT of " + vnf['qrt']['handle'] + " (@" + vnf['qrt']['screen_name'] + "):\n─────────────\n'" + vnf['qrt']['desc'] + "'")
desc = (desc + qrtDisplay + likeDisplay)
except:
vnf['likes'] = 0; vnf['rts'] = 0; vnf['time'] = 0
print(' ➤ [ X ] Failed QRT check - old VNF object')
appNamePost = ""
if vnf['type'] == "Text": # Change the template based on tweet type
template = 'text.html'
if vnf['type'] == "Image":
if vnf['images'][4]!="1":
appNamePost = " - Image " + str(image+1) + "/" + str(vnf['images'][4])
image = vnf['images'][image]
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'
color = "#7FFFD4" # Green
if vnf['nsfw'] == True:
color = "#800020" # Red
return render_template(
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,
user = vnf['uploader'],
video_link = video_link,
color = color,
appname = config['config']['appname']+appNamePost,
repo = config['config']['repo'],
url = config['config']['url'],
urlDesc = urlDesc,
urlUser = urlUser,
urlLink = urlLink,
tweetLink = vnf['tweet'] )
def embedCombined(video_link):
cached_vnf = getVnfFromLinkCache(video_link)
if cached_vnf == None:
try:
vnf = link_to_vnf(video_link)
addVnfToLinkCache(video_link, vnf)
return embedCombinedVnf(video_link, vnf)
except Exception as e:
print(e)
return message(msgs.failedToScan)
else:
return embedCombinedVnf(video_link, cached_vnf)
def embedCombinedVnf(video_link,vnf):
if vnf['type'] != "Image" or vnf['images'][4] == "1":
return embed(video_link, vnf, 0)
desc = re.sub(r' http.*t\.co\S+', '', vnf['description'])
urlUser = urllib.parse.quote(vnf['uploader'])
urlDesc = urllib.parse.quote(desc)
urlLink = urllib.parse.quote(video_link)
likeDisplay = ("\n\n💖 " + str(vnf['likes']) + " 🔁 " + str(vnf['rts']) + "\n")
if vnf['qrt'] == {}: # Check if this is a QRT and modify the description
desc = (desc + likeDisplay)
else:
qrtDisplay = ("\n─────────────\n ➤ QRT of " + vnf['qrt']['handle'] + " (@" + vnf['qrt']['screen_name'] + "):\n─────────────\n'" + vnf['qrt']['desc'] + "'")
desc = (desc + qrtDisplay + likeDisplay)
color = "#7FFFD4" # Green
if vnf['nsfw'] == True:
color = "#800020" # Red
image = "https://vxtwitter.com/rendercombined.jpg?imgs="
for i in range(0,int(vnf['images'][4])):
image = image + vnf['images'][i] + ","
image = image[:-1] # Remove last comma
return render_template(
'image.html',
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,
user = vnf['uploader'],
video_link = video_link,
color = color,
appname = config['config']['appname'] + " - View original tweet for full quality",
repo = config['config']['repo'],
url = config['config']['url'],
urlDesc = urlDesc,
urlUser = urlUser,
urlLink = urlLink,
tweetLink = vnf['tweet'] )
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]:
out = "Video"
else:
out = "Image"
else:
out = "Text"
return out
def oEmbedGen(description, user, video_link, ttype):
out = {
"type" : ttype,
"version" : "1.0",
"provider_name" : config['config']['appname'],
"provider_url" : config['config']['repo'],
"title" : description,
"author_name" : user,
"author_url" : video_link
}
return out
if __name__ == "__main__":
app.config['SERVER_NAME']='localhost:80'
app.run(host='0.0.0.0')