Created
January 21, 2020 17:50
-
-
Save amit177/5809bf892abc55a665846528974f6b05 to your computer and use it in GitHub Desktop.
Just-Some-Bots/MusicBot downloader.py - use random IP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import asyncio | |
import logging | |
import functools | |
import youtube_dl | |
from random import choice | |
from concurrent.futures import ThreadPoolExecutor | |
log = logging.getLogger(__name__) | |
ytdl_format_options = { | |
'format': 'bestaudio/best', | |
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s', | |
'restrictfilenames': True, | |
'noplaylist': True, | |
'nocheckcertificate': True, | |
'ignoreerrors': False, | |
'logtostderr': False, | |
'quiet': True, | |
'no_warnings': True, | |
'default_search': 'auto', | |
'source_address': '0.0.0.0', | |
'usenetrc': True | |
} | |
IP_LIST = ["1.1.1.1", "1.1.1.2", "1.1.1.3"] | |
# Fuck your useless bugreports message that gets two link embeds and confuses users | |
youtube_dl.utils.bug_reports_message = lambda: '' | |
''' | |
Alright, here's the problem. To catch youtube-dl errors for their useful information, I have to | |
catch the exceptions with `ignoreerrors` off. To not break when ytdl hits a dumb video | |
(rental videos, etc), I have to have `ignoreerrors` on. I can change these whenever, but with async | |
that's bad. So I need multiple ytdl objects. | |
''' | |
class Downloader: | |
def __init__(self, download_folder=None): | |
self.thread_pool = ThreadPoolExecutor(max_workers=2) | |
self.unsafe_ytdl = youtube_dl.YoutubeDL(ytdl_format_options) | |
self.safe_ytdl = youtube_dl.YoutubeDL(ytdl_format_options) | |
self.safe_ytdl.params['ignoreerrors'] = True | |
self.download_folder = download_folder | |
if download_folder: | |
otmpl = self.unsafe_ytdl.params['outtmpl'] | |
self.unsafe_ytdl.params['outtmpl'] = os.path.join(download_folder, otmpl) | |
# print("setting template to " + os.path.join(download_folder, otmpl)) | |
otmpl = self.safe_ytdl.params['outtmpl'] | |
self.safe_ytdl.params['outtmpl'] = os.path.join(download_folder, otmpl) | |
@property | |
def ytdl(self): | |
return self.safe_ytdl | |
async def extract_info(self, loop, *args, on_error=None, retry_on_error=False, **kwargs): | |
""" | |
Runs ytdl.extract_info within the threadpool. Returns a future that will fire when it's done. | |
If `on_error` is passed and an exception is raised, the exception will be caught and passed to | |
on_error as an argument. | |
""" | |
ytdl_format_options["source_address"] = choice(IP_LIST) | |
print("Using IP", ytdl_format_options["source_address"]) | |
self.unsafe_ytdl = youtube_dl.YoutubeDL(ytdl_format_options) | |
self.safe_ytdl = youtube_dl.YoutubeDL(ytdl_format_options) | |
if callable(on_error): | |
try: | |
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs)) | |
except Exception as e: | |
# (youtube_dl.utils.ExtractorError, youtube_dl.utils.DownloadError) | |
# I hope I don't have to deal with ContentTooShortError's | |
if asyncio.iscoroutinefunction(on_error): | |
asyncio.ensure_future(on_error(e), loop=loop) | |
elif asyncio.iscoroutine(on_error): | |
asyncio.ensure_future(on_error, loop=loop) | |
else: | |
loop.call_soon_threadsafe(on_error, e) | |
if retry_on_error: | |
return await self.safe_extract_info(loop, *args, **kwargs) | |
else: | |
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ytdl.extract_info, *args, **kwargs)) | |
async def safe_extract_info(self, loop, *args, **kwargs): | |
return await loop.run_in_executor(self.thread_pool, functools.partial(self.safe_ytdl.extract_info, *args, **kwargs)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment