浏览代码

Coding

Signed-off-by: marinofaggiana <ios@nextcloud.com>
marinofaggiana 3 年之前
父节点
当前提交
3ef6f55083

+ 1 - 1
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -808,7 +808,7 @@ extension NCManageDatabase {
         return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", account, serverUrl, fileNameConflict))
     }
 
-    func getSubtitles(account: String, serverUrl: String, fileName: String) -> (all:[tableMetadata], exists:[tableMetadata]) {
+    func getSubtitles(account: String, serverUrl: String, fileName: String) -> (all:[tableMetadata], existing:[tableMetadata]) {
 
         let realm = try! Realm()
         let nameOnly = (fileName as NSString).deletingPathExtension + "."

+ 44 - 26
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift

@@ -46,12 +46,12 @@ class NCPlayer: NSObject {
     private weak var detailView: NCViewerMediaDetailView?
     private var observerAVPlayerItemDidPlayToEndTime: Any?
     private var observerAVPlayertTime: Any?
-    private var observerAVPlayertStatus: Any?
 
-    public var player: AVPlayer?
-    public var durationTime: CMTime = .zero
-    public var metadata: tableMetadata
-    public var videoLayer: AVPlayerLayer?
+    var kvoPlayerObservers: Set<NSKeyValueObservation> = []
+    var player: AVPlayer?
+    var durationTime: CMTime = .zero
+    var metadata: tableMetadata
+    var videoLayer: AVPlayerLayer?
 
     // MARK: - View Life Cycle
 
@@ -126,26 +126,46 @@ class NCPlayer: NSObject {
             }
         }
 
-        observerAVPlayertStatus = player?.currentItem?.addObserver(self, forKeyPath: "status", options: [.new, .initial], context: nil)
-    }
+        let observerAVPlayertStatus = self.player?.currentItem?.observe(\.status, options: [.new,.initial]) { player, change in
+
+            if let player = self.player, let playerItem = player.currentItem, let object = player.currentItem, playerItem === object {
+
+                if self.isStartPlayer {
+                    return
+                }
 
-    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
-        switch keyPath {
-        case "status":
-            if let playerItem = self.player?.currentItem,
-                let object = object as? AVPlayerItem,
-                playerItem === object{
                 if (playerItem.status == .readyToPlay || playerItem.status == .failed) {
-                    print("player ready")
-                    self.startPlayer()
+                    if (playerItem.status == .readyToPlay) {
+                        if self.isPlayerReady(player) {
+                            print("Player ready")
+                            self.startPlayer()
+                        }
+                    } else {
+                        print("Player ready to convert")
+                        self.startPlayer()
+                    }
                 } else {
-                    print("player not ready")
+                    print("Player not ready")
                 }
             }
-            break
-        default:
-            break
         }
+
+        if let observerAVPlayertStatus = observerAVPlayertStatus{
+            kvoPlayerObservers.insert(observerAVPlayertStatus)
+        }
+
+    }
+
+    private func isPlayerReady(_ player: AVPlayer) -> Bool {
+
+        let ready = player.currentItem?.status == .readyToPlay || player.currentItem?.status == .failed
+
+        let timeRange = player.currentItem?.loadedTimeRanges.first as? CMTimeRange
+        guard let duration = timeRange?.duration else { return false } // Fail when loadedTimeRanges is empty
+        let timeLoaded = Int(duration.value) / Int(duration.timescale) // value/timescale = seconds
+        let loaded = timeLoaded > 0
+
+        return ready && loaded
     }
 
     func startPlayer() {
@@ -278,21 +298,19 @@ class NCPlayer: NSObject {
             playerPause()
         }
 
+        self.kvoPlayerObservers.forEach { $0.invalidate() }
+        self.kvoPlayerObservers.removeAll()
+
         if let observerAVPlayerItemDidPlayToEndTime = self.observerAVPlayerItemDidPlayToEndTime {
             NotificationCenter.default.removeObserver(observerAVPlayerItemDidPlayToEndTime)
         }
         observerAVPlayerItemDidPlayToEndTime = nil
 
-        if let observerAVPlayertTime = self.observerAVPlayertTime {
-            player?.removeTimeObserver(observerAVPlayertTime)
+        if let observerAVPlayertTime = self.observerAVPlayertTime, let player = player {
+            player.removeTimeObserver(observerAVPlayertTime)
         }
         observerAVPlayertTime = nil
 
-        if observerAVPlayertStatus != nil {
-            player?.currentItem?.removeObserver(self, forKeyPath: "status")
-        }
-        observerAVPlayertStatus = nil
-
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationWillResignActive), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)

+ 191 - 0
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPro/NCPlayer+MFFFDelegate.swift

@@ -0,0 +1,191 @@
+//
+//  NCPlayer+MFFFDelegate
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 31/12/21.
+//  Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import NCCommunication
+import UIKit
+import AVFoundation
+import MediaPlayer
+import Alamofire
+import RealmSwift
+import ffmpegkit
+
+extension NCPlayer: MFFFDelegate {
+
+    // MARK: - NCPlayer
+    @objc func convertVideoDidFinish(_ notification: Notification) {
+        guard let mfffResponse = notification.object as? MFFFResponse,
+              let mfffResponseocId = mfffResponse.ocId else {
+            return
+        }
+        let url = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+        if MFFF.shared.existsMFFFSession(url: url) {
+            MFFF.shared.removeMFFFSession(url: url)
+        }
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: mfffResponseocId), object: nil)
+
+        func updatePlayer() {
+            if mfffResponse.returnCode?.isValueSuccess() ?? false,
+               let urlVideo = mfffResponse.url {
+
+                self.url = urlVideo
+                self.isProxy = false
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+                    self.openAVPlayer()
+                }
+
+            } else if mfffResponse.returnCode?.isValueCancel() ?? false {
+                print("cancel")
+                self.openAVPlayer()
+            } else {
+                MFFF.shared.showMessage(title: "_error_",
+                                      description: "_error_something_wrong_",
+                                      backgroundColor: NCBrandColor.shared.brand,
+                                      color: NCBrandColor.shared.brandText,
+                                      dismissAfterSeconds: NCGlobal.shared.dismissAfterSecond,
+                                      view: viewController?.view)
+            }
+        }
+
+        if let currentTopViewController = UIApplication.topViewController() as? NCViewerMediaPage {
+            if let responseOcId = mfffResponse.ocId {
+                let ocId = currentTopViewController.currentViewController.metadata.ocId
+
+                if responseOcId == ocId {
+                    updatePlayer()
+                }
+            }
+        }
+    }
+
+    func convertVideo(withAlert: Bool) {
+
+        let url = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))
+        let urlOut = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: NCGlobal.shared.fileNameVideoEncoded))
+        let tableVideo = NCManageDatabase.shared.getVideo(metadata: metadata)
+        let view: UIView? = viewController?.view
+
+        func MFFFConvertVideo() {
+            MFFF.shared.convertVideo(url: url,
+                                   urlOut: urlOut,
+                                   serverUrl: self.metadata.serverUrl,
+                                   fileName: self.metadata.fileNameView,
+                                   contentType: self.metadata.contentType,
+                                   ocId: self.metadata.ocId,
+                                   codecNameVideo: tableVideo?.codecNameVideo,
+                                   codecNameAudio: tableVideo?.codecNameAudio,
+                                   channelLayout: tableVideo?.codecAudioChannelLayout,
+                                   languageAudio: tableVideo?.codecAudioLanguage,
+                                   maxCompatibility: tableVideo?.codecMaxCompatibility ?? false,
+                                   codecQuality: tableVideo?.codecQuality,
+                                   verifyAlreadyCodec: false)
+
+        }
+
+        if !(MFFF.shared.existsMFFFSession(url: url)) {
+
+            if !CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
+
+                let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""),
+                                                        message: NSLocalizedString("_video_must_download_", comment: ""),
+                                                        preferredStyle: .alert)
+
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
+                    self.downloadVideo(requiredConvert: true)
+                }))
+
+                alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in
+                    MFFF.shared.showMessage(description: "_conversion_available_",
+                                          backgroundColor: NCBrandColor.shared.brand,
+                                          color: NCBrandColor.shared.brandText,
+                                          image: UIImage(named: "iconInfo"),
+                                          dismissAfterSeconds: NCGlobal.shared.dismissAfterSecond,
+                                          view: view)
+                }))
+
+                appDelegate.window?.rootViewController?.present(alertController, animated: true)
+
+            } else {
+
+                if withAlert {
+
+                    let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""),
+                                                            message: NSLocalizedString("_video_format_not_recognized_", comment: ""),
+                                                            preferredStyle: .alert)
+
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in
+                        MFFFConvertVideo()
+                    }))
+
+                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in
+                        MFFF.shared.showMessage(description: "_conversion_available_",
+                                              backgroundColor: NCBrandColor.shared.brand,
+                                              color: NCBrandColor.shared.brandText,
+                                              image: UIImage(named: "iconInfo"),
+                                              dismissAfterSeconds: NCGlobal.shared.dismissAfterSecond,
+                                              view: view)
+                    }))
+
+                    appDelegate.window?.rootViewController?.present(alertController, animated: true)
+
+                } else {
+                    MFFFConvertVideo()
+                }
+            }
+        }
+    }
+
+    // MARK: - MFFFDelegate
+
+    func sessionStarted(url: URL, ocId: String?, traces: [MFFFTrace]?) {
+
+        self.playerToolBar?.hide()
+    }
+
+    func sessionProgress(url: URL, ocId: String?, traces: [MFFFTrace]?, progress: Float) {
+
+        guard self.metadata.ocId == ocId, let traces = traces, self.viewController is NCViewerMedia else { return }
+
+        self.playerToolBar?.hide()
+
+        var description = NSLocalizedString("_stay_app_foreground_", value: "Stay with the app in the foreground…", comment: "")
+
+        if (traces.filter { $0.conversion == true }).isEmpty == false {
+            description = NSLocalizedString("_stay_app_foreground_", value: "Stay with the app in the foreground…", comment: "") + "\n" + NSLocalizedString("_reuired_conversion_", value: "This video takes a long time to convert.", comment: "")
+        }
+
+        MFFF.shared.messageProgress(title: "_video_being_processed_",
+                                  description: description,
+                                  descriptionBottomAutoHidden: "_video_tap_for_close_",
+                                  backgroundColor: NCBrandColor.shared.brand,
+                                  color: NCBrandColor.shared.brandText,
+                                  view: viewController!.view,
+                                  progress: progress)
+    }
+
+    func sessionEnded(url: URL, ocId: String?, returnCode: Int?, traces: [MFFFTrace]?, maxCompatibility: Bool, codecQuality: String?) {
+
+        if self.metadata.ocId == ocId {
+            MFFF.shared.dismissMessage()
+            self.playerToolBar?.show()
+        }
+
+        if returnCode == 0, let traces = traces, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+            let traceVideo = traces.filter { $0.codecType == "video" }.first
+            let traceAudio = traces.filter { $0.codecType == "audio" }.first
+
+            NCManageDatabase.shared.addVideoCodec(metadata: metadata,
+                                                  codecNameVideo: traceVideo?.codecName,
+                                                  codecNameAudio: traceAudio?.codecName,
+                                                  codecAudioChannelLayout: traceAudio?.channelLayout,
+                                                  codecAudioLanguage: traceAudio?.language,
+                                                  codecMaxCompatibility: maxCompatibility,
+                                                  codecQuality: codecQuality)
+        }
+    }
+}

+ 56 - 0
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCPlayerToolBar+SubtitlePlayerToolBarDelegate.swift

@@ -0,0 +1,56 @@
+//
+//  NCPlayerToolBar+SubtitlePlayerToolBarDelegate.swift
+//  Nextcloud
+//
+//  Created by Federico Malagoni on 11/03/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+
+protocol SubtitlePlayerToolBarDelegate: AnyObject {
+    func subtitleIconTapped()
+    func changeSubtitleIconTo(visible: Bool)
+    func showIconSubtitle()
+}
+
+extension NCPlayerToolBar: SubtitlePlayerToolBarDelegate {
+
+    func subtitleIconTapped() {
+
+        self.ncplayer?.hideOrShowSubtitle()
+    }
+
+    func changeSubtitleIconTo(visible: Bool) {
+
+        let color: UIColor = visible ? .white : .lightGray
+        self.subtitleButton.setImage(NCUtility.shared.loadImage(named: "captions.bubble", color: color), for: .normal)
+    }
+
+    func showIconSubtitle() {
+
+        self.subtitleButton.isEnabled = true
+        self.subtitleButton.isHidden = false
+    }
+
+    func hideIconSubtitle() {
+
+        self.subtitleButton.isEnabled = false
+        self.subtitleButton.isHidden = true
+    }
+}

+ 46 - 0
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitlePlayer+PlayerSubtitleDelegate.swift

@@ -0,0 +1,46 @@
+//
+//  NCPlayer+PlayerSubtitleDelegate.swift
+//  Nextcloud
+//
+//  Created by Federico Malagoni on 11/03/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import Foundation
+import NCCommunication
+import UIKit
+import AVFoundation
+import MediaPlayer
+import Alamofire
+import RealmSwift
+
+public protocol PlayerSubtitleDelegate: AnyObject {
+    func hideOrShowSubtitle()
+}
+
+extension NCPlayer: PlayerSubtitleDelegate {
+
+    func hideOrShowSubtitle() {
+        if self.isSubtitleShowed {
+            self.hideSubtitle()
+            self.isSubtitleShowed = false
+        } else {
+            self.showAlertSubtitles()
+        }
+    }
+}

+ 4 - 4
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitlePlayer.swift

@@ -126,14 +126,14 @@ extension NCPlayer {
                 }
             }
         }
-        let results = NCManageDatabase.shared.getSubtitles(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName)
-        if !results.exists.isEmpty {
-            for subtitle in results.exists {
+        let (all, existing) = NCManageDatabase.shared.getSubtitles(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName)
+        if !existing.isEmpty {
+            for subtitle in existing {
                 let subtitleUrl = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(subtitle.ocId, fileNameView: subtitle.fileName))
                 self.subtitleUrls.append(subtitleUrl)
             }
         }
-        if results.all.count != results.exists.count {
+        if all.count != existing.count {
             NCContentPresenter.shared.messageNotification("_info_", description: "_subtitle_not_dowloaded_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError)
         }
         self.setSubtitleToolbarIcon(subtitleUrls: subtitleUrls)

+ 3 - 8
iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift

@@ -108,7 +108,8 @@ class NCViewerMediaPage: UIViewController {
     }
 
     deinit {
-        print("#deinit NCViewerMediaPage")
+        
+        print("deinit NCViewerMediaPage")
     }
 
     override func viewDidDisappear(_ animated: Bool) {
@@ -469,16 +470,12 @@ extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerD
             direction = .reverse
         }
 
-        currentViewController.ncplayer?.deactivateObserver()
-        
         let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex])
         pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil)
     }
     
     func reloadCurrentPage() {
-        
-        currentViewController.ncplayer?.deactivateObserver()
-        
+
         let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex])
         viewerMedia.autoPlay = false
         pageViewController.setViewControllers([viewerMedia], direction: .forward, animated: false, completion: nil)
@@ -488,8 +485,6 @@ extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerD
 
         currentIndex = index
 
-        currentViewController.ncplayer?.deactivateObserver()
-
         let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex])
         viewerMedia.autoPlay = autoPlay
         pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil)