1
0
mirror of https://github.com/yattee/yattee.git synced 2024-12-14 14:20:32 +05:30
yattee/Model/Stream.swift

250 lines
6.2 KiB
Swift
Raw Normal View History

2021-06-14 23:35:02 +05:30
import AVFoundation
2021-09-25 13:48:22 +05:30
import Defaults
2021-06-14 23:35:02 +05:30
import Foundation
// swiftlint:disable:next final_class
2021-10-17 04:18:58 +05:30
class Stream: Equatable, Hashable, Identifiable {
2021-09-25 13:48:22 +05:30
enum Resolution: String, CaseIterable, Comparable, Defaults.Serializable {
2022-04-10 02:49:00 +05:30
case hd2160p60
case hd2160p50
case hd2160p48
2022-06-17 16:22:10 +05:30
case hd2160p30
2022-03-28 00:29:22 +05:30
case hd1440p60
case hd1440p50
case hd1440p48
2022-06-17 16:22:10 +05:30
case hd1440p30
2022-03-28 00:29:22 +05:30
case hd1080p60
case hd1080p50
case hd1080p48
2022-06-17 16:22:10 +05:30
case hd1080p30
2022-03-28 00:29:22 +05:30
case hd720p60
case hd720p50
case hd720p48
2022-06-17 16:22:10 +05:30
case hd720p30
case sd480p30
case sd360p30
case sd240p30
case sd144p30
2022-03-28 00:29:22 +05:30
case unknown
2021-10-17 04:18:58 +05:30
var name: String {
2022-06-17 16:22:10 +05:30
"\(height)p\(refreshRate != -1 && refreshRate != 30 ? ", \(refreshRate) fps" : "")"
2021-10-17 04:18:58 +05:30
}
2021-07-22 18:13:13 +05:30
var height: Int {
2021-10-17 04:18:58 +05:30
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]
2022-06-17 16:22:10 +05:30
if refreshRatePart.isEmpty {
return 30
}
2021-10-17 04:18:58 +05:30
return Int(refreshRatePart.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()) ?? -1
2021-07-22 18:13:13 +05:30
}
2023-06-17 17:39:51 +05:30
static func from(resolution: String, fps: Int? = nil) -> Self {
2022-06-17 16:22:10 +05:30
allCases.first { $0.rawValue.contains(resolution) && $0.refreshRate == (fps ?? 30) } ?? .unknown
2021-07-22 18:13:13 +05:30
}
2023-06-17 17:39:51 +05:30
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.height == rhs.height ? (lhs.refreshRate < rhs.refreshRate) : (lhs.height < rhs.height)
2021-07-22 18:13:13 +05:30
}
}
enum Kind: String, Comparable {
case hls, adaptive, stream
2021-07-22 18:13:13 +05:30
private var sortOrder: Int {
switch self {
2021-10-17 04:18:58 +05:30
case .hls:
2021-07-22 18:13:13 +05:30
return 0
2021-10-17 04:18:58 +05:30
case .stream:
2021-07-22 18:13:13 +05:30
return 1
2021-10-17 04:18:58 +05:30
case .adaptive:
return 2
2021-07-22 18:13:13 +05:30
}
}
2023-06-17 17:39:51 +05:30
static func < (lhs: Self, rhs: Self) -> Bool {
2021-07-22 18:13:13 +05:30
lhs.sortOrder < rhs.sortOrder
}
}
enum Format: String {
case avc1
case mp4
case av1
case webm
case hls
case stream
case unknown
var description: String {
switch self {
case .webm:
return "WebM"
case .hls:
return "adaptive (HLS)"
case .stream:
return "Stream"
default:
return rawValue.uppercased()
}
}
static func from(_ string: String) -> Self {
let lowercased = string.lowercased()
2023-06-17 17:39:51 +05:30
if lowercased.contains("avc1") {
return .avc1
2023-06-17 17:39:51 +05:30
}
if lowercased.contains("mpeg_4") || lowercased.contains("mp4") {
return .mp4
}
2023-06-17 17:39:51 +05:30
if lowercased.contains("av01") {
return .av1
2023-06-17 17:39:51 +05:30
}
if lowercased.contains("webm") {
return .webm
}
if lowercased.contains("stream") {
return .stream
}
if lowercased.contains("hls") {
return .hls
}
2023-06-17 17:39:51 +05:30
return .unknown
}
}
2021-10-17 04:18:58 +05:30
let id = UUID()
var instance: Instance!
var audioAsset: AVURLAsset!
var videoAsset: AVURLAsset!
var hlsURL: URL!
2022-11-10 22:41:28 +05:30
var localURL: URL!
2021-06-14 23:35:02 +05:30
2021-10-17 04:18:58 +05:30
var resolution: Resolution!
var kind: Kind!
var format: Format!
2021-06-14 23:35:02 +05:30
2022-08-21 02:35:40 +05:30
var encoding: String?
var videoFormat: String?
2021-06-14 23:35:02 +05:30
2021-10-17 04:18:58 +05:30
init(
instance: Instance? = nil,
audioAsset: AVURLAsset? = nil,
videoAsset: AVURLAsset? = nil,
hlsURL: URL? = nil,
2022-11-10 22:41:28 +05:30
localURL: URL? = nil,
2021-10-17 04:18:58 +05:30
resolution: Resolution? = nil,
kind: Kind = .hls,
2022-02-17 01:53:11 +05:30
encoding: String? = nil,
videoFormat: String? = nil
2021-10-17 04:18:58 +05:30
) {
self.instance = instance
2021-06-14 23:35:02 +05:30
self.audioAsset = audioAsset
self.videoAsset = videoAsset
2021-10-17 04:18:58 +05:30
self.hlsURL = hlsURL
2022-11-10 22:41:28 +05:30
self.localURL = localURL
2021-06-14 23:35:02 +05:30
self.resolution = resolution
2021-07-22 18:13:13 +05:30
self.kind = kind
2021-06-14 23:35:02 +05:30
self.encoding = encoding
format = .from(videoFormat ?? "")
2021-06-14 23:35:02 +05:30
}
2022-11-10 22:41:28 +05:30
var isLocal: Bool {
localURL != nil
}
2023-05-21 02:19:10 +05:30
var isHLS: Bool {
hlsURL != nil
}
2021-10-17 04:18:58 +05:30
var quality: String {
2022-11-10 22:41:28 +05:30
guard localURL.isNil else { return "Opened File" }
2024-05-10 00:45:14 +05:30
if kind == .hls {
return "adaptive (HLS)"
}
return resolution.name
2022-02-17 01:53:11 +05:30
}
2022-06-15 04:11:49 +05:30
var shortQuality: String {
2022-11-10 22:41:28 +05:30
guard localURL.isNil else { return "File" }
if kind == .hls {
2024-05-10 00:45:14 +05:30
return "adaptive (HLS)"
}
if kind == .stream {
return resolution.name
2022-06-15 04:11:49 +05:30
}
return resolutionAndFormat
2022-06-15 04:11:49 +05:30
}
2021-06-14 23:35:02 +05:30
var description: String {
2022-11-10 22:41:28 +05:30
guard localURL.isNil else { return resolutionAndFormat }
let instanceString = instance.isNil ? "" : " - (\(instance!.description))"
2024-05-10 00:45:14 +05:30
return format != .hls ? "\(resolutionAndFormat)\(instanceString)" : "adaptive (HLS)\(instanceString)"
}
var resolutionAndFormat: String {
let formatString = format == .unknown ? "" : " (\(format.description))"
return "\(quality)\(formatString)"
2021-06-14 23:35:02 +05:30
}
2021-06-15 22:05:21 +05:30
var assets: [AVURLAsset] {
[audioAsset, videoAsset]
}
2021-10-17 04:18:58 +05:30
var videoAssetContainsAudio: Bool {
assets.dropFirst().allSatisfy { $0.url == assets.first!.url }
2021-07-22 18:13:13 +05:30
}
2021-10-17 04:18:58 +05:30
var singleAssetURL: URL? {
2022-11-10 22:41:28 +05:30
guard localURL.isNil else {
return URLBookmarkModel.shared.loadBookmark(localURL) ?? localURL
}
2021-10-17 04:18:58 +05:30
if kind == .hls {
return hlsURL
2023-06-17 17:39:51 +05:30
}
if videoAssetContainsAudio {
2021-10-17 04:18:58 +05:30
return videoAsset.url
}
return nil
}
2021-06-14 23:35:02 +05:30
static func == (lhs: Stream, rhs: Stream) -> Bool {
2021-10-17 04:18:58 +05:30
lhs.id == rhs.id
2021-07-22 18:13:13 +05:30
}
func hash(into hasher: inout Hasher) {
2023-05-21 02:19:10 +05:30
if let url = videoAsset?.url {
hasher.combine(url)
}
if let url = audioAsset?.url {
hasher.combine(url)
}
if let url = hlsURL {
hasher.combine(url)
}
2021-10-17 04:18:58 +05:30
}
2021-06-14 23:35:02 +05:30
}