Playback rate

This commit is contained in:
Arkadiusz Fal
2021-06-19 22:10:14 +02:00
parent c40fc3e042
commit bb19fca073
9 changed files with 105 additions and 11 deletions

View File

@@ -12,8 +12,6 @@ final class PlayerState: ObservableObject {
@Published private(set) var composition = AVMutableComposition()
@Published private(set) var nextComposition = AVMutableComposition()
private var comp: AVMutableComposition?
@Published private(set) var currentStream: Stream!
@Published private(set) var nextStream: Stream!
@@ -24,6 +22,11 @@ final class PlayerState: ObservableObject {
@Published var currentSegment: Segment?
private var profile = Profile()
@Published private(set) var currentRate: Float = 0.0
static let availablePlaybackRates: [Double] = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
var playerItem: AVPlayerItem {
let playerItem = AVPlayerItem(asset: composition)
@@ -115,6 +118,8 @@ final class PlayerState: ObservableObject {
player.replaceCurrentItem(with: playerItem)
streamDidLoad(stream)
player.play()
seekToSavedTime()
}
@@ -179,10 +184,9 @@ final class PlayerState: ObservableObject {
}
if let time = savedTime {
logger.info("seeking to \(time.seconds)")
player.seek(to: time)
}
player.play()
}
func destroyPlayer() {
@@ -205,7 +209,22 @@ final class PlayerState: ObservableObject {
let interval = CMTime(value: 1, timescale: 1)
timeObserver = player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { time in
self.currentTime = time
self.currentSegment = self.segmentsProvider.segments.first { $0.timeInSegment(time) }
let currentSegment = self.segmentsProvider.segments.first { $0.timeInSegment(time) }
if let segment = currentSegment {
if self.profile.skippedSegmentsCategories.contains(segment.category) {
if segment.shouldSkip(self.currentTime!) {
self.player.seek(to: segment.skipTo)
}
}
}
if self.player.rate != self.currentRate, self.player.rate != 0, self.currentRate != 0 {
self.player.rate = self.currentRate
}
self.currentSegment = currentSegment
}
}
@@ -218,4 +237,9 @@ final class PlayerState: ObservableObject {
return item.copy() as! AVMetadataItem
}
func setPlayerRate(_ rate: Float) {
currentRate = rate
player.rate = rate
}
}

23
Model/Profile.swift Normal file
View File

@@ -0,0 +1,23 @@
import Foundation
final class Profile: ObservableObject {
let defaultStreamResolution: DefaultStreamResolution = .hd720pFirstThenBest
let skippedSegmentsCategories = [String]() // SponsorBlockSegmentsProvider.categories
// let sid = "B3_WzklziGu8JKefihLrCsTNavdj73KMiPUBfN5HW2M="
let sid = "RpoS7YPPK2-QS81jJF9z4KSQAjmzsOnMpn84c73-GQ8="
}
enum DefaultStreamResolution: String {
case hd720pFirstThenBest, hd1080p, hd720p, sd480p, sd360p, sd240p, sd144p
var value: StreamResolution {
switch self {
case .hd720pFirstThenBest:
return .hd720p
default:
return StreamResolution(rawValue: rawValue)!
}
}
}

View File

@@ -47,4 +47,8 @@ class Segment: ObservableObject, Hashable {
func title() -> String {
category
}
func shouldSkip(_ atTime: CMTime) -> Bool {
atTime.seconds - start < 2 && end - atTime.seconds > 2
}
}

View File

@@ -3,7 +3,7 @@ import Foundation
import SwiftyJSON
final class SponsorBlockSegmentsProvider: ObservableObject {
let categories = ["sponsor", "selfpromo", "outro", "intro", "music_offtopic", "interaction"]
static let categories = ["sponsor", "selfpromo", "outro", "intro", "music_offtopic", "interaction"]
@Published var video: Video?
@@ -29,7 +29,7 @@ final class SponsorBlockSegmentsProvider: ObservableObject {
private var parameters: [String: String] {
[
"videoID": id,
"categories": JSON(categories).rawString(String.Encoding.utf8)!,
"categories": JSON(SponsorBlockSegmentsProvider.categories).rawString(String.Encoding.utf8)!,
]
}
}

View File

@@ -1,7 +1,7 @@
import Foundation
enum StreamResolution: String, CaseIterable, Comparable {
case hd_1080p, hd_720p, sd_480p, sd_360p, sd_240p, sd_144p
case hd1080p, hd720p, sd480p, sd360p, sd240p, sd144p
var height: Int {
Int(rawValue.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())!

View File

@@ -5,10 +5,10 @@ import SwiftyJSON
final class SubscriptionVideosProvider: DataProvider {
@Published var videos = [Video]()
var sid: String = "RpoS7YPPK2-QS81jJF9z4KSQAjmzsOnMpn84c73-GQ8="
let profile = Profile()
func load() {
let headers = HTTPHeaders([HTTPHeader(name: "Cookie", value: "SID=\(sid)")])
let headers = HTTPHeaders([HTTPHeader(name: "Cookie", value: "SID=\(profile.sid)")])
DataProvider.request("auth/feed", headers: headers).responseJSON { response in
switch response.result {
case let .success(value):

View File

@@ -88,6 +88,14 @@ final class Video: Identifiable, ObservableObject {
selectableStreams.min { $0.resolution > $1.resolution }
}
func streamWithResolution(_ resolution: StreamResolution) -> Stream? {
selectableStreams.first { $0.resolution == resolution }
}
func defaultStreamForProfile(_ profile: Profile) -> Stream? {
streamWithResolution(profile.defaultStreamResolution.value)
}
private func extractThumbnailURL(from details: JSON) -> URL? {
if details["videoThumbnails"].arrayValue.isEmpty {
return nil