From b2fa5c2620409d57a6c8f6a789c2866a565e5798 Mon Sep 17 00:00:00 2001
From: Arkadiusz Fal <arek@arekf.net>
Date: Thu, 30 Jun 2022 00:44:32 +0200
Subject: [PATCH] Video loading errors reporting

---
 Model/Applications/VideosAPI.swift | 14 +++++++++++---
 Model/Player/PlayerModel.swift     |  4 ++++
 Model/Player/PlayerQueue.swift     | 14 ++++++++++----
 Model/Player/PlayerStreams.swift   |  4 ++++
 Shared/YatteeApp.swift             |  1 +
 5 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/Model/Applications/VideosAPI.swift b/Model/Applications/VideosAPI.swift
index 98e6500f..c2f55baf 100644
--- a/Model/Applications/VideosAPI.swift
+++ b/Model/Applications/VideosAPI.swift
@@ -58,14 +58,22 @@ protocol VideosAPI {
 
     func channelPlaylist(_ id: String) -> Resource?
 
-    func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void)
+    func loadDetails(
+        _ item: PlayerQueueItem,
+        failureHandler: ((RequestError) -> Void)?,
+        completionHandler: @escaping (PlayerQueueItem) -> Void
+    )
     func shareURL(_ item: ContentItem, frontendHost: String?, time: CMTime?) -> URL?
 
     func comments(_ id: Video.ID, page: String?) -> Resource?
 }
 
 extension VideosAPI {
-    func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void = { _ in }) {
+    func loadDetails(
+        _ item: PlayerQueueItem,
+        failureHandler: ((RequestError) -> Void)? = nil,
+        completionHandler: @escaping (PlayerQueueItem) -> Void = { _ in }
+    ) {
         guard (item.video?.streams ?? []).isEmpty else {
             completionHandler(item)
             return
@@ -80,7 +88,7 @@ extension VideosAPI {
             newItem.video = video
 
             completionHandler(newItem)
-        }
+        }.onFailure { failureHandler?($0) }
     }
 
     func shareURL(_ item: ContentItem, frontendHost: String? = nil, time: CMTime? = nil) -> URL? {
diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift
index 6091e21b..efd6f074 100644
--- a/Model/Player/PlayerModel.swift
+++ b/Model/Player/PlayerModel.swift
@@ -98,6 +98,8 @@ final class PlayerModel: ObservableObject {
             backend.networkState.player = self
         }
     }}
+    var navigation: NavigationModel
+
     var context: NSManagedObjectContext = PersistenceController.shared.container.viewContext
     var backgroundContext = PersistenceController.shared.container.newBackgroundContext()
 
@@ -134,12 +136,14 @@ final class PlayerModel: ObservableObject {
         accounts: AccountsModel = AccountsModel(),
         comments: CommentsModel = CommentsModel(),
         controls: PlayerControlsModel = PlayerControlsModel(),
+        navigation: NavigationModel = NavigationModel(),
         playerTime: PlayerTimeModel = PlayerTimeModel(),
         networkState: NetworkStateModel = NetworkStateModel()
     ) {
         self.accounts = accounts
         self.comments = comments
         self.controls = controls
+        self.navigation = navigation
         self.playerTime = playerTime
         self.networkState = networkState
 
diff --git a/Model/Player/PlayerQueue.swift b/Model/Player/PlayerQueue.swift
index 08b78b7a..e84ad63e 100644
--- a/Model/Player/PlayerQueue.swift
+++ b/Model/Player/PlayerQueue.swift
@@ -93,7 +93,7 @@ extension PlayerModel {
 
         currentItem = newItem
 
-        accounts.api.loadDetails(newItem) { newItem in
+        accounts.api.loadDetails(newItem, failureHandler: videoLoadFailureHandler) { newItem in
             self.playItem(newItem, at: time)
         }
     }
@@ -136,7 +136,7 @@ extension PlayerModel {
         }
 
         if loadDetails {
-            accounts.api.loadDetails(item) { [weak self] newItem in
+            accounts.api.loadDetails(item, failureHandler: videoLoadFailureHandler) { [weak self] newItem in
                 guard let self = self else { return }
                 videoDetailsLoadHandler(newItem.video, newItem)
 
@@ -197,10 +197,16 @@ extension PlayerModel {
     func loadQueueVideoDetails(_ item: PlayerQueueItem) {
         guard !accounts.current.isNil, !item.hasDetailsLoaded else { return }
 
-        accounts.api.loadDetails(item) { newItem in
+        accounts.api.loadDetails(item, completionHandler: { newItem in
             if let index = self.queue.firstIndex(where: { $0.id == item.id }) {
                 self.queue[index] = newItem
             }
-        }
+        })
+    }
+
+    private func videoLoadFailureHandler(_ error: RequestError) {
+        navigation.presentAlert(title: "Could not load video", message: error.userMessage)
+        videoBeingOpened = nil
+        currentItem = nil
     }
 }
diff --git a/Model/Player/PlayerStreams.swift b/Model/Player/PlayerStreams.swift
index 0e3bc516..0df73db7 100644
--- a/Model/Player/PlayerStreams.swift
+++ b/Model/Player/PlayerStreams.swift
@@ -47,6 +47,10 @@ extension PlayerModel {
                 }
             }
             .onCompletion(onCompletion)
+            .onFailure { [weak self] responseError in
+                self?.navigation.presentAlert(title: "Could not load streams", message: responseError.userMessage)
+                self?.videoBeingOpened = nil
+            }
     }
 
     func streamsWithInstance(instance: Instance, streams: [Stream]) -> [Stream] {
diff --git a/Shared/YatteeApp.swift b/Shared/YatteeApp.swift
index 0edafb3b..4a598c6f 100644
--- a/Shared/YatteeApp.swift
+++ b/Shared/YatteeApp.swift
@@ -196,6 +196,7 @@ struct YatteeApp: App {
         player.accounts = accounts
         player.comments = comments
         player.controls = playerControls
+        player.navigation = navigation
         player.networkState = networkState
         player.playerTime = playerTime