Compare commits
No commits in common. "44fc039ac9e1f3b397e6e89fbdcbb28df1c10e58" and "96be306b2b7c3a0a3a2a6c4a6a671a077eea2fff" have entirely different histories.
44fc039ac9
...
96be306b2b
4 changed files with 76 additions and 88 deletions
|
@ -6,14 +6,16 @@ import websockets
|
||||||
import config
|
import config
|
||||||
import response
|
import response
|
||||||
import ydl_pool
|
import ydl_pool
|
||||||
|
import ydl_wrap
|
||||||
|
|
||||||
|
|
||||||
type SocketT = websockets.WebSocketServerProtocol
|
type SocketT = websockets.WebSocketServerProtocol
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def handler(socket: SocketT) -> None:
|
async def handler(socket: SocketT) -> None:
|
||||||
|
|
||||||
ydls = ydl_pool.Downloader(None, None) # type: ignore # TODO
|
ydls = ydl_pool.Downloaders()
|
||||||
|
|
||||||
async for message in socket:
|
async for message in socket:
|
||||||
|
|
||||||
|
@ -23,18 +25,20 @@ async def handler(socket: SocketT) -> None:
|
||||||
match data['action']:
|
match data['action']:
|
||||||
|
|
||||||
case 'list': # list tracks in album
|
case 'list': # list tracks in album
|
||||||
ydls.choose_ydl(data['site'])
|
await socket.send(response.ok_playlist(
|
||||||
await socket.send(response.playlist(
|
await ydl_wrap.get_playlist_items(
|
||||||
await ydls.get_playlist_items(data['url']),
|
ydls.get_ydl(data['site']),
|
||||||
|
data['url'],
|
||||||
|
)
|
||||||
))
|
))
|
||||||
|
|
||||||
case 'download': # download by URL
|
case 'download': # download by URL
|
||||||
ydls.choose_ydl(data['site'])
|
ret = await ydl_wrap.download(
|
||||||
ret = await ydls.download(
|
ydls.get_ydl(data['site']),
|
||||||
data['url'],
|
data['url'],
|
||||||
data.get('items'),
|
data.get('items'),
|
||||||
)
|
)
|
||||||
await socket.send(response.dl_end(ret))
|
await socket.send(response.ok_downloaded(ret))
|
||||||
|
|
||||||
# TODO: cancellation
|
# TODO: cancellation
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,25 @@
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
def playlist(items: list[str]) -> str:
|
OK = '{"ok":true}'
|
||||||
|
|
||||||
|
def ok_playlist(items: list[str]) -> str:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"type": "items",
|
"ok": True,
|
||||||
"data": items,
|
"data": items,
|
||||||
})
|
})
|
||||||
|
|
||||||
def dl_progress(msg: str) -> str:
|
def ok_downloaded(ret: int) -> str:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"type": "dl_progress",
|
"type": "downloaded",
|
||||||
"data": msg,
|
|
||||||
})
|
|
||||||
|
|
||||||
def dl_end(ret: int) -> str:
|
|
||||||
return json.dumps({
|
|
||||||
"type": "dl_end",
|
|
||||||
"data": ret,
|
"data": ret,
|
||||||
})
|
})
|
||||||
|
|
||||||
def error(ex: Exception) -> str:
|
def error(ex: Exception) -> str:
|
||||||
traceback.print_tb(ex.__traceback__)
|
traceback.print_tb(ex.__traceback__)
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"type": "error",
|
"ok": False,
|
||||||
"data": {
|
"error": {
|
||||||
"type": ex.__class__.__qualname__,
|
"type": ex.__class__.__qualname__,
|
||||||
"message": str(ex),
|
"message": str(ex),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import asyncio
|
|
||||||
from typing import Callable, Awaitable, Iterable
|
|
||||||
|
|
||||||
from yt_dlp import YoutubeDL
|
from yt_dlp import YoutubeDL
|
||||||
from yt_dlp.postprocessor import FFmpegExtractAudioPP
|
from yt_dlp.postprocessor import FFmpegExtractAudioPP
|
||||||
|
|
||||||
|
@ -37,12 +34,9 @@ create_ydl_fn = {
|
||||||
ydl_fn_keys = create_ydl_fn.keys()
|
ydl_fn_keys = create_ydl_fn.keys()
|
||||||
|
|
||||||
|
|
||||||
class Downloader:
|
class Downloaders:
|
||||||
|
|
||||||
def __init__(
|
def __init__(self) -> None:
|
||||||
self,
|
|
||||||
progress_cb: Callable[[str], Awaitable],
|
|
||||||
lyrics_cb: Callable[[list[str]], Awaitable]) -> None:
|
|
||||||
|
|
||||||
self.ydls: dict[str, YoutubeDL | None] = {
|
self.ydls: dict[str, YoutubeDL | None] = {
|
||||||
'youtube': None,
|
'youtube': None,
|
||||||
|
@ -50,15 +44,10 @@ class Downloader:
|
||||||
'yandex': None,
|
'yandex': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cur_ydl: YoutubeDL | None = None
|
def get_ydl(self, site: str) -> YoutubeDL:
|
||||||
|
|
||||||
self.progress_cb = progress_cb
|
|
||||||
self.lyrics_cb = lyrics_cb
|
|
||||||
|
|
||||||
def choose_ydl(self, site: str) -> None:
|
|
||||||
|
|
||||||
ydl = self.ydls[site]
|
|
||||||
cfg = config.get()
|
cfg = config.get()
|
||||||
|
ydl = self.ydls[site]
|
||||||
|
|
||||||
if ydl is None:
|
if ydl is None:
|
||||||
ydl = create_ydl_fn[site]()
|
ydl = create_ydl_fn[site]()
|
||||||
|
@ -71,62 +60,8 @@ class Downloader:
|
||||||
if cookies.exists():
|
if cookies.exists():
|
||||||
ydl.params['cookiefile'] = str(cookies)
|
ydl.params['cookiefile'] = str(cookies)
|
||||||
|
|
||||||
self.cur_ydl = ydl
|
|
||||||
|
|
||||||
def get_cur_ydl(self) -> YoutubeDL:
|
|
||||||
|
|
||||||
ydl = self.cur_ydl
|
|
||||||
if ydl is None:
|
|
||||||
raise RuntimeError('ydl object not initialized')
|
|
||||||
return ydl
|
return ydl
|
||||||
|
|
||||||
async def get_playlist_items(self, url: str) -> list[str]:
|
|
||||||
|
|
||||||
return await asyncio.get_event_loop().run_in_executor(
|
|
||||||
None,
|
|
||||||
Downloader._target_get_playlist_items,
|
|
||||||
self.get_cur_ydl(),
|
|
||||||
url,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _target_get_playlist_items(ydl: YoutubeDL, url: str) -> list[str]:
|
|
||||||
|
|
||||||
info = ydl.extract_info(url, download=False, process=True)
|
|
||||||
if info is None:
|
|
||||||
raise RuntimeError('ydl.extract_info returned None')
|
|
||||||
return [
|
|
||||||
entry['track'] if 'track' in entry else entry['title']
|
|
||||||
for entry in info['entries']
|
|
||||||
]
|
|
||||||
|
|
||||||
async def download(
|
|
||||||
self,
|
|
||||||
url: str,
|
|
||||||
playlist_items: Iterable[int] | None = None) -> int:
|
|
||||||
|
|
||||||
return await asyncio.get_event_loop().run_in_executor(
|
|
||||||
None,
|
|
||||||
Downloader._target_download,
|
|
||||||
self.get_cur_ydl(),
|
|
||||||
url,
|
|
||||||
playlist_items,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _target_download(
|
|
||||||
ydl: YoutubeDL,
|
|
||||||
url: str,
|
|
||||||
playlist_items: Iterable[int] | None = None) -> int:
|
|
||||||
|
|
||||||
if playlist_items:
|
|
||||||
ydl.params['playlist_items'] = ','.join(str(i) for i in playlist_items)
|
|
||||||
|
|
||||||
ret = ydl.download(url)
|
|
||||||
del ydl.params['playlist_items']
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
|
|
||||||
for ydl in self.ydls.values():
|
for ydl in self.ydls.values():
|
||||||
|
|
53
backend/ydl_wrap.py
Normal file
53
backend/ydl_wrap.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import asyncio
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from yt_dlp import YoutubeDL
|
||||||
|
|
||||||
|
|
||||||
|
async def get_playlist_items(ydl: YoutubeDL, url: str) -> list[str]:
|
||||||
|
|
||||||
|
return await asyncio.get_event_loop().run_in_executor(
|
||||||
|
None,
|
||||||
|
_target_get_playlist_items,
|
||||||
|
ydl,
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _target_get_playlist_items(ydl: YoutubeDL, url: str) -> list[str]:
|
||||||
|
|
||||||
|
info = ydl.extract_info(url, download=False, process=True)
|
||||||
|
if info is None:
|
||||||
|
raise RuntimeError('ydl.extract_info returned None')
|
||||||
|
return [
|
||||||
|
entry['track'] if 'track' in entry else entry['title']
|
||||||
|
for entry in info['entries']
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def download(
|
||||||
|
ydl: YoutubeDL,
|
||||||
|
url: str,
|
||||||
|
playlist_items: Iterable[int] | None = None) -> int:
|
||||||
|
|
||||||
|
return await asyncio.get_event_loop().run_in_executor(
|
||||||
|
None,
|
||||||
|
_target_download,
|
||||||
|
ydl,
|
||||||
|
url,
|
||||||
|
playlist_items,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _target_download(
|
||||||
|
ydl: YoutubeDL,
|
||||||
|
url: str,
|
||||||
|
playlist_items: Iterable[int] | None = None) -> int:
|
||||||
|
|
||||||
|
if playlist_items:
|
||||||
|
ydl.params['playlist_items'] = ','.join(str(i) for i in playlist_items)
|
||||||
|
|
||||||
|
ret = ydl.download(url)
|
||||||
|
del ydl.params['playlist_items']
|
||||||
|
|
||||||
|
return ret
|
Loading…
Add table
Reference in a new issue