7 Commits

Author SHA1 Message Date
Alex
179452b4f4 Merge pull request #858 from ikatkov/master
Improves logging, helpful when debugging yt-dlp options
2025-12-30 22:40:16 +02:00
ikatkov
4fce74d1ed Merge pull request #1 from ikatkov/logging-fix
Logging fix
2025-12-30 10:22:31 -08:00
Igor Katkov
09a2e95515 fix: Root logger aligns with config.LOGLEVEL 2025-12-30 10:19:30 -08:00
Igor Katkov
d947876a71 fix: pass DEBUG log level to ytdl 2025-12-30 10:01:43 -08:00
Igor Katkov
6ba681a3cd fix: Moved code to respect loggin level in main.py 2025-12-30 08:45:54 -08:00
Alex
1f8fa7744e Merge pull request #857 from mercury233/patch-1
fix completed result tooltip
2025-12-27 12:17:40 +02:00
mercury233
092765535f fix completed result tooltip 2025-12-27 10:48:57 +08:00
4 changed files with 42 additions and 19 deletions

View File

@@ -21,6 +21,26 @@ from yt_dlp.version import __version__ as yt_dlp_version
log = logging.getLogger('main') log = logging.getLogger('main')
def parseLogLevel(logLevel):
match logLevel:
case 'DEBUG':
return logging.DEBUG
case 'INFO':
return logging.INFO
case 'WARNING':
return logging.WARNING
case 'ERROR':
return logging.ERROR
case 'CRITICAL':
return logging.CRITICAL
case _:
return None
# Configure logging before Config() uses it so early messages are not dropped.
# Only configure if no handlers are set (avoid clobbering hosting app settings).
if not logging.getLogger().hasHandlers():
logging.basicConfig(level=parseLogLevel(os.environ.get('LOGLEVEL', 'INFO')) or logging.INFO)
class Config: class Config:
_DEFAULTS = { _DEFAULTS = {
'DOWNLOAD_DIR': '.', 'DOWNLOAD_DIR': '.',
@@ -112,6 +132,10 @@ class Config:
return (True, '') return (True, '')
config = Config() config = Config()
# Align root logger level with Config (keeps a single source of truth).
# This re-applies the log level after Config loads, in case LOGLEVEL was
# overridden by config file settings or differs from the environment variable.
logging.getLogger().setLevel(parseLogLevel(str(config.LOGLEVEL)) or logging.INFO)
class ObjectSerializer(json.JSONEncoder): class ObjectSerializer(json.JSONEncoder):
def default(self, obj): def default(self, obj):
@@ -386,21 +410,6 @@ def supports_reuse_port():
except (AttributeError, OSError): except (AttributeError, OSError):
return False return False
def parseLogLevel(logLevel):
match logLevel:
case 'DEBUG':
return logging.DEBUG
case 'INFO':
return logging.INFO
case 'WARNING':
return logging.WARNING
case 'ERROR':
return logging.ERROR
case 'CRITICAL':
return logging.CRITICAL
case _:
return None
def isAccessLogEnabled(): def isAccessLogEnabled():
if config.ENABLE_ACCESSLOG: if config.ENABLE_ACCESSLOG:
return access_logger return access_logger
@@ -408,7 +417,7 @@ def isAccessLogEnabled():
return None return None
if __name__ == '__main__': if __name__ == '__main__':
logging.basicConfig(level=parseLogLevel(config.LOGLEVEL)) logging.getLogger().setLevel(parseLogLevel(config.LOGLEVEL) or logging.INFO)
log.info(f"Listening on {config.HOST}:{config.PORT}") log.info(f"Listening on {config.HOST}:{config.PORT}")
if config.HTTPS: if config.HTTPS:

View File

@@ -83,6 +83,7 @@ class Download:
def _download(self): def _download(self):
log.info(f"Starting download for: {self.info.title} ({self.info.url})") log.info(f"Starting download for: {self.info.title} ({self.info.url})")
try: try:
debug_logging = logging.getLogger().isEnabledFor(logging.DEBUG)
def put_status(st): def put_status(st):
self.status_queue.put({k: v for k, v in st.items() if k in ( self.status_queue.put({k: v for k, v in st.items() if k in (
'tmpfilename', 'tmpfilename',
@@ -105,7 +106,8 @@ class Download:
self.status_queue.put({'status': 'finished', 'filename': filename}) self.status_queue.put({'status': 'finished', 'filename': filename})
ret = yt_dlp.YoutubeDL(params={ ret = yt_dlp.YoutubeDL(params={
'quiet': True, 'quiet': not debug_logging,
'verbose': debug_logging,
'no_color': True, 'no_color': True,
'paths': {"home": self.download_dir, "temp": self.temp_dir}, 'paths': {"home": self.download_dir, "temp": self.temp_dir},
'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter }, 'outtmpl': { "default": self.output_template, "chapter": self.output_template_chapter },
@@ -314,8 +316,10 @@ class DownloadQueue:
asyncio.create_task(self.notifier.completed(download.info)) asyncio.create_task(self.notifier.completed(download.info))
def __extract_info(self, url, playlist_strict_mode): def __extract_info(self, url, playlist_strict_mode):
debug_logging = logging.getLogger().isEnabledFor(logging.DEBUG)
return yt_dlp.YoutubeDL(params={ return yt_dlp.YoutubeDL(params={
'quiet': True, 'quiet': not debug_logging,
'verbose': debug_logging,
'no_color': True, 'no_color': True,
'extract_flat': True, 'extract_flat': True,
'ignore_no_formats_error': True, 'ignore_no_formats_error': True,

View File

@@ -395,7 +395,7 @@
<fa-icon [icon]="faTimesCircle" class="text-danger" /> <fa-icon [icon]="faTimesCircle" class="text-danger" />
} }
</div> </div>
<span ngbTooltip="{{download.value.msg}} | {{download.value.error}}">@if (!!download.value.filename) { <span ngbTooltip="{{buildResultItemTooltip(download.value)}}">@if (!!download.value.filename) {
<a href="{{buildDownloadLink(download.value)}}" target="_blank">{{ download.value.title }}</a> <a href="{{buildDownloadLink(download.value)}}" target="_blank">{{ download.value.title }}</a>
} @else { } @else {
{{download.value.title}} {{download.value.title}}

View File

@@ -367,6 +367,16 @@ export class App implements AfterViewInit, OnInit {
return baseDir + encodeURIComponent(download.filename); return baseDir + encodeURIComponent(download.filename);
} }
buildResultItemTooltip(download: Download) {
const parts = [];
if (download.msg) {
parts.push(download.msg);
}
if (download.error) {
parts.push(download.error);
}
return parts.join(' | ');
}
isNumber(event: KeyboardEvent) { isNumber(event: KeyboardEvent) {
const charCode = +event.code || event.keyCode; const charCode = +event.code || event.keyCode;