|
|
|
|
@ -543,163 +543,10 @@ def post_process_matched_download(context_key, context, file_path, runtime, meta
|
|
|
|
|
|
|
|
|
|
context['artist'] = artist_context
|
|
|
|
|
|
|
|
|
|
playlist_folder_mode = track_info.get("_playlist_folder_mode", False)
|
|
|
|
|
logger.debug(f"[Debug] Post-processing - track_info type: {type(track_info)}, is None: {track_info is None}, is empty: {not track_info}")
|
|
|
|
|
logger.debug(f"[Debug] Post-processing - playlist_folder_mode: {playlist_folder_mode}")
|
|
|
|
|
if track_info:
|
|
|
|
|
logger.debug(f"[Debug] Post-processing - track_info keys: {list(track_info.keys())}")
|
|
|
|
|
|
|
|
|
|
if playlist_folder_mode:
|
|
|
|
|
playlist_name = track_info.get("_playlist_name", "Unknown Playlist")
|
|
|
|
|
logger.info(f"[Playlist Folder Mode] Organizing in playlist folder: {playlist_name}")
|
|
|
|
|
|
|
|
|
|
file_ext = os.path.splitext(file_path)[1]
|
|
|
|
|
final_path, _ = build_final_path_for_track(context, artist_context, None, file_ext)
|
|
|
|
|
logger.info(f"Playlist mode final path: '{final_path}'")
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(file_path):
|
|
|
|
|
if os.path.exists(final_path):
|
|
|
|
|
logger.info(
|
|
|
|
|
f"[Playlist Folder Mode] Source gone but destination exists — already processed by another thread: "
|
|
|
|
|
f"{os.path.basename(final_path)}"
|
|
|
|
|
)
|
|
|
|
|
context['_final_processed_path'] = final_path
|
|
|
|
|
return
|
|
|
|
|
pp_logger.info(f"[inner] EXCEPTION in post-processing for {context_key}: Source file not found and destination does not exist: {file_path}")
|
|
|
|
|
raise FileNotFoundError(f"Source file not found and destination does not exist: {file_path}")
|
|
|
|
|
|
|
|
|
|
context['_audio_quality'] = get_audio_quality_string(file_path)
|
|
|
|
|
if context['_audio_quality']:
|
|
|
|
|
logger.info(f"Audio quality detected: {context['_audio_quality']}")
|
|
|
|
|
|
|
|
|
|
_skip_bit_depth = _should_skip_quarantine_check(context, 'bit_depth')
|
|
|
|
|
rejection_reason = None if _skip_bit_depth else check_flac_bit_depth(file_path, context)
|
|
|
|
|
if _skip_bit_depth:
|
|
|
|
|
logger.info(f"[BitDepth] Skipped (user approval) for {_basename}")
|
|
|
|
|
if rejection_reason:
|
|
|
|
|
try:
|
|
|
|
|
quarantine_path = move_to_quarantine(
|
|
|
|
|
file_path,
|
|
|
|
|
context,
|
|
|
|
|
rejection_reason,
|
|
|
|
|
automation_engine,
|
|
|
|
|
trigger='bit_depth',
|
|
|
|
|
)
|
|
|
|
|
_mark_task_quarantined(context, quarantine_path)
|
|
|
|
|
logger.info(f"File quarantined due to bit depth filter: {quarantine_path}")
|
|
|
|
|
except Exception as quarantine_error:
|
|
|
|
|
logger.error(f"Quarantine failed ({quarantine_error}), deleting file: {file_path}")
|
|
|
|
|
try:
|
|
|
|
|
os.remove(file_path)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.debug("delete quarantine fallback: %s", e)
|
|
|
|
|
|
|
|
|
|
context['_bitdepth_rejected'] = True
|
|
|
|
|
with matched_context_lock:
|
|
|
|
|
if context_key in matched_downloads_context:
|
|
|
|
|
del matched_downloads_context[context_key]
|
|
|
|
|
|
|
|
|
|
task_id = context.get('task_id')
|
|
|
|
|
batch_id = context.get('batch_id')
|
|
|
|
|
if task_id:
|
|
|
|
|
with tasks_lock:
|
|
|
|
|
if task_id in download_tasks:
|
|
|
|
|
download_tasks[task_id]['status'] = 'failed'
|
|
|
|
|
download_tasks[task_id]['error_message'] = f"Bit depth filter: {rejection_reason}"
|
|
|
|
|
if task_id and batch_id:
|
|
|
|
|
_notify_download_completed(batch_id, task_id, success=False)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
logger.warning(
|
|
|
|
|
f"[Metadata Input] Playlist mode - artist: '{artist_context.get('name', 'MISSING')}' "
|
|
|
|
|
f"(id: {artist_context.get('id', 'MISSING')})"
|
|
|
|
|
)
|
|
|
|
|
enhance_file_metadata(file_path, context, artist_context, None, runtime=metadata_runtime)
|
|
|
|
|
except Exception as meta_err:
|
|
|
|
|
import traceback
|
|
|
|
|
pp_logger.info(f"[inner] Metadata enhancement FAILED for {context_key}: {meta_err}\n{traceback.format_exc()}")
|
|
|
|
|
if should_wipe_tags_on_enhancement_failure(has_clean_metadata):
|
|
|
|
|
wipe_source_tags(file_path)
|
|
|
|
|
else:
|
|
|
|
|
logger.warning(
|
|
|
|
|
"[Metadata] Enhancement failed but import has clean/matched metadata — "
|
|
|
|
|
"preserving the file's existing tags (not wiping): %s",
|
|
|
|
|
os.path.basename(file_path))
|
|
|
|
|
|
|
|
|
|
logger.info(f"Moving '{os.path.basename(file_path)}' to '{final_path}'")
|
|
|
|
|
safe_move_file(file_path, final_path)
|
|
|
|
|
context['_final_processed_path'] = final_path
|
|
|
|
|
cleanup_slskd_dedup_siblings(file_path)
|
|
|
|
|
|
|
|
|
|
if config_manager.get('post_processing.replaygain_enabled', False):
|
|
|
|
|
try:
|
|
|
|
|
from core.replaygain import analyze_track as _rg_analyze, write_replaygain_tags as _rg_write, is_ffmpeg_available as _rg_ffmpeg_ok, RG_REFERENCE_LUFS as _RG_REF
|
|
|
|
|
if _rg_ffmpeg_ok():
|
|
|
|
|
lufs, peak_dbfs = _rg_analyze(final_path)
|
|
|
|
|
gain_db = _RG_REF - lufs
|
|
|
|
|
_rg_write(final_path, gain_db, peak_dbfs)
|
|
|
|
|
pp_logger.info(f"ReplayGain: {gain_db:+.2f} dB — {os.path.basename(final_path)}")
|
|
|
|
|
except Exception as rg_err:
|
|
|
|
|
pp_logger.debug(f"ReplayGain analysis skipped: {rg_err}")
|
|
|
|
|
|
|
|
|
|
downsampled_path = downsample_hires_flac(final_path, context)
|
|
|
|
|
if downsampled_path:
|
|
|
|
|
final_path = downsampled_path
|
|
|
|
|
context['_final_processed_path'] = final_path
|
|
|
|
|
|
|
|
|
|
_persist_verification_status(context, final_path)
|
|
|
|
|
|
|
|
|
|
blasphemy_path = create_lossy_copy(final_path)
|
|
|
|
|
if blasphemy_path:
|
|
|
|
|
context['_final_processed_path'] = blasphemy_path
|
|
|
|
|
|
|
|
|
|
downloads_path = docker_resolve_path(config_manager.get('soulseek.download_path', './downloads'))
|
|
|
|
|
cleanup_empty_directories(downloads_path, file_path)
|
|
|
|
|
|
|
|
|
|
logger.info(f"[Playlist Folder Mode] Post-processing complete: {final_path}")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
check_and_remove_from_wishlist(context)
|
|
|
|
|
except Exception as wishlist_error:
|
|
|
|
|
logger.error(f"[Playlist Folder] Error checking wishlist removal: {wishlist_error}")
|
|
|
|
|
|
|
|
|
|
emit_track_downloaded(context, automation_engine)
|
|
|
|
|
record_library_history_download(context)
|
|
|
|
|
record_download_provenance(context)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
pf_album_info = build_import_album_info(context, force_album=False)
|
|
|
|
|
if not pf_album_info or not pf_album_info.get("album_name"):
|
|
|
|
|
pf_album_info = {
|
|
|
|
|
"is_album": True,
|
|
|
|
|
"album_name": playlist_name,
|
|
|
|
|
"track_number": track_info.get("track_number", 1) or 1,
|
|
|
|
|
"disc_number": track_info.get("disc_number", 1) or 1,
|
|
|
|
|
"clean_track_name": get_import_clean_title(
|
|
|
|
|
context,
|
|
|
|
|
default=get_import_original_search(context).get("title", "Unknown"),
|
|
|
|
|
),
|
|
|
|
|
"source": get_import_source(context) or "spotify",
|
|
|
|
|
}
|
|
|
|
|
elif not pf_album_info.get("is_album"):
|
|
|
|
|
pf_album_info["is_album"] = True
|
|
|
|
|
record_soulsync_library_entry(context, artist_context, pf_album_info)
|
|
|
|
|
except Exception as lib_err:
|
|
|
|
|
logger.error(f"[Playlist Folder] SoulSync library registration failed: {lib_err}")
|
|
|
|
|
|
|
|
|
|
task_id = context.get('task_id')
|
|
|
|
|
batch_id = context.get('batch_id')
|
|
|
|
|
if task_id and batch_id:
|
|
|
|
|
with tasks_lock:
|
|
|
|
|
if task_id in download_tasks:
|
|
|
|
|
download_tasks[task_id]['stream_processed'] = True
|
|
|
|
|
download_tasks[task_id]['status'] = 'completed'
|
|
|
|
|
download_tasks[task_id]['final_file_path'] = context.get('_final_processed_path')
|
|
|
|
|
logger.info(f"[Playlist Folder Mode] Marked task {task_id} as completed")
|
|
|
|
|
_notify_download_completed(batch_id, task_id, success=True)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
is_album_download = bool(context.get("is_album_download", False))
|
|
|
|
|
album_info = build_import_album_info(context, force_album=is_album_download)
|
|
|
|
|
|
|
|
|
|
|