diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index b720044e..ccca821a 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -573,20 +573,36 @@ final class PlayerModel: ObservableObject { } func setRelatedAutoplayItem() { - guard let video = currentVideo?.related.randomElement() else { return } + guard let video = currentVideo else { return } + let related = video.related.filter { $0.videoID != autoplayItem?.video?.videoID } - let item = PlayerQueueItem(video) - autoplayItem = item - autoplayItemSource = video + let watchFetchRequest = Watch.fetchRequest() + watchFetchRequest.predicate = NSPredicate(format: "videoID IN %@", related.map(\.videoID) as [String]) - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in - guard let self = self else { return } - self.accounts.api.loadDetails(item, completionHandler: { newItem in - guard newItem.videoID == self.autoplayItem?.videoID else { return } - self.autoplayItem = newItem - self.updateRemoteCommandCenter() - self.controls.objectWillChange.send() - }) + let results = try? context.fetch(watchFetchRequest) + + context.perform { [weak self] in + guard let self = self, + let results = results else { return } + let resultsIds = results.map(\.videoID) + + guard let autoplayVideo = related.filter({ !resultsIds.contains($0.videoID) }).randomElement() else { + return + } + + let item = PlayerQueueItem(autoplayVideo) + self.autoplayItem = item + self.autoplayItemSource = video + + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in + guard let self = self else { return } + self.accounts.api.loadDetails(item, completionHandler: { newItem in + guard newItem.videoID == self.autoplayItem?.videoID else { return } + self.autoplayItem = newItem + self.updateRemoteCommandCenter() + self.controls.objectWillChange.send() + }) + } } } diff --git a/Shared/Player/PlayerQueueRow.swift b/Shared/Player/PlayerQueueRow.swift index 6153a0d4..0abf7af9 100644 --- a/Shared/Player/PlayerQueueRow.swift +++ b/Shared/Player/PlayerQueueRow.swift @@ -6,6 +6,7 @@ import SwiftUI struct PlayerQueueRow: View { let item: PlayerQueueItem var history = false + var autoplay = false @Binding var fullScreen: Bool @EnvironmentObject private var player @@ -14,9 +15,10 @@ struct PlayerQueueRow: View { @FetchRequest private var watchRequest: FetchedResults - init(item: PlayerQueueItem, history: Bool = false, fullScreen: Binding = .constant(false)) { + init(item: PlayerQueueItem, history: Bool = false, autoplay: Bool = false, fullScreen: Binding = .constant(false)) { self.item = item self.history = history + self.autoplay = autoplay _fullScreen = fullScreen _watchRequest = FetchRequest( entity: Watch.entity(), @@ -48,6 +50,10 @@ struct PlayerQueueRow: View { if closePiPOnNavigation, player.playingInPictureInPicture { player.closePiP() } + + if autoplay { + player.resetAutoplay() + } } label: { VideoBanner(video: item.video, playbackTime: watchStoppedAt, videoDuration: watch?.videoDuration) } diff --git a/Shared/Player/PlayerQueueView.swift b/Shared/Player/PlayerQueueView.swift index 419428a0..e8cc4775 100644 --- a/Shared/Player/PlayerQueueView.swift +++ b/Shared/Player/PlayerQueueView.swift @@ -20,6 +20,9 @@ struct PlayerQueueView: View { var body: some View { List { Group { + if player.playbackMode == .related { + autoplaying + } playingNext if sidebarQueue { related @@ -46,10 +49,42 @@ struct PlayerQueueView: View { #endif } + @ViewBuilder var autoplaying: some View { + Section(header: autoplayingHeader) { + if let item = player.autoplayItem { + PlayerQueueRow(item: item, autoplay: true) + } else { + Group { + if player.currentItem.isNil { + Text("Not Playing") + } else { + Text("Finding something to play...") + } + } + .foregroundColor(.secondary) + } + } + } + + var autoplayingHeader: some View { + HStack { + Text("Autoplaying Next") + Spacer() + Button { + player.setRelatedAutoplayItem() + } label: { + Label("Find Other", systemImage: "arrow.triangle.2.circlepath.circle") + .labelStyle(.iconOnly) + } + .disabled(player.currentItem.isNil) + .buttonStyle(.plain) + } + } + var playingNext: some View { - Section(header: Text("Playing Next")) { + Section(header: Text("Queue")) { if player.queue.isEmpty { - Text("Playback queue is empty") + Text("Queue is empty") .foregroundColor(.secondary) } @@ -73,7 +108,7 @@ struct PlayerQueueView: View { var playedPreviously: some View { Group { if !visibleWatches.isEmpty { - Section(header: Text("Played Previously")) { + Section(header: Text("History")) { ForEach(visibleWatches, id: \.videoID) { watch in PlayerQueueRow( item: PlayerQueueItem.from(watch, video: player.historyVideo(watch.videoID)),