feat(podcasts): played/unplayed queue, background generation, share-sheet audio
Turns the Podcasts tab into a proper podcast-app experience across three features that compose on a shared background-generation service. Background generation (#2) - New PodcastGenerationManager runs generate→poll→download off the player, so producing a new episode never stops current playback. - Headphone taps never interrupt: idle → generate-and-play in the foreground (unchanged); already playing → generate in the background, episode lands in the library. A "Generating…" banner surfaces active jobs. Played/unplayed + queue (#1) - PodcastEntry gains playedAt (old index.json decodes as unplayed). - Podcasts tab splits into Up Next / Played with a Play All queue that auto-advances; finishing marks played; swipe to toggle played state. Share-extension audio (#3) - "Create podcast" toggle on the save card, plus configurable Auto-Podcast Tags in Settings. The extension enqueues a request into the App Group; the main app drains it on launch/foreground and generates in the background. Closes #1 Closes #2 Closes #3 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ struct ShareView: View {
|
||||
@State private var tagText = ""
|
||||
@State private var notes = ""
|
||||
@State private var readLater = false
|
||||
@State private var createPodcast = false
|
||||
@State private var suggestedTags: [String] = [] // linkding auto_tags
|
||||
@State private var knownTags: Set<String> = [] // user's existing vocabulary (lowercased)
|
||||
@State private var aiSuggestions: [String] = [] // net-new AI ideas (not in vocabulary)
|
||||
@@ -151,6 +152,11 @@ struct ShareView: View {
|
||||
|
||||
Toggle("Read later", isOn: $readLater)
|
||||
.font(.subheadline)
|
||||
|
||||
Toggle(isOn: $createPodcast) {
|
||||
Label("Create podcast", systemImage: "headphones")
|
||||
}
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +242,8 @@ struct ShareView: View {
|
||||
title = result.metadata?.title ?? ""
|
||||
suggestedTags = result.autoTags ?? []
|
||||
}
|
||||
// Pre-enable the toggle when the current tags match the auto-rule.
|
||||
createPodcast = PodcastRequests.matchesAutoTag(parsedTags)
|
||||
phase = .editing
|
||||
await loadSuggestions()
|
||||
} catch {
|
||||
@@ -294,6 +302,11 @@ struct ShareView: View {
|
||||
)
|
||||
_ = try await api.createBookmark(create)
|
||||
}
|
||||
// Hand off podcast generation to the main app (the extension is too
|
||||
// short-lived to generate audio itself).
|
||||
if createPodcast || PodcastRequests.matchesAutoTag(parsedTags) {
|
||||
PodcastRequests.enqueue(url: url, title: title.isEmpty ? url : title)
|
||||
}
|
||||
phase = .done
|
||||
try? await Task.sleep(nanoseconds: 700_000_000)
|
||||
onClose(true)
|
||||
|
||||
Reference in New Issue
Block a user