Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
11f9130
feat: implement advanced logging with session and event logging
fsdmu Feb 2, 2026
81a5382
chore: bump version to 0.5.0
fsdmu Feb 2, 2026
fa5a101
chore: remove linting and scanning steps from Docker image build process
fsdmu Feb 2, 2026
499e415
ADD: replace tox commands with ruff
fsdmu Feb 5, 2026
0c234a7
ADD: fix linting, doc style and type errors
fsdmu Feb 5, 2026
a4d45bf
ADD: fix errors and python version in actions workflow
fsdmu Feb 5, 2026
16ffaec
FIX: tests
fsdmu Feb 5, 2026
4585e1c
ADD: add tests and minor fixes
fsdmu Feb 5, 2026
cba440e
FIX: noqa annotation
fsdmu Feb 5, 2026
49967b0
FIX: remove ip logging
fsdmu Feb 6, 2026
75f8bf9
UPDATE: help text with logging info
fsdmu Feb 6, 2026
5822783
UPDATE: docstring naming
fsdmu Feb 6, 2026
981f328
ADD: introducing uv, update Dockerfile accordingly, set flac as stand…
fsdmu Feb 6, 2026
ead5f4b
UPDATE: fix tox issues
fsdmu Feb 6, 2026
f7afe9e
ADD: uv.lock file
fsdmu Feb 6, 2026
303727c
UPDATE: tox testenv
fsdmu Feb 6, 2026
9ed6772
UPDATE: pyproject.toml
fsdmu Feb 6, 2026
91b9cb6
UPDATE: project dependencies
fsdmu Feb 6, 2026
56fcafd
ADD: implement full song download option and logging for URL input up…
fsdmu May 10, 2026
b3c6ef3
Merge branch 'main' into implement-full-song-download-option
fsdmu May 10, 2026
ece58ce
ADD: implement lifespan context for logging and enhance album fetchin…
fsdmu May 10, 2026
36a6b36
REMOVE: delete unused _log_page_view function from main.py
fsdmu May 10, 2026
c24108b
UPDATE: refactor logging setup and enhance download handler with opti…
fsdmu May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
import uuid
from contextlib import asynccontextmanager

from nicegui import app, ui
from starlette.requests import Request
Expand Down Expand Up @@ -67,6 +68,12 @@ def build_main_content(self):
.classes("mt-2")
)

self.get_songs = (
ui.switch("Get individual songs (not just albums/eps)", value=False)
.props("color=#CB69C1")
.classes("mt-2")
)

# ensure submit handler logs and receives session_id
ui.button(
"Submit",
Expand All @@ -75,6 +82,28 @@ def build_main_content(self):
"pink-btn w-full h-[50px] font-bold text-lg mt-4"
)

# attach update handler that logs every change once threshold is met
def _extract_value(ev):
# NiceGUI may pass the new value directly or an event-like object
try:
return ev.value # event object
except Exception:
return ev # raw value

def _on_update(ev):
val = _extract_value(ev)
try:
if isinstance(val, str) and len(val.strip()) >= THRESHOLD:
# call decorated logger and propagate session id
try:
self.log_url_input(value=val, session_id=self.session_id)
except Exception:
logger.exception("Failed to log url input update")
except Exception:
logger.exception("Error handling url input update")

self.url_input.on("update", _on_update)

@log_event("submit.click")
async def handle_click(self, session_id: str | None = None):
"""Handle a download submission. session_id is passed to logging wrapper.
Expand All @@ -85,11 +114,18 @@ async def handle_click(self, session_id: str | None = None):
"""
await process_submission(
self.url_input,
self.get_songs.value,
self.auto_dl.value,
self.settings.audio_format.value,
session_id=session_id,
)

@log_event("url_input.update")
def log_url_input(self, value: str, session_id: str | None = None):
"""Log every URL input update (no debounce)."""
# return a small payload for structured logging
return {"url": value}


@log_event("page.view")
def _log_page_view(session_id: str | None, user_agent: str, created: bool):
Expand Down Expand Up @@ -144,7 +180,26 @@ def main_page(request: Request, response: Response) -> None:
MusicApiApp(session_id=session_id)


app.on_shutdown(stop_logging)
@app.on_event("startup")
async def _startup_event():
"""Startup handler to configure logging and related resources.

We keep failures from preventing the app from starting by logging exceptions.
"""
try:
setup_logging(LogDatabaseConnector())
except Exception as e:
logging.getLogger("app").exception(f"Logging setup failed on startup: {e}")


@app.on_event("shutdown")
async def _shutdown_event():
"""Shutdown handler to stop logging and perform cleanup."""
try:
stop_logging()
except Exception as e:
logging.getLogger("app").exception(f"Logging stop failed on shutdown: {e}")


if __name__ in {"__main__", "__mp_main__"}:
setup_logging(LogDatabaseConnector())
Expand Down
5 changes: 4 additions & 1 deletion src/download_handler_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class DownloadHandlerBase(ABC):
def download(
self,
url: str,
auto_download: bool,
get_songs: bool = False, # make optional default False
auto_download: bool = False,
*args,
session_id: str | None = None,
**kwargs,
Expand All @@ -21,6 +22,8 @@ def download(

Args:
url: The URL to download from.
get_songs: Whether to extract individual songs and not just eps and
albums when downloading from an artist URL.
auto_download: Whether to mark the content for auto-download.
*args: Positional arguments for the download method.
session_id: Optional session identifier to propagate to downstream calls.
Expand Down
19 changes: 17 additions & 2 deletions src/ui/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,23 @@


async def process_submission(
url_input, auto_download, audio_format, session_id: str | None = None
url_input,
get_songs,
auto_download,
audio_format,
session_id: str | None = None
):
"""Logic for handling the URL submission."""
"""Logic for handling the URL submission.

Args:
url_input: The input field containing the URL.
get_songs: Whether to fetch individual songs and not just eps and albums
when downloading from an artist URL.
auto_download: Whether to mark the artist for auto-download.
audio_format: The desired audio format for downloads.
session_id: The session ID for logging context.

"""
try:
url = str(url_input.value).strip()
if not url:
Expand All @@ -24,6 +38,7 @@ async def process_submission(

handler.download(
url=url,
get_songs=get_songs,
auto_download=auto_download,
add_without_download=False,
download_format=audio_format,
Expand Down
Loading
Loading