fix: download track on !keep when local file is missing

When the initial download failed during playback and the track streamed
directly from URL, !keep would refuse with "No local file". Now it
downloads the track on the spot before keeping it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
user
2026-02-22 17:01:44 +01:00
parent 6083de13f9
commit 36da191b45
2 changed files with 46 additions and 4 deletions

View File

@@ -1405,8 +1405,20 @@ async def cmd_keep(bot, message):
await bot.reply(message, "Nothing playing")
return
if track.local_path is None:
await bot.reply(message, "No local file for current track")
return
if not track.url:
await bot.reply(message, "No local file for current track")
return
# Download on the spot -- track was streaming without a local file
loop = asyncio.get_running_loop()
tid = hashlib.md5(track.url.encode()).hexdigest()[:12]
dl_path = await loop.run_in_executor(
None, _download_track, track.url, tid, track.title,
)
if dl_path:
track.local_path = dl_path
else:
await bot.reply(message, "Download failed, cannot keep track")
return
track.keep = True
# Check if this track is already kept (by normalized URL)

View File

@@ -1520,14 +1520,44 @@ class TestKeepCommand:
asyncio.run(_mod.cmd_keep(bot, msg))
assert any("Nothing playing" in r for r in bot.replied)
def test_keep_no_local_file(self):
def test_keep_no_local_file_no_url(self):
bot = _FakeBot()
ps = _mod._ps(bot)
ps["current"] = _mod._Track(url="x", title="t", requester="a")
ps["current"] = _mod._Track(url="", title="t", requester="a")
msg = _Msg(text="!keep")
asyncio.run(_mod.cmd_keep(bot, msg))
assert any("No local file" in r for r in bot.replied)
def test_keep_downloads_when_no_local_file(self, tmp_path):
bot = _FakeBot()
ps = _mod._ps(bot)
track = _mod._Track(url="https://example.com/v", title="t",
requester="a")
ps["current"] = track
dl_file = tmp_path / "abc.opus"
dl_file.write_bytes(b"audio")
music_dir = tmp_path / "kept"
music_dir.mkdir()
meta = {"title": "t", "artist": "", "duration": 0}
msg = _Msg(text="!keep")
with patch.object(_mod, "_download_track", return_value=dl_file), \
patch.object(_mod, "_MUSIC_DIR", music_dir), \
patch.object(_mod, "_fetch_metadata", return_value=meta):
asyncio.run(_mod.cmd_keep(bot, msg))
assert track.keep is True
assert track.local_path is not None
assert any("Keeping" in r for r in bot.replied)
def test_keep_download_failure(self):
bot = _FakeBot()
ps = _mod._ps(bot)
ps["current"] = _mod._Track(url="https://example.com/v", title="t",
requester="a")
msg = _Msg(text="!keep")
with patch.object(_mod, "_download_track", return_value=None):
asyncio.run(_mod.cmd_keep(bot, msg))
assert any("Download failed" in r for r in bot.replied)
def test_keep_marks_track(self, tmp_path):
bot = _FakeBot()
ps = _mod._ps(bot)