Aim a removal at the row the server counts

Remote playlist entries carried the display row number as their server
position, so a row the app could not parse shifted every removal after it
by one, and rows were paired back to entries by video id, which cannot
tell two copies of the same video apart.

- Piped entry positions are now the raw stream index; entries carry a
  separate displayRow for the row they are drawn as, filled in by both
  snapshot builders and used by the detail view's row map
- an emptied description clears a server-side playlist instead of being
  read as "leave it alone"
- a delete that reported failure forgets a playlist the refetch 404s on,
  and drops it from the account's cached list
- the browse view discards account-scoped content when a status change
  moves which account the instance resolves to
- every mutation re-reads the credential immediately before it is sent,
  which matters most after Piped's verify-before-remove fetch
- Handoff carries the owning account id, so a Piped playlist keeps its
  ownership on the receiving device
This commit is contained in:
Arkadiusz Fal
2026-09-02 23:56:59 +02:00
parent b3c4216013
commit 24b43ac866
11 changed files with 430 additions and 52 deletions

View File

@@ -327,16 +327,27 @@ actor InvidiousAPI: InstanceAPI {
// and `index` places it. Rows missing either are still displayed —
// they stay in `playlist.videos` — they just never appear in `entries`,
// so no removal can be aimed at a row the server refuses to name.
let entries: [RemotePlaylistEntry] = allVideos.compactMap { item in
guard case .video(let video) = item,
let indexID = video.indexId,
let index = video.index
else { return nil }
//
// `displayRow` is counted over exactly what `toPlaylist` keeps, so an
// entry says which visible row it belongs to without anyone having to
// match video ids — which could not tell two rows of the same video
// apart anyway.
var displayRow = 0
var entries: [RemotePlaylistEntry] = []
for item in allVideos {
guard case .video(let video) = item else { continue }
let row = displayRow
displayRow += 1
return RemotePlaylistEntry(
id: .invidious(playlistID: playlistID, indexID: indexID),
video: video.toVideo(baseURL: baseURL),
position: index
guard let indexID = video.indexId, let index = video.index else { continue }
entries.append(
RemotePlaylistEntry(
id: .invidious(playlistID: playlistID, indexID: indexID),
video: video.toVideo(baseURL: baseURL),
position: index,
displayRow: row
)
)
}