One place to get an episode's file where you are (#457)

Getting an episode onto a phone took two buttons in two states: download
it to the server, wait, then keep it on the device, beside Save to this
computer, Share and Delete, five buttons that did not say where each
copy went. Now a file has one button that brings it to where you are.
On a phone or in the iOS app it ends on the device; when iPX has not got
the file yet, iPX fetches it and the device's copy follows once iPX says
it is done (remembered in localStorage, so a page closed meanwhile
catches up when a list next shows the file). On a computer it has iPX
fetch the file, and with iPX's copy there saves one. A line beside the
size says where the file is, and the rarer actions, two of them
destructive, are under a menu.

isPlayable is false for any file not on iPX, so the phone's button is
decided by playsKind, the same test without the path. download_done now
marks the file as on iPX in what the page holds, so the open file's box
changes at once rather than after the list is fetched again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqkrVA8TdTxe556JB3sR1n
This commit is contained in:
2026-11-13 18:40:49 +00:00
parent 47e093e313
commit 5ef01593d7
9 changed files with 201 additions and 86 deletions

View File

@@ -38,6 +38,11 @@ function connect(){
// Said only for a file on screen, as one downloaded by hand is: the scheduled downloads of
// everyone's feeds used to announce themselves to everyone.
if(bar){ bar.classList.remove('live'); toast('Downloaded '+ev.path.split('/').pop()); }
// On iPX now, in what this page holds too, so the open file's box says so at once rather
// than after the list comes again; and, asked for on this device, its copy here follows (#457).
for(const e of S.entries) for(const x of e.enclosures) if(x.id===ev.enclosure){ x.path=ev.path; x.state='done'; }
if(wanted.has(ev.enclosure)) followWanted({id: ev.enclosure, path: ev.path});
else syncKeepButtons();
refreshEntries();
}
// A copy finished: the episode's "Cutting dead air…" becomes how much shorter it is.
@@ -50,6 +55,7 @@ function connect(){
else if(ev.ev==='download_error'){
const bar=document.querySelector(`.dlbar[data-bar="${ev.enclosure}"]`);
if(bar) bar.classList.remove('live');
unwant(ev.enclosure);
// Which item, and what to do: the reason (ev.msg) is in the daemon's log (#304).
const e=S.entries.find(x=>x.enclosures.some(n=>n.id===ev.enclosure));
toast(`Unable to download ${e?`"${entryName(e).text}"`:'a file'}. Try again with its download button.`,true); refreshEntries();