Merge branch 'main' into use-mpvkit

This commit is contained in:
Arkadiusz Fal
2023-09-23 16:17:33 +02:00
committed by GitHub
104 changed files with 601 additions and 1063 deletions

View File

@@ -76,8 +76,7 @@ final class InstancesModel: ObservableObject {
func standardizedURL(_ url: String) -> String {
if url.count > 7, url.last == "/" {
return String(url.dropLast())
} else {
return url
}
return url
}
}

View File

@@ -65,9 +65,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
if type == "channel" {
return ContentItem(channel: self.extractChannel(from: json))
} else if type == "playlist" {
}
if type == "playlist" {
return ContentItem(playlist: self.extractChannelPlaylist(from: json))
} else if type == "video" {
}
if type == "video" {
return ContentItem(video: self.extractVideo(from: json))
}
@@ -724,9 +726,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
if type == "channel" {
return ContentItem(channel: extractChannel(from: json))
} else if type == "playlist" {
}
if type == "playlist" {
return ContentItem(playlist: extractChannelPlaylist(from: json))
} else if type == "video" {
}
if type == "video" {
return ContentItem(video: extractVideo(from: json))
}

View File

@@ -392,7 +392,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
}
func search(_ query: SearchQuery, page _: String?) -> Resource {
var resource = resource(baseURL: account.url, path: basePathAppending("search/videos"))
resource(baseURL: account.url, path: basePathAppending("search/videos"))
.withParam("search", query.query)
// .withParam("sort_by", query.sortBy.parameter)
// .withParam("type", "all")
@@ -409,7 +409,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
// resource = resource.withParam("page", page)
// }
return resource
// return resource
}
func searchSuggestions(query: String) -> Resource {

View File

@@ -620,6 +620,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
.dictionaryValue["audioStreams"]?
.arrayValue
.filter { $0.dictionaryValue["format"]?.string == "M4A" }
.filter { stream in
let type = stream.dictionaryValue["audioTrackType"]?.string
return type == nil || type == "ORIGINAL"
}
.sorted {
$0.dictionaryValue["bitrate"]?.int ?? 0 >
$1.dictionaryValue["bitrate"]?.int ?? 0

View File

@@ -18,12 +18,14 @@ struct FeedCacheModel: CacheModel {
)
func storeFeed(account: Account, videos: [Video]) {
let date = iso8601DateFormatter.string(from: Date())
logger.info("caching feed \(account.feedCacheKey) -- \(date)")
let feedTimeObject: JSON = ["date": date]
let videosObject: JSON = ["videos": videos.prefix(cacheLimit).map { $0.json.object }]
try? storage?.setObject(feedTimeObject, forKey: feedTimeCacheKey(account.feedCacheKey))
try? storage?.setObject(videosObject, forKey: account.feedCacheKey)
DispatchQueue.global(qos: .background).async {
let date = iso8601DateFormatter.string(from: Date())
logger.info("caching feed \(account.feedCacheKey) -- \(date)")
let feedTimeObject: JSON = ["date": date]
let videosObject: JSON = ["videos": videos.prefix(cacheLimit).map { $0.json.object }]
try? storage?.setObject(feedTimeObject, forKey: feedTimeCacheKey(account.feedCacheKey))
try? storage?.setObject(videosObject, forKey: account.feedCacheKey)
}
}
func retrieveFeed(account: Account) -> [Video] {

View File

@@ -85,7 +85,6 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
self.error = nil
if let channels: [Channel] = resource.typedContent() {
self.channels = channels
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
self.storeChannels(account: account, channels: channels)
FeedModel.shared.calculateUnwatchedFeed()
onSuccess()
@@ -105,16 +104,18 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
}
func storeChannels(account: Account, channels: [Channel]) {
let date = iso8601DateFormatter.string(from: Date())
logger.info("caching channels \(channelsDateCacheKey(account)) -- \(date)")
DispatchQueue.global(qos: .background).async {
let date = self.iso8601DateFormatter.string(from: Date())
self.logger.info("caching channels \(self.channelsDateCacheKey(account)) -- \(date)")
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
let dateObject: JSON = ["date": date]
let channelsObject: JSON = ["channels": channels.map(\.json).map(\.object)]
let dateObject: JSON = ["date": date]
let channelsObject: JSON = ["channels": channels.map(\.json).map(\.object)]
try? storage?.setObject(dateObject, forKey: channelsDateCacheKey(account))
try? storage?.setObject(channelsObject, forKey: channelsCacheKey(account))
try? self.storage?.setObject(dateObject, forKey: self.channelsDateCacheKey(account))
try? self.storage?.setObject(channelsObject, forKey: self.channelsCacheKey(account))
}
}
func getChannels(account: Account) -> [Channel] {

View File

@@ -110,7 +110,7 @@ struct Channel: Identifiable, Hashable {
}
func hasData(for contentType: ContentType) -> Bool {
return tabs.contains { $0.contentType == contentType }
tabs.contains { $0.contentType == contentType }
}
var cacheKey: String {

View File

@@ -15,7 +15,7 @@ struct ContentItem: Identifiable {
}
}
static func < (lhs: ContentType, rhs: ContentType) -> Bool {
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.sortOrder < rhs.sortOrder
}
}
@@ -30,19 +30,19 @@ struct ContentItem: Identifiable {
var id: String = UUID().uuidString
static func array(of videos: [Video]) -> [ContentItem] {
static func array(of videos: [Video]) -> [Self] {
videos.map { Self(video: $0) }
}
static func array(of playlists: [ChannelPlaylist]) -> [ContentItem] {
static func array(of playlists: [ChannelPlaylist]) -> [Self] {
playlists.map { Self(playlist: $0) }
}
static func array(of channels: [Channel]) -> [ContentItem] {
static func array(of channels: [Channel]) -> [Self] {
channels.map { Self(channel: $0) }
}
static func < (lhs: ContentItem, rhs: ContentItem) -> Bool {
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.contentType < rhs.contentType
}

View File

@@ -215,7 +215,7 @@ extension Country {
case .lk: return "Sri Lanka"
case .se: return "Sweden"
case .ch: return "Switzerland"
case .tw: return "Taiwan, Province of China[a]"
case .tw: return "Taiwan"
case .tz: return "Tanzania, United Republic of"
case .th: return "Thailand"
case .tn: return "Tunisia"

View File

@@ -47,7 +47,7 @@ struct FavoriteItem: Codable, Equatable, Identifiable, Defaults.Serializable {
}
}
static func == (lhs: FavoriteItem, rhs: FavoriteItem) -> Bool {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.section == rhs.section
}

View File

@@ -17,7 +17,11 @@ struct FavoritesModel {
}
func toggle(_ item: FavoriteItem) {
contains(item) ? remove(item) : add(item)
if contains(item) {
remove(item)
} else {
add(item)
}
}
func add(_ item: FavoriteItem) {

View File

@@ -52,7 +52,6 @@ extension PlayerModel {
let id = currentVideo.videoID
let time = time ?? backend.currentTime
let seconds = time?.seconds ?? 0
let duration = playerTime.duration.seconds
if seconds < 3 {
return
}

View File

@@ -45,7 +45,8 @@ final class InstancesManifest: Service, ObservableObject {
instancesList?.load().onSuccess { response in
if let instances: [ManifestedInstance] = response.typedContent() {
guard let instance = instances.filter { $0.country == country }.randomElement() else { return }
let countryInstances = instances.filter { $0.country == country }
guard let instance = countryInstances.randomElement() else { return }
let account = instance.anonymousAccount
AccountsModel.shared.publicAccount = account
if asCurrent {

View File

@@ -63,7 +63,7 @@ struct OpenVideosModel {
return []
}
func openURLsFromClipboard(removeQueueItems: Bool = false, playbackMode: OpenVideosModel.PlaybackMode = .playNow) {
func openURLsFromClipboard(removeQueueItems: Bool = false, playbackMode: Self.PlaybackMode = .playNow) {
if urlsFromClipboard.isEmpty {
NavigationModel.shared.alert = Alert(title: Text("Could not find any links to open in your clipboard".localized()))
if NavigationModel.shared.presentingOpenVideos {
@@ -76,7 +76,7 @@ struct OpenVideosModel {
}
}
func openURLs(_ urls: [URL], removeQueueItems: Bool = false, playbackMode: OpenVideosModel.PlaybackMode = .playNow) {
func openURLs(_ urls: [URL], removeQueueItems: Bool = false, playbackMode: Self.PlaybackMode = .playNow) {
guard !urls.isEmpty else {
return
}

View File

@@ -174,7 +174,11 @@ final class AVPlayerBackend: PlayerBackend {
}
func togglePlay() {
isPlaying ? pause() : play()
if isPlaying {
pause()
} else {
play()
}
}
func stop() {
@@ -414,9 +418,8 @@ final class AVPlayerBackend: PlayerBackend {
private func playerItem(_: Stream) -> AVPlayerItem? {
if let asset {
return AVPlayerItem(asset: asset)
} else {
return AVPlayerItem(asset: composition)
}
return AVPlayerItem(asset: composition)
}
private func attachMetadata() {

View File

@@ -357,7 +357,11 @@ final class MPVBackend: PlayerBackend {
}
func togglePlay() {
isPlaying ? pause() : play()
if isPlaying {
pause()
} else {
play()
}
}
func cancelLoads() {

View File

@@ -86,7 +86,7 @@ final class MPVClient: ObservableObject {
]
if mpv_render_context_create(&mpvGL, mpv, &params) < 0 {
puts("failed to initialize mpv GL context")
print("failed to initialize mpv GL context")
exit(1)
}
@@ -471,9 +471,8 @@ final class MPVClient: ObservableObject {
let data = Data(bufPtr)
if let lastIndex = data.lastIndex(where: { $0 != 0 }) {
return String(data: data[0 ... lastIndex], encoding: .isoLatin1)!
} else {
return String(data: data, encoding: .isoLatin1)!
}
return String(data: data, encoding: .isoLatin1)!
}
}
}

View File

@@ -14,7 +14,7 @@ final class PlayerControlsModel: ObservableObject {
var timer: Timer?
#if os(tvOS)
private(set) var reporter = PassthroughSubject<String, Never>()
private(set) var reporter = PassthroughSubject<String, Never>() // swiftlint:disable:this private_subject
#endif
var player: PlayerModel! { .shared }
@@ -106,7 +106,11 @@ final class PlayerControlsModel: ObservableObject {
}
func toggle() {
presentingControls ? hide() : show()
if presentingControls {
hide()
} else {
show()
}
}
func resetTimer() {

View File

@@ -717,7 +717,11 @@ final class PlayerModel: ObservableObject {
}
func togglePiPAction() {
(pipController?.isPictureInPictureActive ?? false) ? closePiP() : startPiP()
if pipController?.isPictureInPictureActive ?? false {
closePiP()
} else {
startPiP()
}
}
#if os(iOS)
@@ -812,12 +816,12 @@ final class PlayerModel: ObservableObject {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
guard let self else { return }
self.playerAPI(item.video)?.loadDetails(item, completionHandler: { newItem in
self.playerAPI(item.video)?.loadDetails(item, failureHandler: nil) { newItem in
guard newItem.videoID == self.autoplayItem?.videoID else { return }
self.autoplayItem = newItem
self.updateRemoteCommandCenter()
self.controls.objectWillChange.send()
})
}
}
}
}

View File

@@ -328,13 +328,13 @@ extension PlayerModel {
}
playerAPI(video)?
.loadDetails(item, completionHandler: { [weak self] newItem in
.loadDetails(item, failureHandler: nil) { [weak self] newItem in
guard let self else { return }
replaceQueueItem(newItem)
self.logger.info("LOADED queue details: \(videoID)")
})
}
}
private func videoLoadFailureHandler(_ error: RequestError, video: Video? = nil) {

View File

@@ -60,7 +60,7 @@ struct Playlist: Identifiable, Equatable, Hashable {
)
}
static func == (lhs: Playlist, rhs: Playlist) -> Bool {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id && lhs.updated == rhs.updated
}

View File

@@ -62,7 +62,8 @@ struct QualityProfile: Hashable, Identifiable, Defaults.Serializable {
var formatsDescription: String {
if formats.count == Format.allCases.count {
return "Any format".localized()
} else if formats.count <= 3 {
}
if formats.count <= 3 {
return formats.map(\.description).joined(separator: ", ")
}

View File

@@ -4,7 +4,7 @@ import Siesta
final class Store<Data>: ResourceObserver, ObservableObject {
@Published private var all: Data?
var collection: Data { all ?? ([] as! Data) }
var collection: Data { all ?? ([item].compactMap { $0 } as! Data) }
var item: Data? { all }
init(_ data: Data? = nil) {

View File

@@ -54,11 +54,11 @@ class Stream: Equatable, Hashable, Identifiable {
return Int(refreshRatePart.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()) ?? -1
}
static func from(resolution: String, fps: Int? = nil) -> Resolution {
static func from(resolution: String, fps: Int? = nil) -> Self {
allCases.first { $0.rawValue.contains(resolution) && $0.refreshRate == (fps ?? 30) } ?? .unknown
}
static func < (lhs: Resolution, rhs: Resolution) -> Bool {
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.height == rhs.height ? (lhs.refreshRate < rhs.refreshRate) : (lhs.height < rhs.height)
}
}
@@ -77,7 +77,7 @@ class Stream: Equatable, Hashable, Identifiable {
}
}
static func < (lhs: Kind, rhs: Kind) -> Bool {
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.sortOrder < rhs.sortOrder
}
}
@@ -123,15 +123,17 @@ class Stream: Equatable, Hashable, Identifiable {
if lowercased.contains("webm") {
return .webm
} else if lowercased.contains("avc1") {
return .avc1
} else if lowercased.contains("av01") {
return .av1
} else if lowercased.contains("mpeg_4") || lowercased.contains("mp4") {
return .mp4
} else {
return .unknown
}
if lowercased.contains("avc1") {
return .avc1
}
if lowercased.contains("av01") {
return .av1
}
if lowercased.contains("mpeg_4") || lowercased.contains("mp4") {
return .mp4
}
return .unknown
}
}
@@ -190,9 +192,8 @@ class Stream: Equatable, Hashable, Identifiable {
if kind == .hls {
return "HLS"
} else {
return resolution?.name ?? "?"
}
return resolution?.name ?? "?"
}
var description: String {
@@ -221,7 +222,8 @@ class Stream: Equatable, Hashable, Identifiable {
if kind == .hls {
return hlsURL
} else if videoAssetContainsAudio {
}
if videoAssetContainsAudio {
return videoAsset.url
}

View File

@@ -45,7 +45,7 @@ struct URLBookmarkModel {
func saveBookmark(_ url: NSURL) {
guard url.isFileURL else {
logger.error("trying to save bookmark for something that is not a file")
logger.error("not a file: \(url.absoluteString)")
logger.error("not a file: \(url.absoluteString ?? "unknown")")
return
}

View File

@@ -83,7 +83,7 @@ struct Video: Identifiable, Equatable, Hashable {
dislikes: Int? = nil,
keywords: [String] = [],
streams: [Stream] = [],
related: [Video] = [],
related: [Self] = [],
chapters: [Chapter] = [],
captions: [Captions] = []
) {
@@ -116,7 +116,7 @@ struct Video: Identifiable, Equatable, Hashable {
self.captions = captions
}
static func local(_ url: URL) -> Video {
static func local(_ url: URL) -> Self {
Self(
app: .local,
videoID: url.absoluteString,
@@ -249,7 +249,7 @@ struct Video: Identifiable, Equatable, Hashable {
thumbnails.first { $0.quality == quality }?.url
}
static func == (lhs: Video, rhs: Video) -> Bool {
static func == (lhs: Self, rhs: Self) -> Bool {
let videoIDIsEqual = lhs.videoID == rhs.videoID
if !lhs.indexID.isNil, !rhs.indexID.isNil {