Skip to content

Instantly share code, notes, and snippets.

@katabame
Created September 10, 2017 21:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save katabame/38b9b40119c19ba4e60e959e055497f3 to your computer and use it in GitHub Desktop.
Save katabame/38b9b40119c19ba4e60e959e055497f3 to your computer and use it in GitHub Desktop.
Just-Some-Bots/MusicBot を niconico に対応させるやつ

Just-Some-Bots/MusicBot を niconico に対応させるやつ

使い方

downloader.pymusicbot/downloader.py に上書きしてください。 39行目と40行目の部分をniconicoのメールアドレス/パスワードに書き換えてください。 (Bot用のアカウントを取得するのをおすすめします)

注意点

たまに認証に失敗します。 ニコニコの鯖が遅いのか、DLに時間がかかるっぽいです。

import os
import re
import asyncio
import functools
import youtube_dl
from concurrent.futures import ThreadPoolExecutor
ytdl_format_options = {
'format': 'bestaudio/best',
'extractaudio': True,
'audioformat': 'mp3',
'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'
}
ncdl_format_options = {
'format': 'bestaudio/best',
'extractaudio': True,
'audioformat': 'mp3',
'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',
'username': '<USERNAME@example.com>',
'password': '<PASSWORD>'
}
# 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
self.unsafe_ncdl = youtube_dl.YoutubeDL(ncdl_format_options)
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)
otmpl = self.unsafe_ncdl.params['outtmpl']
self.unsafe_ncdl.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.
"""
if callable(on_error):
try:
if re.search(r'nicovideo.jp', args[0]):
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ncdl.extract_info, *args, **kwargs))
else:
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:
if re.search(r'nicovideo.jp', args[0]):
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ncdl.extract_info, *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):
if re.search(r'nicovideo.jp', args[0]):
return await loop.run_in_executor(self.thread_pool, functools.partial(self.unsafe_ncdl.extract_info, *args, **kwargs))
else:
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