feat: add ready audio files (m4a/mp3/aac) to library with structured copy
- Expand Add Files dialog to accept all supported audio formats
- Copy ready files to output_dir/Artist/Album/{track} - Title.ext
- Fallback to output_dir root when metadata is missing
- Handle filename collisions with numeric suffix
This commit is contained in:
parent
b7367e63c4
commit
597603f90e
@ -8,6 +8,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
import shutil
|
||||||
from typing import List, Dict, Optional, Tuple, Any
|
from typing import List, Dict, Optional, Tuple, Any
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -753,20 +754,77 @@ class LibraryTab(QWidget):
|
|||||||
self._scan_library(force=True)
|
self._scan_library(force=True)
|
||||||
|
|
||||||
def _on_library_add_files(self):
|
def _on_library_add_files(self):
|
||||||
source_filter = "Audio Files (*.flac *.wav *.ogg *.opus *.wma *.ape *.aiff *.alac)"
|
all_exts = sorted(SOURCE_EXTS | AUDIO_EXTS)
|
||||||
|
source_filter = f"Audio Files (*{' *'.join(all_exts)})"
|
||||||
files, _ = QFileDialog.getOpenFileNames(
|
files, _ = QFileDialog.getOpenFileNames(
|
||||||
self, "Select Audio Files", "", source_filter
|
self, "Select Audio Files", "", source_filter
|
||||||
)
|
)
|
||||||
new_files = []
|
new_source_files = []
|
||||||
|
copied_ready = []
|
||||||
for f in files:
|
for f in files:
|
||||||
ext = os.path.splitext(f)[1].lower()
|
ext = os.path.splitext(f)[1].lower()
|
||||||
if ext in SOURCE_EXTS and os.path.exists(f) and f not in self.library_source_files:
|
if ext in SOURCE_EXTS and os.path.exists(f) and f not in self.library_source_files:
|
||||||
self.library_source_files.append(f)
|
self.library_source_files.append(f)
|
||||||
new_files.append(f)
|
new_source_files.append(f)
|
||||||
|
elif ext in AUDIO_EXTS and os.path.exists(f):
|
||||||
|
output_dir = self.get_output_dir()
|
||||||
|
if output_dir:
|
||||||
|
dest = self._copy_to_library(f, output_dir)
|
||||||
|
if dest:
|
||||||
|
copied_ready.append(dest)
|
||||||
|
|
||||||
if new_files:
|
if new_source_files or copied_ready:
|
||||||
self._scan_library()
|
self._scan_library()
|
||||||
|
|
||||||
|
def _copy_to_library(self, src: str, output_dir: str) -> str:
|
||||||
|
handler = MetadataHandler()
|
||||||
|
ext = os.path.splitext(src)[1].lower()
|
||||||
|
try:
|
||||||
|
info = handler.extract_tags(src)
|
||||||
|
artist = info.artist if info and info.artist else None
|
||||||
|
album = info.album if info and info.album else None
|
||||||
|
title = info.title if info and info.title else None
|
||||||
|
track_num = info.track_number if info else 0
|
||||||
|
except Exception:
|
||||||
|
artist = album = title = None
|
||||||
|
track_num = 0
|
||||||
|
|
||||||
|
if title:
|
||||||
|
safe_title = self._sanitize_filename(title)
|
||||||
|
safe_artist = self._sanitize_filename(artist) if artist else "_Unknown"
|
||||||
|
safe_album = self._sanitize_filename(album) if album else "_Unknown"
|
||||||
|
|
||||||
|
fname = f"{track_num:02d} - {safe_title}{ext}" if track_num > 0 else f"{safe_title}{ext}"
|
||||||
|
dest_dir = os.path.join(output_dir, safe_artist, safe_album)
|
||||||
|
else:
|
||||||
|
fname = os.path.basename(src)
|
||||||
|
dest_dir = output_dir
|
||||||
|
|
||||||
|
os.makedirs(dest_dir, exist_ok=True)
|
||||||
|
dest = os.path.join(dest_dir, fname)
|
||||||
|
|
||||||
|
if os.path.normpath(src) == os.path.normpath(dest):
|
||||||
|
return dest
|
||||||
|
if not os.path.exists(dest):
|
||||||
|
shutil.copy2(src, dest)
|
||||||
|
return dest
|
||||||
|
base, ext = os.path.splitext(fname)
|
||||||
|
for i in range(1, 100):
|
||||||
|
dest = os.path.join(dest_dir, f"{base}_{i}{ext}")
|
||||||
|
if not os.path.exists(dest):
|
||||||
|
shutil.copy2(src, dest)
|
||||||
|
return dest
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sanitize_filename(filename: str) -> str:
|
||||||
|
invalid_chars = '<>:"/\\|?*'
|
||||||
|
for char in invalid_chars:
|
||||||
|
filename = filename.replace(char, '_')
|
||||||
|
if len(filename) > 100:
|
||||||
|
filename = filename[:97] + '...'
|
||||||
|
return filename.strip()
|
||||||
|
|
||||||
def _incremental_update_after_edit(self, file_paths: list):
|
def _incremental_update_after_edit(self, file_paths: list):
|
||||||
cache = get_library_cache()
|
cache = get_library_cache()
|
||||||
handler = MetadataHandler()
|
handler = MetadataHandler()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user