Browse Source

improvements video

marinofaggiana 3 years ago
parent
commit
379ef8f732

+ 8 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -286,6 +286,8 @@
 		F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
 		F7B8CD96261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
 		F7B8CD9B261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */; };
+		F7BAA040268DECC200AEA403 /* NCViewerVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAA03F268DECC200AEA403 /* NCViewerVideo.swift */; };
+		F7BAA042268DF78F00AEA403 /* NCViewerVideoToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAA041268DF78F00AEA403 /* NCViewerVideoToolBar.swift */; };
 		F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; };
 		F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; };
 		F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */; };
@@ -691,6 +693,8 @@
 		F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
 		F7B8B82F25681C3400967775 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; };
 		F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingChunkedUpload.swift; sourceTree = "<group>"; };
+		F7BAA03F268DECC200AEA403 /* NCViewerVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerVideo.swift; sourceTree = "<group>"; };
+		F7BAA041268DF78F00AEA403 /* NCViewerVideoToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerVideoToolBar.swift; sourceTree = "<group>"; };
 		F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCDatabase.swift; sourceTree = "<group>"; };
 		F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCManageDatabase.swift; sourceTree = "<group>"; };
 		F7BB04851FD58ACB00BBFD2A /* cs-CZ */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "cs-CZ"; path = "cs-CZ.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -1236,6 +1240,8 @@
 			children = (
 				F703FFEA268CA60D00FA1459 /* NCKTVHTTPCache.swift */,
 				F752011C25480387000BF3A7 /* NCViewerAVPlayerViewController.swift */,
+				F7BAA03F268DECC200AEA403 /* NCViewerVideo.swift */,
+				F7BAA041268DF78F00AEA403 /* NCViewerVideoToolBar.swift */,
 			);
 			path = NCViewerVideo;
 			sourceTree = "<group>";
@@ -2114,6 +2120,7 @@
 				F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */,
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
+				F7BAA042268DF78F00AEA403 /* NCViewerVideoToolBar.swift in Sources */,
 				F70753F12542A9A200972D44 /* NCViewerImageZoom.swift in Sources */,
 				F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
@@ -2158,6 +2165,7 @@
 				F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */,
 				F7501C332212E57500FB1415 /* NCMedia.swift in Sources */,
 				F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */,
+				F7BAA040268DECC200AEA403 /* NCViewerVideo.swift in Sources */,
 				F7C1EEA525053A9C00866ACC /* NCDataSource.swift in Sources */,
 				F713FF002472764100214AF6 /* UIImage+animatedGIF.m in Sources */,
 				F749C10B23C4A5340027D966 /* NCIntroCollectionViewCell.swift in Sources */,

+ 8 - 6
iOSClient/Viewer/NCViewerImage/NCViewerImage.storyboard

@@ -33,7 +33,7 @@
                                     <constraint firstAttribute="height" constant="1" id="F4E-lI-3jZ"/>
                                 </constraints>
                             </progressView>
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JKq-ki-dtz">
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JKq-ki-dtz" customClass="NCViewerVideoToolBar" customModule="Nextcloud" customModuleProvider="target">
                                 <rect key="frame" x="0.0" y="812" width="414" height="50"/>
                                 <subviews>
                                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vaQ-iz-6jn">
@@ -45,7 +45,7 @@
                                         <color key="tintColor" systemColor="labelColor"/>
                                         <state key="normal" image="play.fill" catalog="system"/>
                                         <connections>
-                                            <action selector="playerPause:" destination="ne8-hS-cp3" eventType="touchUpInside" id="DPP-bu-HdY"/>
+                                            <action selector="playerPause:" destination="JKq-ki-dtz" eventType="touchUpInside" id="g2n-Lo-Kum"/>
                                         </connections>
                                     </button>
                                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EEV-Aj-0lu">
@@ -57,7 +57,7 @@
                                         <color key="tintColor" systemColor="labelColor"/>
                                         <state key="normal" title="Button" image="audioOn"/>
                                         <connections>
-                                            <action selector="setMute:" destination="ne8-hS-cp3" eventType="touchUpInside" id="gc7-ym-5Dg"/>
+                                            <action selector="setMute:" destination="JKq-ki-dtz" eventType="touchUpInside" id="VBj-CW-RaB"/>
                                         </connections>
                                     </button>
                                 </subviews>
@@ -69,6 +69,10 @@
                                     <constraint firstItem="vaQ-iz-6jn" firstAttribute="leading" secondItem="JKq-ki-dtz" secondAttribute="leading" constant="10" id="SpM-Ue-jtI"/>
                                     <constraint firstAttribute="height" constant="50" id="i5H-Nn-hox"/>
                                 </constraints>
+                                <connections>
+                                    <outlet property="muteButton" destination="EEV-Aj-0lu" id="rZK-aK-b9M"/>
+                                    <outlet property="playButton" destination="vaQ-iz-6jn" id="9i8-js-Jiy"/>
+                                </connections>
                             </view>
                         </subviews>
                         <viewLayoutGuide key="safeArea" id="Ozy-9S-dMl"/>
@@ -90,10 +94,8 @@
                     <navigationItem key="navigationItem" id="azh-DM-TLz"/>
                     <nil key="simulatedBottomBarMetrics"/>
                     <connections>
-                        <outlet property="muteButton" destination="EEV-Aj-0lu" id="Brf-jh-7So"/>
-                        <outlet property="playButton" destination="vaQ-iz-6jn" id="coX-gh-2aN"/>
                         <outlet property="progressView" destination="sD9-1i-ZdY" id="nag-cc-Up3"/>
-                        <outlet property="toolBar" destination="JKq-ki-dtz" id="iTS-hJ-3EA"/>
+                        <outlet property="toolBar" destination="JKq-ki-dtz" id="dhN-fe-HNh"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="qhs-BU-mEq" userLabel="First Responder" sceneMemberID="firstResponder"/>

+ 15 - 184
iOSClient/Viewer/NCViewerImage/NCViewerImage.swift

@@ -28,11 +28,8 @@ import NCCommunication
 class NCViewerImage: UIViewController {
 
     @IBOutlet weak var progressView: UIProgressView!
+    @IBOutlet weak var toolBar: NCViewerVideoToolBar!
     
-    @IBOutlet weak var toolBar: UIView!
-    @IBOutlet weak var playButton: UIButton!
-    @IBOutlet weak var muteButton: UIButton!
-
     enum ScreenMode {
         case full, normal
     }
@@ -58,11 +55,7 @@ class NCViewerImage: UIViewController {
     var singleTapGestureRecognizer: UITapGestureRecognizer!
     var longtapGestureRecognizer: UILongPressGestureRecognizer!
     
-    private var player: AVPlayer?
-    private var videoLayer: AVPlayerLayer?
-    private var timeObserver: Any?
-    private var rateObserver: Any?
-    var pictureInPictureOcId: String = ""
+    var viewerVideo: NCViewerVideo?
     var textColor: UIColor = NCBrandColor.shared.label
 
     // MARK: - View Life Cycle
@@ -104,8 +97,6 @@ class NCViewerImage: UIViewController {
         progressView.tintColor = NCBrandColor.shared.brandElement
         progressView.trackTintColor = .clear
         progressView.progress = 0
-        
-        setToolBar()
     }
     
     override func viewWillAppear(_ animated: Bool) {
@@ -139,8 +130,6 @@ class NCViewerImage: UIViewController {
         NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object:nil)
-        
-        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
     }
     
     override func viewWillDisappear(_ animated: Bool) {
@@ -148,7 +137,7 @@ class NCViewerImage: UIViewController {
 
         if let navigationController = self.navigationController {
             if !navigationController.viewControllers.contains(self) {
-                videoStop()
+                self.viewerVideo?.videoStop()
             }
         }
     }
@@ -163,8 +152,6 @@ class NCViewerImage: UIViewController {
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil)
-        
-        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
     }
     
     @objc func viewUnload() {
@@ -180,13 +167,6 @@ class NCViewerImage: UIViewController {
     
     //MARK: - NotificationCenter
 
-    @objc func applicationDidEnterBackground(_ notification: NSNotification) {
-        
-        if currentMetadata.typeFile == NCGlobal.shared.metadataTypeFileVideo {
-            player?.pause()
-        }
-    }
-    
     @objc func downloadedFile(_ notification: NSNotification) {
         
         if let userInfo = notification.userInfo as NSDictionary? {
@@ -276,7 +256,7 @@ class NCViewerImage: UIViewController {
     }
     
     @objc func changeTheming() {
-        toolBar.tintColor = NCBrandColor.shared.brandElement
+        //toolBar.tintColor = NCBrandColor.shared.brandElement
     }
     
     //
@@ -298,7 +278,7 @@ class NCViewerImage: UIViewController {
                 if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
                     
                     AudioServicesPlaySystemSound(1519) // peek feedback
-                    self.videoPlay(metadata: metadata)
+                    self.viewerVideo?.videoPlay(metadata: metadata)
                     
                 } else {
                     
@@ -323,7 +303,7 @@ class NCViewerImage: UIViewController {
                             
                             if gestureRecognizer.state == .changed || gestureRecognizer.state == .began {
                                 AudioServicesPlaySystemSound(1519) // peek feedback
-                                self.videoPlay(metadata: metadata)
+                                self.viewerVideo?.videoPlay(metadata: metadata)
                             }
                         }
                     }
@@ -334,7 +314,7 @@ class NCViewerImage: UIViewController {
             
             currentViewerImageZoom?.statusViewImage.isHidden = false
             currentViewerImageZoom?.statusLabel.isHidden = false
-            videoStop()
+            self.viewerVideo?.videoStop()
         }
     }
     
@@ -398,156 +378,6 @@ class NCViewerImage: UIViewController {
         
         return image
     }
-        
-    //MARK: - Video
-    
-    func videoPlay(metadata: tableMetadata) {
-           
-        NCKTVHTTPCache.shared.startProxy(user: appDelegate.user, password: appDelegate.password, metadata: metadata)
-        
-        func play(url: URL) {
-            
-            self.player = AVPlayer(url: url)
-            self.player?.isMuted = CCUtility.getAudioMute()
-            self.videoLayer = AVPlayerLayer(player: self.player)
-            
-            if self.videoLayer != nil && self.currentViewerImageZoom != nil {
-            
-                self.videoLayer!.frame = self.currentViewerImageZoom!.imageView.bounds
-                self.videoLayer!.videoGravity = AVLayerVideoGravity.resizeAspectFill
-                
-                self.currentViewerImageZoom!.imageView.layer.addSublayer(self.videoLayer!)
-                
-                // At end go back to start
-                NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { (notification) in
-                    if let item = notification.object as? AVPlayerItem, let currentItem = self.player?.currentItem, item == currentItem {
-                        self.player?.seek(to: .zero)
-                        if !self.currentMetadata.livePhoto {
-                            NCManageDatabase.shared.deleteVideoTime(metadata: self.currentMetadata)
-                        }
-                    }
-                }
-                            
-                self.rateObserver = self.player?.addObserver(self, forKeyPath: "rate", options: [], context: nil)
-                
-                if self.pictureInPictureOcId != metadata.ocId {
-                    self.player?.play()
-                }
-            }
-        }
-        
-        //NCNetworking.shared.getVideoUrl(metadata: metadata) { url in
-        //            if let url = url {
-
-        if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
-            play(url: url)
-        }
-    }
-    
-    func videoStop() {
-        
-        player?.pause()
-        player?.seek(to: CMTime.zero)
-        progressView.progress = 0
-        
-        if let timeObserver = timeObserver {
-            player?.removeTimeObserver(timeObserver)
-            self.timeObserver = nil
-        }
-        
-        if rateObserver != nil {
-            player?.removeObserver(self, forKeyPath: "rate")
-            NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
-            NCKTVHTTPCache.shared.stopProxy(metadata: currentMetadata)
-            self.rateObserver = nil
-        }
-               
-        videoLayer?.removeFromSuperlayer()
-    }
-    
-    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
-        
-        if keyPath != nil && keyPath == "rate" {
-            
-            setToolBar()
-            
-            if ((player?.rate) == 1) {
-                
-                if let time = NCManageDatabase.shared.getVideoTime(metadata: self.currentMetadata) {
-                    player?.seek(to: time)
-                    player?.isMuted = CCUtility.getAudioMute()
-                }
-                
-                if rateObserver != nil && !currentMetadata.livePhoto {
-                    self.progressView.progress = 0
-                    if let duration = self.player?.currentItem?.asset.duration {
-                        let durationSeconds = Double(CMTimeGetSeconds(duration))
-                        if durationSeconds > 0 {
-                            let width = Double(self.progressView.bounds.width)
-                            let interval = (0.5 * durationSeconds) / width
-                            let time = CMTime(seconds: interval, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
-                            if CMTIME_IS_VALID(time) && CMTimeCompare(time, .zero) != 0 {
-                                self.timeObserver = self.player?.addPeriodicTimeObserver(forInterval: time, queue: .main, using: { [weak self] time in
-                                    let durationSeconds = Float(CMTimeGetSeconds(duration))
-                                    if durationSeconds > 0 {
-                                        let currentTimeSeconds = Float(CMTimeGetSeconds(time))
-                                        self?.progressView.progress = currentTimeSeconds / durationSeconds
-                                    } else {
-                                        self?.progressView.progress = 0
-                                    }
-                                })
-                            }
-                        }
-                    }
-                }
-                
-            } else if !self.currentMetadata.livePhoto {
-                
-                if let time = player?.currentTime(), let duration = self.player?.currentItem?.asset.duration {
-                    let timeSecond = Double(CMTimeGetSeconds(time))
-                    let durationSeconds = Double(CMTimeGetSeconds(duration))
-                    if timeSecond < durationSeconds {
-                        NCManageDatabase.shared.addVideoTime(metadata: self.currentMetadata, time: player?.currentTime())
-                    }
-                }
-            }
-        }
-    }
-    
-    //MARK: - audio/video Tool Bar
-
-    func setToolBar() {
-        
-        let mute = CCUtility.getAudioMute()
-        
-        if player?.rate == 1 {
-            playButton.setImage(NCUtility.shared.loadImage(named: "pause.fill"), for: .normal)
-        } else {
-            playButton.setImage(NCUtility.shared.loadImage(named: "play.fill"), for: .normal)
-        }
-       
-        if mute {
-            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOff"), for: .normal)
-        } else {
-            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOn"), for: .normal)
-        }
-    }
-
-    @IBAction func playerPause(_ sender: Any) {
-        
-        if player?.timeControlStatus == .playing {
-            player?.pause()
-        } else if player?.timeControlStatus == .paused {
-            player?.play()
-        }
-    }
-        
-    @IBAction func setMute(_ sender: Any) {
-        let mute = CCUtility.getAudioMute()
-        CCUtility.setAudioMute(!mute)
-        player?.isMuted = !mute
-        setToolBar()
-    }
 }
 
 
@@ -704,7 +534,7 @@ extension NCViewerImage: UIGestureRecognizerDelegate {
         
         if currentMetadata.typeFile == NCGlobal.shared.metadataTypeFileVideo || currentMetadata.typeFile == NCGlobal.shared.metadataTypeFileAudio {
             
-            if pictureInPictureOcId != currentMetadata.ocId {
+            if self.viewerVideo?.pictureInPictureOcId != currentMetadata.ocId {
                                 
                 // Kill PIP
                 appDelegate.activeViewerAVPlayerViewController?.player?.replaceCurrentItem(with: nil)
@@ -716,7 +546,7 @@ extension NCViewerImage: UIGestureRecognizerDelegate {
                 appDelegate.activeViewerAVPlayerViewController?.delegateViewerVideo = self
                 if let currentViewerVideo = appDelegate.activeViewerAVPlayerViewController {
                     present(currentViewerVideo, animated: false) { }
-                    self.videoStop()
+                    self.viewerVideo?.videoStop()
                 }
             }
             
@@ -753,7 +583,7 @@ extension NCViewerImage: NCViewerImageZoomDelegate {
     }
     
     func willAppearImageZoom(viewerImageZoom: NCViewerImageZoom, metadata: tableMetadata) {
-        videoStop()
+        self.viewerVideo?.videoStop()
     }
     
     func didAppearImageZoom(viewerImageZoom: NCViewerImageZoom, metadata: tableMetadata) {
@@ -762,10 +592,11 @@ extension NCViewerImage: NCViewerImageZoomDelegate {
         currentMetadata = metadata
         currentViewerImageZoom = viewerImageZoom
         toolBar.isHidden = true
+        viewerVideo = NCViewerVideo.init(view: viewerImageZoom.imageView, progressView: progressView, viewerVideoToolBar: toolBar)
         
         if (currentMetadata.typeFile == NCGlobal.shared.metadataTypeFileVideo || currentMetadata.typeFile == NCGlobal.shared.metadataTypeFileAudio) {
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
-                self.videoPlay(metadata: metadata)
+                self.viewerVideo?.videoPlay(metadata: metadata)
             }
             toolBar.isHidden = false
         }
@@ -817,13 +648,13 @@ extension NCViewerImage: NCViewerImageZoomDelegate {
 extension NCViewerImage: NCViewerAVPlayerViewControllerDelegate {
     
     func startPictureInPicture(metadata: tableMetadata) {
-        pictureInPictureOcId = metadata.ocId
+        self.viewerVideo?.pictureInPictureOcId = metadata.ocId
     }
     
     func stopPictureInPicture(metadata: tableMetadata, playing: Bool) {
-        pictureInPictureOcId = ""
+        self.viewerVideo?.pictureInPictureOcId = ""
         if playing && currentMetadata.ocId == metadata.ocId {
-            playerPause(self)
+            self.viewerVideo?.viewerVideoToolBar?.playerPause(self)
         }
     }
 }

+ 189 - 0
iOSClient/Viewer/NCViewerVideo/NCViewerVideo.swift

@@ -0,0 +1,189 @@
+//
+//  NCViewerVideo.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 01/07/21.
+//  Copyright © 2021 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
+
+class NCViewerVideo: NSObject {
+    
+    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    private var progressView: UIProgressView?
+
+    private var videoLayer: AVPlayerLayer?
+    private var view: UIView?
+    private var timeObserver: Any?
+    private var rateObserver: Any?
+    private var metadata: tableMetadata?
+    
+    public var viewerVideoToolBar: NCViewerVideoToolBar?
+    public var player: AVPlayer?
+    public var pictureInPictureOcId: String = ""
+    
+    init(view: UIView?, progressView: UIProgressView?, viewerVideoToolBar: NCViewerVideoToolBar?) {
+        super.init()
+
+        self.view = view
+        self.progressView = progressView
+        self.viewerVideoToolBar = viewerVideoToolBar
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
+    }
+    
+    deinit {
+        
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
+    }
+    
+    //MARK: - NotificationCenter
+
+    @objc func applicationDidEnterBackground(_ notification: NSNotification) {
+        
+        if metadata?.typeFile == NCGlobal.shared.metadataTypeFileVideo {
+            player?.pause()
+        }
+    }
+    
+    func videoPlay(metadata: tableMetadata) {
+        self.metadata = metadata
+        
+        NCKTVHTTPCache.shared.startProxy(user: appDelegate.user, password: appDelegate.password, metadata: metadata)
+        
+        func play(url: URL) {
+            
+            self.player = AVPlayer(url: url)
+            self.player?.isMuted = CCUtility.getAudioMute()
+            self.videoLayer = AVPlayerLayer(player: self.player)
+
+            if let view = view  {
+
+                self.videoLayer!.frame = view.bounds
+                self.videoLayer!.videoGravity = AVLayerVideoGravity.resizeAspectFill
+                view.layer.addSublayer(self.videoLayer!)
+                
+                // At end go back to start
+                NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { (notification) in
+                    if let item = notification.object as? AVPlayerItem, let currentItem = self.player?.currentItem, item == currentItem {
+                        self.player?.seek(to: .zero)
+                        if metadata.livePhoto {
+                            NCManageDatabase.shared.deleteVideoTime(metadata: metadata)
+                        }
+                    }
+                }
+                            
+                self.rateObserver = self.player?.addObserver(self, forKeyPath: "rate", options: [], context: nil)
+                
+                if self.pictureInPictureOcId != metadata.ocId {
+                    self.player?.play()
+                }
+            }
+            
+            // TOOLBAR
+            viewerVideoToolBar?.setPlayer(player: player)
+            viewerVideoToolBar?.setToolBar()
+        }
+        
+        //NCNetworking.shared.getVideoUrl(metadata: metadata) { url in
+        //            if let url = url {
+
+        if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) {
+            play(url: url)
+        }
+    }
+    
+    func videoStop() {
+        
+        guard let metadata = self.metadata else { return }
+        
+        player?.pause()
+        player?.seek(to: CMTime.zero)
+//        progressView.progress = 0
+        
+        if let timeObserver = timeObserver {
+            player?.removeTimeObserver(timeObserver)
+            self.timeObserver = nil
+        }
+        
+        if rateObserver != nil {
+            player?.removeObserver(self, forKeyPath: "rate")
+            NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
+            NCKTVHTTPCache.shared.stopProxy(metadata: metadata)
+            self.rateObserver = nil
+        }
+               
+        videoLayer?.removeFromSuperlayer()
+    }
+    
+    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+        
+        guard let metadata = self.metadata else { return }
+        
+        if keyPath != nil && keyPath == "rate" {
+            
+            self.viewerVideoToolBar?.setToolBar()
+            
+            if ((player?.rate) == 1) {
+                
+                if let time = NCManageDatabase.shared.getVideoTime(metadata: metadata) {
+                    player?.seek(to: time)
+                    player?.isMuted = CCUtility.getAudioMute()
+                }
+                
+                if rateObserver != nil && !metadata.livePhoto {
+                    self.progressView?.progress = 0
+                    if let duration = self.player?.currentItem?.asset.duration {
+                        let durationSeconds = Double(CMTimeGetSeconds(duration))
+                        if durationSeconds > 0 {
+                            let width = Double(self.progressView?.bounds.width ?? 0)
+                            let interval = (0.5 * durationSeconds) / width
+                            let time = CMTime(seconds: interval, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
+                            if CMTIME_IS_VALID(time) && CMTimeCompare(time, .zero) != 0 {
+                                self.timeObserver = self.player?.addPeriodicTimeObserver(forInterval: time, queue: .main, using: { [weak self] time in
+                                    let durationSeconds = Float(CMTimeGetSeconds(duration))
+                                    if durationSeconds > 0 {
+                                        let currentTimeSeconds = Float(CMTimeGetSeconds(time))
+                                        self?.progressView?.progress = currentTimeSeconds / durationSeconds
+                                    } else {
+                                        self?.progressView?.progress = 0
+                                    }
+                                })
+                            }
+                        }
+                    }
+                }
+                
+            } else if !metadata.livePhoto {
+                
+                if let time = player?.currentTime(), let duration = self.player?.currentItem?.asset.duration {
+                    let timeSecond = Double(CMTimeGetSeconds(time))
+                    let durationSeconds = Double(CMTimeGetSeconds(duration))
+                    if timeSecond < durationSeconds {
+                        NCManageDatabase.shared.addVideoTime(metadata: metadata, time: player?.currentTime())
+                    }
+                }
+            }
+        }
+    }
+    
+
+    
+}
+

+ 57 - 0
iOSClient/Viewer/NCViewerVideo/NCViewerVideoToolBar.swift

@@ -0,0 +1,57 @@
+//
+//  NCViewerVideoToolBar.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 01/07/21.
+//  Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+
+class NCViewerVideoToolBar: UIView {
+    
+    @IBOutlet weak var playButton: UIButton!
+    @IBOutlet weak var muteButton: UIButton!
+    
+    var player: AVPlayer?
+
+    func setPlayer(player: AVPlayer?) {
+        self.player = player
+    }
+    
+    func setToolBar() {
+        
+        let mute = CCUtility.getAudioMute()
+        
+        if  player?.rate == 1 {
+            playButton.setImage(NCUtility.shared.loadImage(named: "pause.fill"), for: .normal)
+        } else {
+            playButton.setImage(NCUtility.shared.loadImage(named: "play.fill"), for: .normal)
+        }
+       
+        if mute {
+            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOff"), for: .normal)
+        } else {
+            muteButton.setImage(NCUtility.shared.loadImage(named: "audioOn"), for: .normal)
+        }
+    }
+
+    @IBAction func playerPause(_ sender: Any) {
+        
+        if player?.timeControlStatus == .playing {
+            player?.pause()
+        } else if player?.timeControlStatus == .paused {
+            player?.play()
+        }
+    }
+        
+    @IBAction func setMute(_ sender: Any) {
+        
+        let mute = CCUtility.getAudioMute()
+        
+        CCUtility.setAudioMute(!mute)
+        player?.isMuted = !mute
+        setToolBar()
+    }
+    
+}