mirror of
https://github.com/yattee/yattee.git
synced 2026-11-03 18:37:49 +00:00
Track the active chapter in PlayerState instead of per-tick time reads
Chapter lists highlighted the active row by reading currentTime in the view body, re-rendering every row many times per second during playback. PlayerState now stores a change-guarded activeChapterID updated when time or chapters change, so views observe only chapter transitions.
This commit is contained in:
@@ -226,7 +226,9 @@ final class PlayerState {
|
|||||||
// MARK: - Time & Progress
|
// MARK: - Time & Progress
|
||||||
|
|
||||||
/// Current playback time in seconds.
|
/// Current playback time in seconds.
|
||||||
var currentTime: TimeInterval = 0
|
var currentTime: TimeInterval = 0 {
|
||||||
|
didSet { updateActiveChapter() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Total duration in seconds.
|
/// Total duration in seconds.
|
||||||
var duration: TimeInterval = 0
|
var duration: TimeInterval = 0
|
||||||
@@ -356,11 +358,27 @@ final class PlayerState {
|
|||||||
// MARK: - Chapters
|
// MARK: - Chapters
|
||||||
|
|
||||||
/// Video chapters if available.
|
/// Video chapters if available.
|
||||||
var chapters: [VideoChapter] = []
|
var chapters: [VideoChapter] = [] {
|
||||||
|
didSet { updateActiveChapter() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ID of the chapter containing the current playback time. Stored and
|
||||||
|
/// change-guarded so views highlighting the active chapter observe only
|
||||||
|
/// chapter transitions, not every time-pos tick — reading `currentTime`
|
||||||
|
/// from a panel body re-renders it many times per second during playback.
|
||||||
|
private(set) var activeChapterID: UUID?
|
||||||
|
|
||||||
/// Current chapter based on playback time.
|
/// Current chapter based on playback time.
|
||||||
var currentChapter: VideoChapter? {
|
var currentChapter: VideoChapter? {
|
||||||
chapters.last { $0.startTime <= currentTime }
|
guard let activeChapterID else { return nil }
|
||||||
|
return chapters.first { $0.id == activeChapterID }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateActiveChapter() {
|
||||||
|
let id = chapters.last { $0.startTime <= currentTime }?.id
|
||||||
|
if activeChapterID != id {
|
||||||
|
activeChapterID = id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Storyboards
|
// MARK: - Storyboards
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ struct ChaptersPanelPage: View {
|
|||||||
private var playerState: PlayerState? { playerService?.state }
|
private var playerState: PlayerState? { playerService?.state }
|
||||||
|
|
||||||
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
||||||
private var currentTime: TimeInterval { playerState?.currentTime ?? 0 }
|
|
||||||
private var storyboard: Storyboard? { playerState?.preferredStoryboard }
|
private var storyboard: Storyboard? { playerState?.preferredStoryboard }
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -50,9 +49,8 @@ struct ChaptersPanelPage: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
||||||
let nextChapter = chapters.first { $0.startTime > chapter.startTime }
|
// Observes only chapter transitions, not per-tick currentTime updates.
|
||||||
let endTime = nextChapter?.startTime ?? .infinity
|
playerState?.activeChapterID == chapter.id
|
||||||
return currentTime >= chapter.startTime && currentTime < endTime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preloadChapterSheets() async {
|
private func preloadChapterSheets() async {
|
||||||
|
|||||||
@@ -230,7 +230,6 @@ struct InfoPanelPage: View {
|
|||||||
// MARK: - Inline Sections (prototype)
|
// MARK: - Inline Sections (prototype)
|
||||||
|
|
||||||
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
||||||
private var currentTime: TimeInterval { playerState?.currentTime ?? 0 }
|
|
||||||
private var storyboard: Storyboard? { playerState?.preferredStoryboard }
|
private var storyboard: Storyboard? { playerState?.preferredStoryboard }
|
||||||
private var relatedVideos: [Video] { video.relatedVideos ?? [] }
|
private var relatedVideos: [Video] { video.relatedVideos ?? [] }
|
||||||
|
|
||||||
@@ -308,9 +307,8 @@ struct InfoPanelPage: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
||||||
let nextChapter = chapters.first { $0.startTime > chapter.startTime }
|
// Observes only chapter transitions, not per-tick currentTime updates.
|
||||||
let endTime = nextChapter?.startTime ?? .infinity
|
playerState?.activeChapterID == chapter.id
|
||||||
return currentTime >= chapter.startTime && currentTime < endTime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preloadChapterSheets() async {
|
private func preloadChapterSheets() async {
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ struct TVChaptersPanelList: View {
|
|||||||
private var playerState: PlayerState? { playerService?.state }
|
private var playerState: PlayerState? { playerService?.state }
|
||||||
|
|
||||||
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
private var chapters: [VideoChapter] { playerState?.chapters ?? [] }
|
||||||
private var currentTime: TimeInterval { playerState?.currentTime ?? 0 }
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollViewReader { proxy in
|
ScrollViewReader { proxy in
|
||||||
@@ -124,9 +123,8 @@ struct TVChaptersPanelList: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
private func isChapterActive(_ chapter: VideoChapter) -> Bool {
|
||||||
let nextChapter = chapters.first { $0.startTime > chapter.startTime }
|
// Observes only chapter transitions, not per-tick currentTime updates.
|
||||||
let endTime = nextChapter?.startTime ?? .infinity
|
playerState?.activeChapterID == chapter.id
|
||||||
return currentTime >= chapter.startTime && currentTime < endTime
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user