Fixes #21 — Curated YouTube videos per guide + grammar item
Each of the 56 tense guides and grammar notes gets a curated YouTube video attached (54 with picks, 2 silent nulls on rare / hard-to-find topics). Users can stream in YouTube/Safari, download via YouTubeKit for offline viewing, or play the local MP4 full-screen via AVPlayer. YouTubeVideoStore loads the bundled youtube_videos.json at launch and serves lookups by tense id or grammar note id. VideoDownloadService resolves the best progressive MP4 stream off the main actor (YouTubeKit isn't Sendable), writes to documents/videos/<videoId>.mp4, and records a DownloadedVideo row in the local SwiftData container so the app knows what's on disk across launches. VideoActionsButtonRow is the unified UI for both detail views: three large buttons — Stream (red, always enabled), Download (blue, disabled while in flight and after completion, shows progress), Play (green, enabled only when downloaded). Full-screen cover on tap. Settings gains a Downloaded Videos list with swipe-delete, total-size summary, and a 500 MB warning. Local store reset version bumped to 4 for the new DownloadedVideo schema. Known fragility: YouTubeKit scrapes YouTube's private stream API and will break when YouTube changes their internal format. Streaming keeps working regardless. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
/// Persistent record of a YouTube video downloaded to the device (Issue #21).
|
||||
/// Files live in the app's documents directory under `videos/<videoId>.mp4`;
|
||||
/// this model tracks the metadata needed to locate, display, and manage them.
|
||||
///
|
||||
/// Lives in the local store, not CloudKit — downloads are per-device.
|
||||
@Model
|
||||
public final class DownloadedVideo {
|
||||
/// YouTube video ID — the primary key (unique).
|
||||
@Attribute(.unique) public var videoId: String = ""
|
||||
public var title: String = ""
|
||||
public var filename: String = ""
|
||||
public var byteCount: Int = 0
|
||||
public var downloadedAt: Date = Date()
|
||||
|
||||
public init(videoId: String, title: String, filename: String, byteCount: Int) {
|
||||
self.videoId = videoId
|
||||
self.title = title
|
||||
self.filename = filename
|
||||
self.byteCount = byteCount
|
||||
self.downloadedAt = Date()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user