Compare commits

...

2 commits

Author SHA1 Message Date
DarkCat09 5444969027
Update frontend for YDL logging 2024-05-08 13:53:02 +04:00
DarkCat09 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: 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: async for message in socket:
@ -34,9 +45,7 @@ async def handler(socket: SocketT) -> None:
data['url'], data['url'],
data.get('items'), data.get('items'),
) )
await socket.send(response.dl_end(ret)) await socket.send(response.ydl_end(ret))
# TODO: cancellation
case _: case _:
raise ValueError('invalid "action" field value') raise ValueError('invalid "action" field value')

View file

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

View file

@ -1,10 +1,12 @@
import asyncio import asyncio
import logging
from typing import Callable, Awaitable, Iterable 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
import config import config
import response
import id3pp import id3pp
@ -40,12 +42,33 @@ ydl_fn_keys = create_ydl_fn.keys()
NP_YDLS = {'yandex'} NP_YDLS = {'yandex'}
class Downloader: class YdlLogger:
def __init__( def __init__(
self, self,
progress_cb: Callable[[str], Awaitable], log_cb: Callable[[response.YdlLogLevel, str], Awaitable],
lyrics_cb: Callable[[list[str]], Awaitable]) -> None: 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] = { self.ydls: dict[str, YoutubeDL | None] = {
'youtube': None, 'youtube': None,
@ -56,8 +79,9 @@ class Downloader:
self.cur_ydl: YoutubeDL | None = None self.cur_ydl: YoutubeDL | None = None
self.cur_site = '' self.cur_site = ''
self.progress_cb = progress_cb self.id3pp_obj = id3pp.ID3TagsPP()
self.lyrics_cb = lyrics_cb
self.logger = logger
def choose_ydl(self, site: str) -> None: def choose_ydl(self, site: str) -> None:
@ -67,8 +91,11 @@ class Downloader:
if ydl is None: if ydl is None:
ydl = create_ydl_fn[site]() ydl = create_ydl_fn[site]()
if self.logger is not None:
ydl.params['logger'] = self.logger
ydl.params['outtmpl']['default'] = cfg.tmpl 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') cookies = cfg.cookies_dir / (site + '.txt')
if cookies.exists(): if cookies.exists():

View file

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

View file

@ -21,12 +21,15 @@ addEventListener('DOMContentLoaded', () => {
const itemsDiv = document.getElementById('items-container') const itemsDiv = document.getElementById('items-container')
let items = [] let items = []
const logField = document.getElementById('log')
document.getElementById('items-btn').addEventListener('click', () => { document.getElementById('items-btn').addEventListener('click', () => {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
action: 'list', action: 'list',
site: site.value, site: site.value,
url: urlField.value, url: urlField.value,
})) }))
logField.textContent = '' // clear
}) })
document.getElementById('dl-btn').addEventListener('click', () => { document.getElementById('dl-btn').addEventListener('click', () => {
@ -37,13 +40,14 @@ addEventListener('DOMContentLoaded', () => {
items: items, items: items,
})) }))
items = [] items = []
logField.textContent = ''
}) })
socket.addEventListener('message', ev => { socket.addEventListener('message', ev => {
const msg = JSON.parse(ev.data) const msg = JSON.parse(ev.data)
switch (msg.type) { switch (msg.type) {
case 'items': case 'items':
itemsDiv.textContent = '' // clear itemsDiv.textContent = ''
const len = msg.data.length const len = msg.data.length
for (let i = 1; i <= len; i++) { for (let i = 1; i <= len; i++) {
const itemElem = document.createElement('label') const itemElem = document.createElement('label')
@ -62,9 +66,22 @@ addEventListener('DOMContentLoaded', () => {
itemsDiv.append(itemElem) itemsDiv.append(itemElem)
} }
break 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 break
case 'dl_end': case 'ydl_end':
break break
} }
}) })