Browse Source

Code improvements - Alignment

Signed-off-by: marinofaggiana <96728420+MarinoFaggianaAstraIridium@users.noreply.github.com>
marinofaggiana 3 years ago
parent
commit
f7761c5182

+ 2 - 0
iOSClient/Data/NCDatabase.swift

@@ -531,6 +531,8 @@ class tableVideo: Object {
     @objc dynamic var codecAudioChannelLayout: String?
     @objc dynamic var codecAudioLanguage: String?
     @objc dynamic var codecSubtitleLanguage: String?
+    @objc dynamic var codecMaxCompatibility: Bool = false
+    @objc dynamic var codecQuality: String?
 
     override static func primaryKey() -> String {
         return "ocId"

+ 5 - 1
iOSClient/Data/NCManageDatabase.swift

@@ -1722,7 +1722,7 @@ class NCManageDatabase: NSObject {
         }
     }
     
-    func addVideoCodec(metadata: tableMetadata, codecNameVideo: String?, codecNameAudio: String?, codecAudioChannelLayout: String?, codecAudioLanguage: String?, codecSubtitleLanguage: String?) {
+    func addVideoCodec(metadata: tableMetadata, codecNameVideo: String?, codecNameAudio: String?, codecAudioChannelLayout: String?, codecAudioLanguage: String?, codecSubtitleLanguage: String?, codecMaxCompatibility: Bool, codecQuality: String?) {
 
         let realm = try! Realm()
 
@@ -1734,6 +1734,8 @@ class NCManageDatabase: NSObject {
                     if let codecAudioChannelLayout = codecAudioChannelLayout { result.codecAudioChannelLayout = codecAudioChannelLayout }
                     if let codecAudioLanguage = codecAudioLanguage { result.codecAudioLanguage = codecAudioLanguage }
                     if let codecSubtitleLanguage = codecSubtitleLanguage { result.codecSubtitleLanguage = codecSubtitleLanguage }
+                    result.codecMaxCompatibility = codecMaxCompatibility
+                    if let codecQuality = codecQuality { result.codecQuality = codecQuality }
                     realm.add(result, update: .all)
                 } else {
                     let addObject = tableVideo()
@@ -1744,6 +1746,8 @@ class NCManageDatabase: NSObject {
                     addObject.codecAudioChannelLayout = codecAudioChannelLayout
                     addObject.codecAudioLanguage = codecAudioLanguage
                     addObject.codecSubtitleLanguage = codecSubtitleLanguage
+                    addObject.codecMaxCompatibility = codecMaxCompatibility
+                    addObject.codecQuality = codecQuality
                     realm.add(addObject, update: .all)
                 }
             }

+ 2 - 15
iOSClient/Menu/NCViewer+Menu.swift

@@ -145,21 +145,8 @@ extension NCViewer {
                     title: NSLocalizedString("_video_conversion_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "film"),
                     action: { menuAction in
-                        
-                        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)
-
-                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterPauseMedia)
-                        
-                        MFFF.shared.convertVideo(url: url, urlOut: urlOut, serverUrl: metadata.serverUrl, fileName: metadata.fileNameView, contentType: metadata.contentType, ocId: metadata.ocId, codecNameVideo: tableVideo?.codecNameVideo, codecNameAudio: tableVideo?.codecNameAudio, codecChannelLayout: tableVideo?.codecAudioChannelLayout, languageAudio: tableVideo?.codecAudioLanguage, languageSubtitle: tableVideo?.codecSubtitleLanguage) { urlVideo, urlSubtitle, returnCode in
-                            if returnCode?.isSuccess() ?? false {
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadMediaPage)
-                            } else if returnCode?.isCancel() ?? false {
-                                print("cancel")
-                            } else {
-                                NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
-                            }
+                        if let ncplayer = (viewController as? NCViewerMediaPage)?.currentViewController.ncplayer {
+                            ncplayer.convertVideo()
                         }
                     }
                 )

+ 1 - 1
iOSClient/NCGlobal.swift

@@ -112,7 +112,7 @@ class NCGlobal: NSObject {
     // Database Realm
     //
     let databaseDefault                             = "nextcloud.realm"
-    let databaseSchemaVersion: UInt64               = 214
+    let databaseSchemaVersion: UInt64               = 215
 
     // Intro selector
     //

+ 10 - 75
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift

@@ -29,10 +29,10 @@ import MediaPlayer
 
 class NCPlayer: NSObject {
    
-    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    internal let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    internal var url: URL
+    internal var playerToolBar: NCPlayerToolBar?
     
-    private var url: URL
-    private var playerToolBar: NCPlayerToolBar?
     private var imageVideoContainer: imageVideoContainerView
     private var detailView: NCViewerMediaDetailView?
     private var viewController: UIViewController
@@ -69,12 +69,7 @@ class NCPlayer: NSObject {
         if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: NCGlobal.shared.fileNameVideoEncoded) {
             self.url = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: NCGlobal.shared.fileNameVideoEncoded))
         }
-        
-        // MFFF Delegate
-        #if MFFF
-        MFFF.shared.delegate = self
-        #endif
-        
+              
         openAVPlayer() { status, error in
             
             switch status {
@@ -85,43 +80,12 @@ class NCPlayer: NSObject {
                 break
             case .failed:
                 #if MFFF
-                if error?.code == AVError.Code.fileFormatNotRecognized.rawValue {
-                    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: { action in
-                        
-                        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)
-                        
-                        MFFF.shared.convertVideo(url: url, urlOut: urlOut, serverUrl: self.metadata.serverUrl, fileName: self.metadata.fileNameView, contentType: self.metadata.contentType, ocId: metadata.ocId, codecNameVideo: tableVideo?.codecNameVideo, codecNameAudio: tableVideo?.codecNameAudio, codecChannelLayout: tableVideo?.codecAudioChannelLayout, languageAudio: tableVideo?.codecAudioLanguage, languageSubtitle: tableVideo?.codecSubtitleLanguage) { urlVideo, urlSubtitle, returnCode in
-                            if returnCode?.isSuccess() ?? false, let url = urlVideo {
-                                self.url = url
-                                self.openAVPlayer() { status, error in
-                                    if let error = error {
-                                        NCContentPresenter.shared.messageNotification(error.localizedDescription, description: error.localizedFailureReason, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
-                                    }
-                                }
-                            } else if returnCode?.isCancel() ?? false {
-                                print("cancel")
-                            } else {
-                                NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
-                            }
-                        }
-                    }))
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in
-                        NCContentPresenter.shared.messageNotification("_info_", description: "_video_conversion_available_after_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError, priority: .max)
-                    }))
-                    self.appDelegate.window?.rootViewController?.present(alertController, animated: true)
-                } else if let title = error?.localizedDescription, let description = error?.localizedFailureReason {
-                    NCContentPresenter.shared.messageNotification(title, description: description, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
-                } else {
-                    NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
-                }
+                self.convertVideo(error: error)
                 #else
                 if let title = error?.localizedDescription, let description = error?.localizedFailureReason {
-                    NCContentPresenter.shared.messageNotification(title, description: description, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
+                    self.playerToolBar?.showMessage(title, description: description, hiddenAfterSeconds: NCGlobal.shared.dismissAfterSecond)
                 } else {
-                    NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max)
+                    self.playerToolBar?.showMessage("_error_", description: "_error_something_wrong_", hiddenAfterSeconds: NCGlobal.shared.dismissAfterSecond)
                 }
                 #endif
                 break
@@ -137,6 +101,7 @@ class NCPlayer: NSObject {
         
         print("Play URL: \(self.url)")
         player = AVPlayer(url: self.url)
+        playerToolBar?.setMetadata(self.metadata)
         
         if metadata.livePhoto {
             player?.isMuted = false
@@ -173,7 +138,7 @@ class NCPlayer: NSObject {
                         self.imageVideoContainer.image = self.imageVideoContainer.image?.image(alpha: 0)
                     }
                     
-                    self.playerToolBar?.setBarPlayer(ncplayer: self, metadata: self.metadata)
+                    self.playerToolBar?.setBarPlayer(ncplayer: self)
                     self.generatorImagePreview()
                     if !(self.detailView?.isShow() ?? false) {
                         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":self.metadata.ocId, "enableTimerAutoHide": false])
@@ -267,7 +232,7 @@ class NCPlayer: NSObject {
 
         playerToolBar?.updateToolBar()
     }
-
+    
     // MARK: -
 
     func isPlay() -> Bool {
@@ -351,33 +316,3 @@ class NCPlayer: NSObject {
     }
 }
 
-#if MFFF
-extension NCPlayer: MFFFDelegate {
-    
-    func downloadedFile(url: URL, ocId: String?) {
-        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            NCManageDatabase.shared.addLocalFile(metadata: metadata)
-            CCUtility.setExif(metadata) { _, _, _, _, _ in }
-        }
-    }
-    
-    func sessionStarted(url: URL, ocId: String?) {
-        self.playerToolBar?.forcedHide(true)
-    }
-    
-    func sessionProgress(url: URL, ocId: String?, progress: CGFloat) {
-    }
-    
-    func sessionEnded(url: URL, ocId: String?, returnCode: Int?, traces: [MFFFTrace]?) {
-        self.playerToolBar?.forcedHide(false)
-        
-        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
-            let traceSubtitle = traces.filter { $0.codecType == "subtitle" }.first
-            NCManageDatabase.shared.addVideoCodec(metadata: metadata, codecNameVideo: traceVideo?.codecName, codecNameAudio: traceAudio?.codecName, codecAudioChannelLayout: traceAudio?.codecChannelLayout, codecAudioLanguage: traceAudio?.languageAudio, codecSubtitleLanguage: traceSubtitle?.languageSubtitle)
-        }
-    }
-}
-#endif
-

+ 101 - 13
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift

@@ -31,6 +31,7 @@ import MediaPlayer
 class NCPlayerToolBar: UIView {
 
     @IBOutlet weak var playerTopToolBarView: UIView!
+    @IBOutlet weak var playerMessage: UIView!
     @IBOutlet weak var pipButton: UIButton!
     @IBOutlet weak var muteButton: UIButton!
     @IBOutlet weak var playButton: UIButton!
@@ -40,6 +41,13 @@ class NCPlayerToolBar: UIView {
     @IBOutlet weak var labelLeftTime: UILabel!
     @IBOutlet weak var labelCurrentTime: UILabel!
 
+    @IBOutlet weak var playerMessageHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var playerMessageProgressView: UIProgressView!
+    @IBOutlet weak var playerMessageTitle: UILabel!
+    @IBOutlet weak var playerMessageComment: UILabel!
+    @IBOutlet weak var playerMessageButton: UIButton!
+    @IBOutlet weak var playerMessageCommentBottomConstraint: NSLayoutConstraint!
+
     enum sliderEventType {
         case began
         case ended
@@ -51,7 +59,7 @@ class NCPlayerToolBar: UIView {
     private var wasInPlay: Bool = false
     private var playbackSliderEvent: sliderEventType = .ended
     private var timerAutoHide: Timer?
-    private var forcedHide: Bool = false
+    private var timerAutoHideMessage: Timer?
 
     var pictureInPictureController: AVPictureInPictureController?
     weak var viewerMediaPage: NCViewerMediaPage?
@@ -83,6 +91,10 @@ class NCPlayerToolBar: UIView {
         blurEffectTopToolBarView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         playerTopToolBarView.insertSubview(blurEffectTopToolBarView, at: 0)
 
+        playerMessage.layer.cornerRadius = 10
+        playerMessage.layer.masksToBounds = true
+        playerMessage.isHidden = true
+        
         pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .lightGray), for: .normal)
         pipButton.isEnabled = false
 
@@ -122,11 +134,15 @@ class NCPlayerToolBar: UIView {
     }
 
     // MARK: -
-
-    func setBarPlayer(ncplayer: NCPlayer, metadata: tableMetadata) {
+    
+    func setMetadata(_ metadata: tableMetadata) {
+        
+        self.metadata = metadata
+    }
+    
+    func setBarPlayer(ncplayer: NCPlayer) {
 
         self.ncplayer = ncplayer
-        self.metadata = metadata
 
         playbackSlider.value = 0
         playbackSlider.minimumValue = 0
@@ -258,17 +274,24 @@ class NCPlayerToolBar: UIView {
 
     public func show(enableTimerAutoHide: Bool = false) {
 
-        if metadata?.classFile != NCCommunicationCommon.typeClassFile.video.rawValue && metadata?.classFile != NCCommunicationCommon.typeClassFile.audio.rawValue { return }
-        if let metadata = self.metadata, metadata.livePhoto { return }
-        if forcedHide { return }
+        guard let metadata = self.metadata else { return }
+        if ncplayer == nil { return }
+        if metadata.livePhoto { return }
+        if metadata.classFile != NCCommunicationCommon.typeClassFile.video.rawValue && metadata.classFile != NCCommunicationCommon.typeClassFile.audio.rawValue { return }
+
+        #if MFFF
+        if MFFF.shared.existsMFFFSession(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))) {
+            self.hide()
+            return
+        }
+        #endif
         
         timerAutoHide?.invalidate()
         if enableTimerAutoHide {
             startTimerAutoHide()
         }
-
         if !self.isHidden { return }
-
+        
         UIView.animate(withDuration: 0.3, animations: {
             self.alpha = 1
             self.playerTopToolBarView.alpha = 1
@@ -362,12 +385,68 @@ class NCPlayerToolBar: UIView {
         timerAutoHide?.invalidate()
     }
     
-    func forcedHide(_ forcedHide: Bool) {
+    // MARK: - Message
+    
+    func showMessage(_ title: String, description: String?, backgroundColor: UIColor = NCBrandColor.shared.brand, isHiddenPregress: Bool = true, hiddenAfterSeconds: Double = 0) {
         
-        if forcedHide { hide() } else { show() }
-        self.forcedHide = forcedHide
-    }
+        self.playerMessage.backgroundColor = backgroundColor
 
+        self.playerMessageTitle.text = NSLocalizedString(title, comment: "")
+        self.playerMessageTitle.textColor = NCBrandColor.shared.brandText
+        
+        self.playerMessageButton.isHidden = isHiddenPregress
+        self.playerMessageButton.setBackgroundImage(UIImage(named: "stop")!.image(color: .black, size: 30), for: .normal)
+        
+        if let description = description {
+            self.playerMessageComment.text = NSLocalizedString(description, comment: "")      
+        }
+        self.playerMessageComment.textColor = NCBrandColor.shared.brandText
+        
+        self.playerMessageProgressView.progress = 0
+        self.playerMessageProgressView.tintColor = .black
+        self.playerMessageProgressView.isHidden = isHiddenPregress
+        
+        playerMessageCommentBottomConstraint.constant = 30
+        playerMessageHeightConstraint.constant = 120
+        if isHiddenPregress {
+            playerMessageCommentBottomConstraint.constant = 5
+            playerMessageHeightConstraint.constant = 90
+        }
+        
+        UIView.animate(withDuration: 0.3, animations: {
+            self.playerMessage.alpha = 1
+        }, completion: { (_: Bool) in
+            self.playerMessage.isHidden = false
+        })
+        
+        timerAutoHideMessage?.invalidate()
+        if hiddenAfterSeconds > 0 {
+            timerAutoHideMessage = Timer.scheduledTimer(timeInterval: hiddenAfterSeconds, target: self, selector: #selector(hideMessage), userInfo: nil, repeats: false)
+        }
+    }
+    
+    @objc func hideMessage() {
+        
+        self.playerMessageProgressView.progress = 0
+        
+        UIView.animate(withDuration: 0.3, animations: {
+            self.playerMessage.alpha = 0
+        }, completion: { (_: Bool) in
+            self.playerMessage.isHidden = true
+        })
+    }
+    
+    func showMessageProgress(_ progress: Float) {
+        
+        self.playerMessageProgressView.progress = progress
+    }
+    
+    func isHiddenMessage() -> Bool {
+        
+        timerAutoHideMessage?.invalidate()
+        return self.playerMessage.isHidden
+    }
+    
     // MARK: - Event / Gesture
 
     @objc func onSliderValChanged(slider: UISlider, event: UIEvent) {
@@ -495,6 +574,15 @@ class NCPlayerToolBar: UIView {
         }
         */
     }
+    
+    @IBAction func playerMessageButtonTouchInside(_ sender: UIButton) {
+       
+        #if MFFF
+        if let metadata = metadata {
+            MFFF.shared.stopMFFFSession(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)))
+        }
+        #endif
+    }
 
     func forward() {
 

+ 9 - 0
iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift

@@ -162,6 +162,15 @@ class NCViewerMedia: UIViewController {
             if let ncplayer = self.ncplayer {
                 self.viewerMediaPage?.updateCommandCenter(ncplayer: ncplayer, metadata: self.metadata)
             }
+            
+            #if MFFF
+            MFFF.shared.delegate = self.ncplayer
+            if !MFFF.shared.existsMFFFSession(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))) {
+                self.playerToolBar.hideMessage()
+            }
+            #else
+            self.playerToolBar.hideMessage()
+            #endif
 
         } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue {
 

+ 68 - 2
iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ne8-hS-cp3">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ne8-hS-cp3">
     <device id="retina5_5" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
         <capability name="Image references" minToolsVersion="12.0"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -392,9 +392,68 @@
                                             <outlet property="pipButton" destination="NwE-zQ-Y5D" id="veJ-QD-fOd"/>
                                             <outlet property="playButton" destination="x3E-b2-obf" id="0Nw-L4-W7M"/>
                                             <outlet property="playbackSlider" destination="SR4-e8-1hC" id="Khx-Oe-NEQ"/>
+                                            <outlet property="playerMessage" destination="dcP-0P-XaU" id="Elb-g9-KLN"/>
+                                            <outlet property="playerMessageButton" destination="IBy-FL-p7m" id="oCg-wD-wui"/>
+                                            <outlet property="playerMessageComment" destination="0ay-aW-oWe" id="iR2-lY-e92"/>
+                                            <outlet property="playerMessageCommentBottomConstraint" destination="mQr-bP-ade" id="UTD-j1-pNq"/>
+                                            <outlet property="playerMessageHeightConstraint" destination="Hj6-hL-o4b" id="hbN-pd-ZtS"/>
+                                            <outlet property="playerMessageProgressView" destination="xph-h7-QuN" id="mJi-16-HJC"/>
+                                            <outlet property="playerMessageTitle" destination="hDm-OS-aGd" id="aHa-Zu-vYF"/>
                                             <outlet property="playerTopToolBarView" destination="dgJ-dQ-lSp" id="22g-Yn-k5r"/>
                                         </connections>
                                     </view>
+                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dcP-0P-XaU" userLabel="Player Message">
+                                        <rect key="frame" x="15" y="606" width="384" height="120"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hDm-OS-aGd">
+                                                <rect key="frame" x="15" y="8" width="354" height="19"/>
+                                                <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Comment" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0ay-aW-oWe">
+                                                <rect key="frame" x="15" y="30" width="354" height="60"/>
+                                                <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="xph-h7-QuN">
+                                                <rect key="frame" x="15" y="99" width="324" height="3"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="3" id="mH2-gB-utu"/>
+                                                </constraints>
+                                            </progressView>
+                                            <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IBy-FL-p7m">
+                                                <rect key="frame" x="348.99999999999994" y="85" width="30" height="30"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="30" id="FEu-rH-BBs"/>
+                                                    <constraint firstAttribute="width" constant="30" id="mVG-Ki-u50"/>
+                                                </constraints>
+                                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                                <state key="normal" backgroundImage="taskCancelDownload"/>
+                                                <connections>
+                                                    <action selector="playerMessageButtonTouchInside:" destination="sBp-t2-eFh" eventType="touchUpInside" id="6vh-TU-5IR"/>
+                                                </connections>
+                                            </button>
+                                        </subviews>
+                                        <color key="backgroundColor" systemColor="systemTealColor"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="120" id="Hj6-hL-o4b"/>
+                                            <constraint firstAttribute="trailing" secondItem="0ay-aW-oWe" secondAttribute="trailing" constant="15.000000000000057" id="KK6-KV-ov5"/>
+                                            <constraint firstItem="xph-h7-QuN" firstAttribute="leading" secondItem="dcP-0P-XaU" secondAttribute="leading" constant="15" id="PG4-3d-CnT"/>
+                                            <constraint firstItem="0ay-aW-oWe" firstAttribute="top" secondItem="hDm-OS-aGd" secondAttribute="bottom" constant="3" id="VoM-4v-uHz"/>
+                                            <constraint firstItem="IBy-FL-p7m" firstAttribute="leading" secondItem="xph-h7-QuN" secondAttribute="trailing" constant="10" id="Wxd-Wo-eC1"/>
+                                            <constraint firstAttribute="trailing" secondItem="IBy-FL-p7m" secondAttribute="trailing" constant="5" id="bbL-qa-ywb"/>
+                                            <constraint firstItem="0ay-aW-oWe" firstAttribute="leading" secondItem="dcP-0P-XaU" secondAttribute="leading" constant="15" id="efH-Ty-CxK"/>
+                                            <constraint firstItem="hDm-OS-aGd" firstAttribute="top" secondItem="dcP-0P-XaU" secondAttribute="top" constant="8" id="hZH-VC-Bs7"/>
+                                            <constraint firstAttribute="bottom" secondItem="IBy-FL-p7m" secondAttribute="bottom" constant="5" id="hj6-vu-1oD"/>
+                                            <constraint firstAttribute="bottom" secondItem="xph-h7-QuN" secondAttribute="bottom" constant="18" id="ktP-qt-9Vo"/>
+                                            <constraint firstAttribute="bottom" secondItem="0ay-aW-oWe" secondAttribute="bottom" constant="30" id="mQr-bP-ade"/>
+                                            <constraint firstItem="hDm-OS-aGd" firstAttribute="leading" secondItem="dcP-0P-XaU" secondAttribute="leading" constant="15" id="uJh-HI-mhr"/>
+                                            <constraint firstAttribute="trailing" secondItem="hDm-OS-aGd" secondAttribute="trailing" constant="15" id="waz-yc-nK3"/>
+                                            <constraint firstItem="0ay-aW-oWe" firstAttribute="top" secondItem="dcP-0P-XaU" secondAttribute="top" constant="30" id="xau-Hg-qaU"/>
+                                        </constraints>
+                                    </view>
                                 </subviews>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                 <constraints>
@@ -414,7 +473,9 @@
                         <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                         <constraints>
                             <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="dgJ-dQ-lSp" secondAttribute="trailing" constant="15" id="15m-hD-wYt"/>
+                            <constraint firstItem="dcP-0P-XaU" firstAttribute="leading" secondItem="Yo6-7W-moG" secondAttribute="leading" constant="15" id="32M-PK-SC7"/>
                             <constraint firstAttribute="bottom" secondItem="CdQ-LC-Trx" secondAttribute="bottom" id="4qB-8y-OcG"/>
+                            <constraint firstItem="Yo6-7W-moG" firstAttribute="bottom" secondItem="dcP-0P-XaU" secondAttribute="bottom" constant="10" id="AQ8-nh-2cp"/>
                             <constraint firstAttribute="trailing" secondItem="CdQ-LC-Trx" secondAttribute="trailing" id="IwE-oE-d3Y"/>
                             <constraint firstItem="Yo6-7W-moG" firstAttribute="bottom" secondItem="sBp-t2-eFh" secondAttribute="bottom" constant="10" id="QHF-av-zeT"/>
                             <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="sBp-t2-eFh" secondAttribute="trailing" constant="15" id="TCr-e0-gnG"/>
@@ -427,6 +488,7 @@
                             <constraint firstItem="CdQ-LC-Trx" firstAttribute="top" secondItem="fIE-H6-KKc" secondAttribute="top" id="hcQ-lB-JwU"/>
                             <constraint firstItem="sBp-t2-eFh" firstAttribute="leading" secondItem="Yo6-7W-moG" secondAttribute="leading" constant="15" id="hwP-QY-nRI"/>
                             <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="P8R-4f-zAl" secondAttribute="trailing" id="jf2-Nv-gFi"/>
+                            <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="dcP-0P-XaU" secondAttribute="trailing" constant="15" id="wf6-YZ-0aq"/>
                         </constraints>
                     </view>
                     <connections>
@@ -454,8 +516,12 @@
         <image name="networkInProgress" width="300" height="300"/>
         <image name="pip.enter" catalog="system" width="128" height="96"/>
         <image name="play.fill" catalog="system" width="116" height="128"/>
+        <image name="taskCancelDownload" width="25" height="25"/>
         <systemColor name="systemBackgroundColor">
             <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
         </systemColor>
+        <systemColor name="systemTealColor">
+            <color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
     </resources>
 </document>