feat: media search, choices, library management (Phase 6)

This commit is contained in:
snowspeeder committed 2026-08-30 08:39:57 -04:00
1 parent c10ea72cd6
commit cbf18306ff
4 files changed
+343 -4

No files matched your search

+2
View File
@@ -87,6 +87,7 @@ struct MediaLibrary: Decodable, Equatable {
struct MediaSearchResult: Decodable, Equatable {
let videoID: String?
let trackID: String?
let title: String?
let channel: String?
let duration: Double?
@@ -95,6 +96,7 @@ struct MediaSearchResult: Decodable, Equatable {
enum CodingKeys: String, CodingKey {
case title, channel, duration
case videoID = "video_id"
case trackID = "track_id"
case inLibrary = "in_library"
}
}
+35 -2
View File
@@ -110,12 +110,23 @@ final class MediaPlayerController: ObservableObject {
pollTask = nil
}
/// Start polling the default room when no session is active. Called from
/// the Media Panel so play/stop state is visible without a voice session.
func startDefaultPolling() {
guard room == nil else { return }
startPolling()
}
// MARK: Poll + reconcile (ported from app.js pollMediaState/reconcileMedia)
private func pollState() async {
let scope = room ?? "default"
guard let state = try? await api.state(room: scope) else { return }
reconcile(state)
do {
let state = try await api.state(room: scope)
reconcile(state)
} catch {
logger.log("poll failed: \(String(describing: error), privacy: .public)")
}
}
private func reconcile(_ s: MediaState) {
@@ -270,6 +281,28 @@ final class MediaPlayerController: ObservableObject {
}
}
// MARK: Play requests (search results, choices, library)
func playVideo(videoID: String?, title: String?, channel: String?) async {
let targetRoom = room ?? "default"
do {
try await api.play(room: targetRoom, videoID: videoID, title: title, channel: channel)
logger.log("play requested video_id=\(videoID ?? "nil", privacy: .public) room=\(targetRoom, privacy: .public)")
} catch {
logger.log("play failed: \(String(describing: error), privacy: .public)")
}
}
func playTrack(id: String) async {
let targetRoom = room ?? "default"
do {
try await api.play(room: targetRoom, trackID: id)
logger.log("play requested track_id=\(id, privacy: .public) room=\(targetRoom, privacy: .public)")
} catch {
logger.log("play failed: \(String(describing: error), privacy: .public)")
}
}
// MARK: Transport controls (parity with web UI play/pause/seek/stop)
func togglePlayPause() async {