<commit_before>//
//  VideoPlayerView.swift
//  ios-app
//
//  Created by <NAME> on 12/4/19.
//  Copyright © 2019 Testpress. All rights reserved.
//

import UIKit
import AVKit
import M3U8KitDynamic


class VideoPlayerView: UIView {
    var url: URL!
    var playerLayer: AVPlayerLayer?
    var player: AVPlayer? = AVPlayer()
    var playerDelegate: VideoPlayerDelegate!
    var controlsContainerView: VideoPlayerControlsView! = .fromNib("VideoPlayerControls")
    var timeObserver: Any?
    var videoEndObserver: Any?
    var startTime: Float = 0.0
    var resolutionInfo:[VideoQuality] = [VideoQuality(resolution:"Auto", bitrate: 0)]
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init(frame: CGRect, url: URL) {
        self.url = url
        super.init(frame: frame)
        setupPlayer()
        controlsContainerView.frame = frame
        controlsContainerView.delegate = self
        controlsContainerView.setUp()
        addSubview(controlsContainerView)
        addObservers()
        
        self.addTapGestureRecognizer {
            self.controlsContainerView.showControls()
            self.controlsContainerView.startTimerTohideControls()
        }
        controlsContainerView.startLoading()
    }
    
    
    private func setupPlayer() {
        backgroundColor = .black
        playVideo(url: url)
        playerLayer = AVPlayerLayer(player: player)
        self.layer.addSublayer(playerLayer!)
        playerLayer?.frame = self.frame
    }
    
    func playVideo(url: URL) {
        controlsContainerView.startLoading()
        self.url = url
        let playerItem = AVPlayerItem(url: url)
        player?.replaceCurrentItem(with: playerItem)
        player?.seek(to: kCMTimeZero)
        player?.rate = 1
        play()
        
        if #available(iOS 10.0, *) {
            player?.currentItem?.preferredForwardBufferDuration = 1
        }
        addObservers()
        parseResolutionInfo()
    }
    
    func parseResolutionInfo() {
        do {
            resolutionInfo.removeAll()
            resolutionInfo = [VideoQuality(resolution:"Auto", bitrate: 0)]
            let playlistModel = try M3U8PlaylistModel(url: url)
            let masterPlaylist = playlistModel.masterPlaylist
            guard let streamList = masterPlaylist?.xStreamList else {
                return
            }
            streamList.sortByBandwidth(inOrder: .orderedAscending)
            
            for i in 0 ..< streamList.count {
                if let extXStreamInf = streamList.xStreamInf(at: i){
                    let resolution = "\(Int(extXStreamInf.resolution.height))p"
                    resolutionInfo.append(VideoQuality(resolution: resolution, bitrate: extXStreamInf.bandwidth))
                }
            }
        } catch {}
    }
    
    func addObservers() {
        initPlayer()
        player?.currentItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
        videoEndObserver = NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem)

        
        let interval = CMTime(value: 1, timescale: CMTimeScale(NSEC_PER_SEC))
        timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { (progressTime) in
            let seconds = CMTimeGetSeconds(progressTime)
            if (self.player?.currentItem != nil) {
                let loadedDuration = CMTimeGetSeconds((self.player?.availableDuration())!)
                self.controlsContainerView.updateDuration(seconds:seconds, videoDuration: CMTimeGetSeconds((self.player?.currentItem?.duration)!))
                self.controlsContainerView.updateLoadedDuration(seconds:loadedDuration)
            }
        })

    }
    
    func initPlayer()  {
        if player != nil {
            player?.play()
        } else {
            playVideo(url: self.url)
        }
    }
    
    @objc func playerDidFinishPlaying() {
        controlsContainerView.timer?.invalidate()
        controlsContainerView.showControls()
        controlsContainerView.playerStatus = .finished
    }
    
    func dealloc() {
        player?.pause()
        controlsContainerView.playerStatus = .paused
        player?.removeTimeObserver(timeObserver!)
        NotificationCenter.default.removeObserver(videoEndObserver!)
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackBufferFull")
        player?.currentItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
        player?.replaceCurrentItem(with: nil)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object is AVPlayerItem {
            switch keyPath {
                case "playbackBufferEmpty":
                    controlsContainerView.startLoading()
                case "playbackLikelyToKeepUp":<commit_msg>Fix video not playing after bookmark<commit_after>//
//  VideoPlayerView.swift
//  ios-app
//
//  Created by <NAME> on 12/4/19.
//  Copyright © 2019 Testpress. All rights reserved.
//

import UIKit
import AVKit
import M3U8KitDynamic


class VideoPlayerView: UIView {
    var url: URL!
    var playerLayer: AVPlayerLayer?
    var player: AVPlayer? = AVPlayer()
    var playerDelegate: VideoPlayerDelegate!
    var controlsContainerView: VideoPlayerControlsView! = .fromNib("VideoPlayerControls")
    var timeObserver: Any?
    var videoEndObserver: Any?
    var startTime: Float = 0.0
    var resolutionInfo:[VideoQuality] = [VideoQuality(resolution:"Auto", bitrate: 0)]
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    init(frame: CGRect, url: URL) {
        self.url = url
        super.init(frame: frame)
        setupPlayer()
        controlsContainerView.frame = frame
        controlsContainerView.delegate = self
        controlsContainerView.setUp()
        addSubview(controlsContainerView)
        addObservers()
        
        self.addTapGestureRecognizer {
            self.controlsContainerView.showControls()
            self.controlsContainerView.startTimerTohideControls()
        }
        controlsContainerView.startLoading()
    }
    
    
    private func setupPlayer() {
        backgroundColor = .black
        playVideo(url: url)
        playerLayer = AVPlayerLayer(player: player)
        self.layer.addSublayer(playerLayer!)
        playerLayer?.frame = self.frame
    }
    
    func playVideo(url: URL) {
        controlsContainerView.startLoading()
        self.url = url
        let playerItem = AVPlayerItem(url: url)
        player?.replaceCurrentItem(with: playerItem)
        player?.seek(to: kCMTimeZero)
        player?.rate = 1
        play()
        
        if #available(iOS 10.0, *) {
            player?.currentItem?.preferredForwardBufferDuration = 1
        }
        addObservers()
        parseResolutionInfo()
    }
    
    func parseResolutionInfo() {
        do {
            resolutionInfo.removeAll()
            resolutionInfo = [VideoQuality(resolution:"Auto", bitrate: 0)]
            let playlistModel = try M3U8PlaylistModel(url: url)
            let masterPlaylist = playlistModel.masterPlaylist
            guard let streamList = masterPlaylist?.xStreamList else {
                return
            }
            streamList.sortByBandwidth(inOrder: .orderedAscending)
            
            for i in 0 ..< streamList.count {
                if let extXStreamInf = streamList.xStreamInf(at: i){
                    let resolution = "\(Int(extXStreamInf.resolution.height))p"
                    resolutionInfo.append(VideoQuality(resolution: resolution, bitrate: extXStreamInf.bandwidth))
                }
            }
        } catch {}
    }
    
    func addObservers() {
        initPlayer()
        player?.currentItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil)
        player?.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)
        videoEndObserver = NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem)

        
        let interval = CMTime(value: 1, timescale: CMTimeScale(NSEC_PER_SEC))
        timeObserver = player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { (progressTime) in
            let seconds = CMTimeGetSeconds(progressTime)
            if (self.player?.currentItem != nil) {
                let loadedDuration = CMTimeGetSeconds((self.player?.availableDuration())!)
                self.controlsContainerView.updateDuration(seconds:seconds, videoDuration: CMTimeGetSeconds((self.player?.currentItem?.duration)!))
                self.controlsContainerView.updateLoadedDuration(seconds:loadedDuration)
            }
        })

    }
    
    func initPlayer()  {
        if player != nil {
            player?.play()
            controlsContainerView.playerStatus = .playing
        } else {
            playVideo(url: self.url)
        }
    }
    
    @objc func playerDidFinishPlaying() {
        controlsContainerView.timer?.invalidate()
        controlsContainerView.showControls()
        controlsContainerView.playerStatus = .finished
    }
    
    func dealloc() {
        player?.pause()
        controlsContainerView.playerStatus = .paused
        player?.removeTimeObserver(timeObserver!)
        NotificationCenter.default.removeObserver(videoEndObserver!)
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
        player?.currentItem?.removeObserver(self, forKeyPath: "playbackBufferFull")
        player?.currentItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object is AVPlayerItem {
            switch keyPath {
                case "playbackBufferEmpty":
                    controlsContainerView.startLoading()
                case "playbackLikelyToKeepUp":
{'fraction_non_alphanumeric': 0.09586954573962907, 'fraction_numerical': 0.0043006899023385, 'mean_word_length': 2.605297157622739, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1990886', 'n_tokens_mistral': 3055, 'n_tokens_neox': 2947, 'n_words': 576}