first version

This commit is contained in:
thezero 2021-06-19 20:35:07 +02:00
parent 363942e0e0
commit 586668c7e0
4 changed files with 227 additions and 42 deletions

133
.gitignore vendored Normal file
View File

@ -0,0 +1,133 @@
viddb.sqlite3
out/
conf/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

36
db.py Normal file
View File

@ -0,0 +1,36 @@
import os
import sqlite3
class VidDatabase():
def __init__(self, path):
self.path = path
self.is_valid = os.path.isfile(self.path)
self.con = sqlite3.connect(self.path)
def __del__(self):
self.con.close()
def create(self):
cur = self.con.cursor()
cur.execute("CREATE TABLE vid (link TEXT NOT NULL, code TEXT NOT NULL);")
self.con.commit()
def select_vid(self, vid):
cur = self.con.cursor()
cur.execute("SELECT link, code FROM vid WHERE rowid = ?;", (vid,))
rows = cur.fetchall()
video = rows[0] if len(rows) > 0 else None
self.con.commit()
return video
def insert_vid(self, link, code):
cur = self.con.cursor()
cur.execute("INSERT INTO vid (link, code) VALUES (?, ?);", (link, code))
self.con.commit()
return cur.lastrowid
def delete_vid(self, link):
cur = self.con.cursor()
cur.execute("DELETE FROM vid WHERE link = ?;", link)
self.con.commit()

32
main.py
View File

@ -9,35 +9,39 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_format(bot, update): def get_format(update, context):
logger.info("from {}: {}".format(update.message.chat_id, update.message.text)) # "history" logger.info("from {}: {}".format(update.message.chat_id, update.message.text)) # "history"
try: try:
video = Video(update.message.text, init_keyboard=True) video = Video(link=update.message.text, init_keyboard=True)
except BadLink: except BadLink as e:
update.message.reply_text("Bad link") update.message.reply_text("Bad link: {}".format(e))
else: else:
reply_markup = InlineKeyboardMarkup(video.keyboard) reply_markup = InlineKeyboardMarkup(video.keyboard)
update.message.reply_text('Choose format:', reply_markup=reply_markup) update.message.reply_text('Choose format:', reply_markup=reply_markup)
def download_choosen_format(bot, update): def download_choosen_format(update, context):
query = update.callback_query query = update.callback_query
resolution_code, link = query.data.split(' ', 1) video_index = query.data
bot.edit_message_text(text="Downloading...", context.bot.edit_message_text(text="Downloading...",
chat_id=query.message.chat_id, chat_id=query.message.chat_id,
message_id=query.message.message_id) message_id=query.message.message_id)
video = Video(link) video = Video(vid=video_index)
video.download(resolution_code) video.download()
with video.send() as files: with video.send() as files:
for f in files: for f in files:
bot.send_document(chat_id=query.message.chat_id, document=open(f, 'rb')) logger.log("Sending... {} ".format(f))
context.bot.send_document(chat_id=query.message.chat_id, document=open(f, 'rb'))
context.bot.delete_message(chat_id=query.message.chat_id,
message_id=query.message.message_id)
updater = Updater(token=YOUR_TOKEN) updater = Updater(token="")
updater.dispatcher.add_handler(MessageHandler(Filters.text, get_format)) updater.dispatcher.add_handler(MessageHandler(Filters.text, get_format))
updater.dispatcher.add_handler(CallbackQueryHandler(download_choosen_format)) updater.dispatcher.add_handler(CallbackQueryHandler(download_choosen_format))

View File

@ -1,79 +1,91 @@
import re
import os import os
from glob import glob, escape from glob import glob, escape
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from time import strftime, strptime, sleep
from contextlib import contextmanager from contextlib import contextmanager
from telegram import InlineKeyboardButton from telegram import InlineKeyboardButton
from db import VidDatabase
class BadLink(Exception): class BadLink(Exception):
pass pass
class Video: class Video:
def __init__(self, link, init_keyboard=False): def __init__(self, link=None, vid=None, init_keyboard=False):
self.link = link self.db = VidDatabase("viddb.sqlite3")
self.file_name = None if not self.db.is_valid:
# Database file not present
# Create a new database
self.db.create()
if vid is None and link is not None:
self.link = link
self.file_name = None
elif vid is not None and link is None:
self.link, self.code = self.db.select_vid(vid)
else:
raise Exception('what is going on?')
if init_keyboard: if init_keyboard:
self.formats = self.get_formats() self.formats = self.get_formats()
self.keyboard = self.generate_keyboard() self.keyboard = self.generate_keyboard()
def get_formats(self): def get_formats(self):
formats = [] formats = {}
cmd = "youtube-dl -F {}".format(self.link) p = Popen(["youtube-dl", "-F", self.link], stdout=PIPE, stderr=PIPE).communicate()
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate() it = iter(str(p[0], 'utf-8').split('\n')) # iterator of output lines
it = iter(str(p[0], 'utf-8').split('\n')) # iterator of output lines
try: try:
while "code extension" not in next(it): pass # Remove garbage lines while "code extension" not in next(it):
pass # Remove garbage lines
except StopIteration: except StopIteration:
raise BadLink # Isn't a valid youtube link raise BadLink("youtube-dl couldn't download the link you provided") # Isn't a valid youtube link
while True: while True:
try: try:
line = next(it) line = next(it)
if not line: if not line:
raise StopIteration # Usually the last line is empty raise StopIteration # Usually the last line is empty
if "video only" in line: if "video only" in line:
continue # I don't need video without audio continue # I don't need video without audio
except StopIteration: except StopIteration:
break break
else: else:
format_code, extension, resolution, *_ = line.strip().split() format_code, extension, resolution, *_ = line.strip().split()
formats.append([format_code, extension, resolution]) key = '{},{}'.format(extension, resolution)
index = self.db.insert_vid(self.link, format_code)
formats[key] = index
return formats return formats
def generate_keyboard(self): def generate_keyboard(self):
""" Generate a list of InlineKeyboardButton of resolutions """ """ Generate a list of InlineKeyboardButton of resolutions """
kb = [] kb = []
for code, extension, resolution in self.formats: for key in self.formats.keys():
kb.append([InlineKeyboardButton("{0}, {1}".format(extension, resolution), cb = "{}".format(self.formats[key])
callback_data="{} {}".format(code, self.link))]) # maybe callback_data can support a list or tuple? kb.append([InlineKeyboardButton(key, callback_data=cb)])
return kb return kb
def download(self, resolution_code): def download(self):
cmd = "youtube-dl -f {0} {1}".format(resolution_code, self.link) p = Popen(["youtube-dl", "-f", self.code, self.link], stdout=PIPE, stderr=PIPE).communicate()
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate()
for line in str(p[0], 'utf-8').split('\n'): for line in str(p[0], 'utf-8').split('\n'):
if "[download] Destination:" in line: if "[download] Destination:" in line:
self.file_name = line[24:] # name of the file self.file_name = line[24:] # name of the file
def check_dimension(self): def check_dimension(self):
if os.path.getsize(self.file_name) > 50 * 1024 * 1023: if os.path.getsize(self.file_name) > 50 * 1024 * 1023:
os.system('split -b 49M "{0}" "{1}"'.format(self.file_name, self.file_name)) Popen(["split", "-b", "49M", self.file_name, self.file_name])
os.remove(self.file_name) os.remove(self.file_name)
return glob(escape(self.file_name) + '*') return glob(escape(self.file_name) + '*')
@contextmanager @contextmanager
def send(self): def send(self):
files = self.check_dimension() # split if size >= 50MB files = self.check_dimension() # split if size >= 50MB
yield files yield files
for f in files: #removing old files for f in files: # removing old files
os.remove(f) os.remove(f)