diff --git a/core/amazon_download_client.py b/core/amazon_download_client.py index 76ba23ec..18e4bad1 100644 --- a/core/amazon_download_client.py +++ b/core/amazon_download_client.py @@ -285,10 +285,21 @@ class AmazonDownloadClient(DownloadSourcePlugin): else: enc_path.rename(out_path) + final_size = out_path.stat().st_size logger.info( f"Amazon download complete ({codec}): {out_path} " - f"({out_path.stat().st_size / (1024 * 1024):.1f} MB)" + f"({final_size / (1024 * 1024):.1f} MB)" ) + # Sync size == transferred so the download monitor's bytes-incomplete + # guard doesn't block post-processing. The throttled updates in + # _stream_to_file leave transferred < size after the last 0.5s tick; + # other streaming clients avoid this by not tracking bytes at all + # (size stays 0, the guard is skipped). Writing the final output size + # here restores parity. + if self._engine is not None: + self._engine.update_record( + "amazon", download_id, {'size': final_size, 'transferred': final_size} + ) return str(out_path) logger.error(f"All codec tiers exhausted for '{display_name}' ({asin})") diff --git a/tests/tools/test_amazon_download_client.py b/tests/tools/test_amazon_download_client.py index 2d1b70f9..d3a77d3a 100644 --- a/tests/tools/test_amazon_download_client.py +++ b/tests/tools/test_amazon_download_client.py @@ -616,6 +616,24 @@ class TestDownloadSync: states = [c[0][2].get("state") for c in update_calls if "state" in c[0][2]] assert "downloading" in states + def test_final_size_update_syncs_transferred_to_size(self, tmp_path): + """size == transferred after success so monitor bytes-incomplete guard doesn't block.""" + client, audio_data = self._setup(tmp_path, decryption_key=None) + engine = MagicMock() + client._engine = engine + + result = client._download_sync("dl-001", "B09XYZ1234", "Artist - Track") + + assert result is not None + # Last update must set size == transferred (both equal the output file size) + final_calls = [ + c[0][2] for c in engine.update_record.call_args_list + if 'size' in c[0][2] and 'transferred' in c[0][2] + and c[0][2]['size'] == c[0][2]['transferred'] + and c[0][2]['size'] > 0 + ] + assert final_calls, "Expected a final engine update with size == transferred > 0" + def test_clear_stream_skips_ffmpeg(self, tmp_path): client, audio_data = self._setup(tmp_path, decryption_key=None)