Compare commits

...

2 commits

Author SHA1 Message Date
5444969027
Update frontend for YDL logging 2024-05-08 13:53:02 +04:00
247bf65439
Add YDL logging, change schema 2024-05-08 13:52:36 +04:00
5 changed files with 80 additions and 20 deletions

View file

@ -13,7 +13,18 @@ type SocketT = websockets.WebSocketServerProtocol
async def handler(socket: SocketT) -> None:
ydls = ydl_pool.Downloader(None, None) # type: ignore # TODO
async def ydl_log_handler(level: response.YdlLogLevel, msg: str) -> None:
try:
await socket.send(response.ydl_log(level, msg))
except:
pass
ydls = ydl_pool.Downloader(
ydl_pool.YdlLogger(
ydl_log_handler,
asyncio.get_event_loop(),
)
)
async for message in socket:
@ -34,9 +45,7 @@ async def handler(socket: SocketT) -> None:
data['url'],
data.get('items'),
)
await socket.send(response.dl_end(ret))
# TODO: cancellation
await socket.send(response.ydl_end(ret))
case _:
raise ValueError('invalid "action" field value')

View file

@ -1,5 +1,7 @@
import json
import traceback
import logging
from typing import Literal
def playlist(items: list[str]) -> str:
return json.dumps({
@ -7,20 +9,25 @@ def playlist(items: list[str]) -> str:
"data": items,
})
def dl_progress(msg: str) -> str:
type YdlLogLevel = Literal['debug'] | Literal['warning'] | Literal['error']
def ydl_log(level: YdlLogLevel, msg: str) -> str:
return json.dumps({
"type": "dl_progress",
"type": "ydl_log",
"level": level,
"data": msg,
})
def dl_end(ret: int) -> str:
def ydl_end(ret: int) -> str:
return json.dumps({
"type": "dl_end",
"type": "ydl_end",
"data": ret,
})
def error(ex: Exception) -> str:
traceback.print_tb(ex.__traceback__)
logging.getLogger('musicdlp').exception(ex)
return json.dumps({
"type": "error",
"data": {

View file

@ -1,10 +1,12 @@
import asyncio
import logging
from typing import Callable, Awaitable, Iterable
from yt_dlp import YoutubeDL
from yt_dlp.postprocessor import FFmpegExtractAudioPP
import config
import response
import id3pp
@ -40,12 +42,33 @@ ydl_fn_keys = create_ydl_fn.keys()
NP_YDLS = {'yandex'}
class Downloader:
class YdlLogger:
def __init__(
self,
progress_cb: Callable[[str], Awaitable],
lyrics_cb: Callable[[list[str]], Awaitable]) -> None:
log_cb: Callable[[response.YdlLogLevel, str], Awaitable],
loop: asyncio.AbstractEventLoop) -> None:
self.log_cb = log_cb
self.loop = loop
self.mdlp_logger = logging.getLogger('musicdlp')
def debug(self, msg: str) -> None:
asyncio.run_coroutine_threadsafe(self.log_cb('debug', msg), self.loop)
def info(self, _: str) -> None: # afaik not used in yt-dlp
pass
def warning(self, msg: str) -> None:
asyncio.run_coroutine_threadsafe(self.log_cb('warning', msg), self.loop)
def error(self, msg: str) -> None:
self.mdlp_logger.error(msg)
asyncio.run_coroutine_threadsafe(self.log_cb('error', msg), self.loop)
class Downloader:
def __init__(self, logger: YdlLogger | None = None) -> None:
self.ydls: dict[str, YoutubeDL | None] = {
'youtube': None,
@ -56,8 +79,9 @@ class Downloader:
self.cur_ydl: YoutubeDL | None = None
self.cur_site = ''
self.progress_cb = progress_cb
self.lyrics_cb = lyrics_cb
self.id3pp_obj = id3pp.ID3TagsPP()
self.logger = logger
def choose_ydl(self, site: str) -> None:
@ -67,8 +91,11 @@ class Downloader:
if ydl is None:
ydl = create_ydl_fn[site]()
if self.logger is not None:
ydl.params['logger'] = self.logger
ydl.params['outtmpl']['default'] = cfg.tmpl
ydl.add_post_processor(id3pp.ID3TagsPP(), when='post_process')
ydl.add_post_processor(self.id3pp_obj, when='post_process')
cookies = cfg.cookies_dir / (site + '.txt')
if cookies.exists():

View file

@ -27,7 +27,7 @@
<button type="button" id="dl-btn">Download</button>
</div>
<div>
<label>Progress: <span id="progress">not implemented</span></label>
<pre><code id="log"></code></pre>
</div>
</body>
</html>

View file

@ -21,12 +21,15 @@ addEventListener('DOMContentLoaded', () => {
const itemsDiv = document.getElementById('items-container')
let items = []
const logField = document.getElementById('log')
document.getElementById('items-btn').addEventListener('click', () => {
socket.send(JSON.stringify({
action: 'list',
site: site.value,
url: urlField.value,
}))
logField.textContent = '' // clear
})
document.getElementById('dl-btn').addEventListener('click', () => {
@ -37,13 +40,14 @@ addEventListener('DOMContentLoaded', () => {
items: items,
}))
items = []
logField.textContent = ''
})
socket.addEventListener('message', ev => {
const msg = JSON.parse(ev.data)
switch (msg.type) {
case 'items':
itemsDiv.textContent = '' // clear
itemsDiv.textContent = ''
const len = msg.data.length
for (let i = 1; i <= len; i++) {
const itemElem = document.createElement('label')
@ -62,9 +66,22 @@ addEventListener('DOMContentLoaded', () => {
itemsDiv.append(itemElem)
}
break
case 'dl_progress':
case 'ydl_log':
const lineElem = document.createElement('span')
switch (msg.level) {
case 'warning':
lineElem.className = 'log-warning'
break
case 'error':
lineElem.className = 'log-error'
break
default: // add no classes if `debug` or some unknown level
break
}
lineElem.innerText = msg.data + '\n'
logField.append(lineElem)
break
case 'dl_end':
case 'ydl_end':
break
}
})