1
0
mirror of https://github.com/yattee/yattee.git synced 2024-12-13 22:00:31 +05:30
yattee/Model/Stream.swift
2021-10-17 23:01:43 +02:00

177 lines
4.9 KiB
Swift

import AVFoundation
import Defaults
import Foundation
// swiftlint:disable:next final_class
class Stream: Equatable, Hashable, Identifiable {
enum ResolutionSetting: String, Defaults.Serializable, CaseIterable {
case hd720pFirstThenBest, hd1080p, hd720p, sd480p, sd360p, sd240p, sd144p
var value: Stream.Resolution {
switch self {
case .hd720pFirstThenBest:
return .hd720p
default:
return Stream.Resolution(rawValue: rawValue)!
}
}
var description: String {
switch self {
case .hd720pFirstThenBest:
return "Default: adaptive"
default:
return value.name
}
}
}
enum Resolution: String, CaseIterable, Comparable, Defaults.Serializable {
case hd1440p60, hd1440p, hd1080p60, hd1080p, hd720p60, hd720p, sd480p, sd360p, sd240p, sd144p, unknown
var name: String {
"\(height)p\(refreshRate != -1 ? ", \(refreshRate) fps" : "")"
}
var height: Int {
if self == .unknown {
return -1
}
let resolutionPart = rawValue.components(separatedBy: "p").first!
return Int(resolutionPart.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())!
}
var refreshRate: Int {
if self == .unknown {
return -1
}
let refreshRatePart = rawValue.components(separatedBy: "p")[1]
return Int(refreshRatePart.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()) ?? -1
}
static func from(resolution: String) -> Resolution {
allCases.first { "\($0)".contains(resolution) } ?? .unknown
}
static func < (lhs: Resolution, rhs: Resolution) -> Bool {
lhs.height < rhs.height
}
}
enum Kind: String, Comparable {
case stream, adaptive, hls
private var sortOrder: Int {
switch self {
case .hls:
return 0
case .stream:
return 1
case .adaptive:
return 2
}
}
static func < (lhs: Kind, rhs: Kind) -> Bool {
lhs.sortOrder < rhs.sortOrder
}
}
let id = UUID()
var instance: Instance!
var audioAsset: AVURLAsset!
var videoAsset: AVURLAsset!
var hlsURL: URL!
var resolution: Resolution!
var kind: Kind!
var encoding: String!
init(
instance: Instance? = nil,
audioAsset: AVURLAsset? = nil,
videoAsset: AVURLAsset? = nil,
hlsURL: URL? = nil,
resolution: Resolution? = nil,
kind: Kind = .hls,
encoding: String? = nil
) {
self.instance = instance
self.audioAsset = audioAsset
self.videoAsset = videoAsset
self.hlsURL = hlsURL
self.resolution = resolution
self.kind = kind
self.encoding = encoding
}
var shortQuality: String {
kind == .hls ? "adaptive" : resolution.name
}
var quality: String {
kind == .hls ? "adaptive (HLS)" : "\(resolution.name) \(kind == .stream ? "(\(kind.rawValue))" : "")"
}
var description: String {
"\(quality) - \(instance?.description ?? "")"
}
var assets: [AVURLAsset] {
[audioAsset, videoAsset]
}
var videoAssetContainsAudio: Bool {
assets.dropFirst().allSatisfy { $0.url == assets.first!.url }
}
var singleAssetURL: URL? {
if kind == .hls {
return hlsURL
} else if videoAssetContainsAudio {
return videoAsset.url
}
return nil
}
static func == (lhs: Stream, rhs: Stream) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(videoAsset?.url)
hasher.combine(audioAsset?.url)
hasher.combine(hlsURL)
}
func withAssetsFrom(_ instance: Instance) -> Stream {
if kind == .hls {
return Stream(instance: instance, hlsURL: hlsURL)
} else {
return Stream(
instance: instance,
audioAsset: AVURLAsset(url: assetURLFrom(instance: instance, url: (audioAsset ?? videoAsset).url)!),
videoAsset: AVURLAsset(url: assetURLFrom(instance: instance, url: videoAsset.url)!),
resolution: resolution,
kind: kind,
encoding: encoding
)
}
}
private func assetURLFrom(instance: Instance, url: URL) -> URL? {
guard let instanceURLComponents = URLComponents(string: instance.url),
var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil }
urlComponents.scheme = instanceURLComponents.scheme
urlComponents.host = instanceURLComponents.host
return urlComponents.url
}
}