Share never hands over a private feed's file (#455)

Share on a paid episode's file gave the file's own address, which for
Patreon is patreon.com/api/rss/u/<your id>/e/<n>.mp3?sig=..., signed for
the subscriber: whoever got it could download the episode as them. A
private feed's episode is now shared by its page alone, from the file
too, and with no page there is nothing to share. The access warning
matched any patreon.com address, so it warned on a post's page, which
asks for a sign-in and carries nothing of yours; it now matches
Patreon's feed and file addresses only.

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 17:34:17 +00:00
parent 45ef1e83f2
commit 757ce5fbb3
5 changed files with 35 additions and 7 deletions

View File

@@ -527,15 +527,15 @@ const shareAt=(url: string, secs: number)=>secs>=5 ? `${url}?t=${Math.floor(secs
/// ipx-play, where the item's own address is all Share has ever given.
function noLinkNote(why: string|null, url: string): string{
const notes: string[]=[];
if(why==='private') notes.push('This podcast\'s feed is private to you, so it gets no page anyone can play: Share gives the episode\'s own address instead.');
if(why==='private') notes.push('This podcast\'s feed is private to you, so it gets no page anyone can play, and its file is never shared: this is the episode\'s own page.');
else if(why==='failed') notes.push('The page anyone can play could not be made just now, so this is the episode\'s own address. Try again later for a link to it.');
if(carriesAccess(url)) notes.push(ACCESS_WARNING);
return notes.map(n=>`<p class="hint">${esc(n)}</p>`).join('');
}
/// An episode shared as Overcast shares one (#447): a link to its page on ipx-play, where anyone
/// can play it, from where you are in it if you like. A private feed, a post, or a server with no
/// ipx-play shares the item's own address, through the same menu.
/// can play it, from where you are in it if you like. A post, or a server with no ipx-play, shares
/// the item's own address through the same menu; a private feed's episode, its page alone.
async function shareEpisode(e: Entry, encId: number|null){
const name=entryName(e).text;
const own=encId!=null ? e.enclosures.find(x=>x.id===encId)?.url : e.link;
@@ -544,8 +544,14 @@ async function shareEpisode(e: Entry, encId: number|null){
link=await api(`/api/entries/${encodeURIComponent(e.feed_id)}/${encodeURIComponent(e.guid)}/share`,
{method:'POST', body:JSON.stringify(encId!=null ? {enc:encId} : {})});
}catch{}
const url=link.url ?? own;
if(!url){ toast('There is nothing to share for this item.', true); return; }
// A private feed's file can be signed for you (Patreon's are), so only the episode's page goes,
// even from Share on the file (#455).
const url=link.url ?? (link.why==='private' ? e.link : own);
if(!url){
toast(link.why==='private' ? 'This podcast is private to you, and this episode has no page of its own to share.'
: 'There is nothing to share for this item.', true);
return;
}
// Where you are: the player's clock when it has this one, else where you left off. Only
// ipx-play's page reads a time.
const at=!link.url ? 0 : player.guid===e.guid && player.feed===e.feed_id ? audio.currentTime : e.position;