"""Music library API — list dirs, list files, move files between folders.""" import os import shutil from flask import Blueprint, jsonify, request from ..config import MUSIC_BASE, FALLBACK_DIR bp = Blueprint("music", __name__) AUDIO_EXTENSIONS = {".mp3", ".flac", ".ogg", ".opus", ".m4a", ".wav"} def _get_folders(): """Return dict mapping folder name -> absolute path.""" folders = {} if MUSIC_BASE.exists(): for d in sorted(MUSIC_BASE.iterdir()): if d.is_dir(): folders[d.name] = d folders["fallback"] = FALLBACK_DIR return folders def _count_audio_files(directory): if not directory.exists(): return 0 return sum( 1 for f in directory.rglob("*") if f.is_file() and f.suffix.lower() in AUDIO_EXTENSIONS ) def _list_audio_files(directory): if not directory.exists(): return [] files = [] for f in sorted(directory.iterdir()): if f.is_file() and f.suffix.lower() in AUDIO_EXTENSIONS: files.append({ "name": f.name, "size": f.stat().st_size, }) return files @bp.route("/music") def list_music(): dirs = [] if MUSIC_BASE.exists(): for d in sorted(MUSIC_BASE.iterdir()): if d.is_dir(): dirs.append({ "folder": d.name, "path": str(d), "file_count": _count_audio_files(d), }) fallback_count = _count_audio_files(FALLBACK_DIR) return jsonify({ "directories": dirs, "fallback": { "path": str(FALLBACK_DIR), "file_count": fallback_count, }, }) @bp.route("/music//files") def list_files(folder): folders = _get_folders() if folder not in folders: return jsonify({"error": "Unknown folder"}), 404 files = _list_audio_files(folders[folder]) return jsonify({"folder": folder, "files": files}) @bp.route("/music/move", methods=["POST"]) def move_file(): data = request.get_json() src_folder = data.get("from") dst_folder = data.get("to") filename = data.get("file") if not all([src_folder, dst_folder, filename]): return jsonify({"error": "Missing from, to, or file"}), 400 folders = _get_folders() if src_folder not in folders or dst_folder not in folders: return jsonify({"error": "Unknown folder"}), 404 if os.sep in filename or "/" in filename or ".." in filename: return jsonify({"error": "Invalid filename"}), 400 src_path = folders[src_folder] / filename dst_path = folders[dst_folder] / filename if not src_path.exists(): return jsonify({"error": "File not found"}), 404 if dst_path.exists(): return jsonify({"error": "File already exists in destination"}), 409 dst_path.parent.mkdir(parents=True, exist_ok=True) shutil.move(str(src_path), str(dst_path)) return jsonify({"ok": True, "moved": filename, "from": src_folder, "to": dst_folder})