#442: a car's pick told the page `replaced`, and the page closed its player bar, so leaving the car with the episode playing left nothing on the phone to pause it. And when the car launched the app, the page loaded later and was never told anything played. Now the app sends `nowPlaying` (the item, its place, playing or not) on a car pick, at the page's `ready`, and on coming forward, and the page's bar takes it up and drives the phone's player. `replaced` is still handled for builds before this one. #443: Shows listed every feed with items, blogs included. Now only feeds with a downloaded file, and an episode only with a downloaded audio file: /media/{id} answers 404 before the download. #444: tests/native-bridge.ts read its failure count before the CarPlay checks, so those could fail and still exit 0. The count is read last now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqkrVARreS9wxzH5o2rF7N
This commit is contained in:
@@ -13,6 +13,8 @@ struct CarEpisode: Decodable {
|
||||
let url: String
|
||||
let mime: String?
|
||||
let deadair: Double?
|
||||
/// Set once ipx has the file; `/media/{id}` answers 404 before that.
|
||||
let path: String?
|
||||
}
|
||||
|
||||
let guid: String
|
||||
@@ -23,10 +25,17 @@ struct CarEpisode: Decodable {
|
||||
let position: Double
|
||||
let enclosures: [Enclosure]
|
||||
|
||||
/// The file to play: the first that is not a video, which a car would only play the sound of
|
||||
/// with nothing to say so.
|
||||
/// The file to play: the first downloaded one that is sound (items.ts isPlayable, less video,
|
||||
/// which a car would play the sound of with nothing to say so). An item with none is not listed
|
||||
/// (#443): a post's picture or a file still to come is nothing a car can play.
|
||||
var audio: Enclosure? {
|
||||
enclosures.first { !($0.mime ?? "").lowercased().hasPrefix("video/") }
|
||||
enclosures.first { e in
|
||||
guard e.path != nil else { return false }
|
||||
let mime = (e.mime ?? "").lowercased()
|
||||
if !mime.isEmpty { return mime.hasPrefix("audio/") }
|
||||
let ext = URL(string: e.url)?.pathExtension.lowercased() ?? ""
|
||||
return ["mp3", "m4a", "m4b", "aac", "wav", "flac"].contains(ext)
|
||||
}
|
||||
}
|
||||
|
||||
/// Its title, or the file's name: a car's list cannot show the opening of a post as the
|
||||
@@ -66,6 +75,8 @@ struct CarFeed: Decodable {
|
||||
let image: String?
|
||||
let unread: Int?
|
||||
let entries: Int?
|
||||
/// Its files ipx has downloaded.
|
||||
let downloaded: Int?
|
||||
let group: String?
|
||||
|
||||
var name: String { title ?? id }
|
||||
@@ -166,13 +177,14 @@ final class CarScreen: NSObject, CPTabBarTemplateDelegate {
|
||||
}
|
||||
|
||||
/// Alphabetical, which a driver can find a show in without reading the whole list. Only
|
||||
/// shows with items: an OPML subscription's own row holds none, its feeds do.
|
||||
/// shows with files downloaded (#443): a blog has nothing a car can play, and an OPML
|
||||
/// subscription's own row holds no items, its feeds do.
|
||||
private func showFeeds() {
|
||||
guard !feeds.isEmpty else {
|
||||
unreachable(shows)
|
||||
return
|
||||
}
|
||||
let sorted = feeds.values.filter { ($0.entries ?? 0) > 0 }
|
||||
let sorted = feeds.values.filter { ($0.downloaded ?? 0) > 0 }
|
||||
.sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending }
|
||||
let rows: [CPListItem] = sorted.map { f in
|
||||
let n = f.unread ?? 0
|
||||
|
||||
Reference in New Issue
Block a user