import time from pathlib import Path from sqlalchemy.orm import Session from app.models import ManualImport from app.services import pipeline_runner from app.settings import settings def import_me_dir() -> Path: return settings.music_data_dir / "import-me" def list_import_me_contents() -> list[str]: root = import_me_dir() if not root.exists(): return [] return sorted(p.name for p in root.iterdir()) def save_uploaded_file(filename: str, content: bytes) -> Path: """Write an uploaded file into import-me/ alongside whatever's already there via SMB. Path-traversal guard: only the basename of the supplied filename is used, so an upload can't escape import-me/.""" root = import_me_dir() root.mkdir(parents=True, exist_ok=True) safe_name = Path(filename).name if not safe_name: raise ValueError("empty filename") dest = root / safe_name dest.write_bytes(content) return dest async def import_track( db: Session, source: str | None, playlist_name: str | None, imported_by: str ) -> ManualImport: """source: a filename/subdir relative to import-me/, or None/empty to import everything currently sitting in import-me/. Mirrors import-track.sh's own argument-count-based disambiguation (see its docstring): 0 args = import everything untagged, 1 arg = either a real path under import-me/ or a playlist name (the script figures out which), 2 args = path + playlist name explicitly.""" args: list[str] = [] if source: args.append(source) if playlist_name: args.append(playlist_name) elif playlist_name: args.append(playlist_name) job_run = await pipeline_runner.run_import_track(args, triggered_by="manual") record = ManualImport( source_path=source or "(all of import-me/)", playlist_name=playlist_name, imported_by=imported_by, imported_at=time.time(), status=job_run.status, job_run_id=job_run.id, ) db.add(record) db.commit() db.refresh(record) return record