mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Implement SponsorBlock API
This commit is contained in:
parent
9d7abda63f
commit
d551dee426
@ -117,6 +117,14 @@ struct PlayerViewController: UIViewControllerRepresentable {
|
|||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
controller.transportBarCustomMenuItems = items
|
controller.transportBarCustomMenuItems = items
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if let skip = skipSegmentAction {
|
||||||
|
if controller.contextualActions.isEmpty {
|
||||||
|
controller.contextualActions = [skip]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
controller.contextualActions = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var streamingQualityMenu: UIMenu {
|
fileprivate var streamingQualityMenu: UIMenu {
|
||||||
@ -149,4 +157,16 @@ struct PlayerViewController: UIViewControllerRepresentable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var skipSegmentAction: UIAction? {
|
||||||
|
if state.currentSegment == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIAction(title: "Skip \(state.currentSegment!.title())") { _ in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
state.player.seek(to: state.currentSegment!.skipTo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,23 +14,34 @@ final class PlayerState: ObservableObject {
|
|||||||
@Published private(set) var streamToLoad: Stream!
|
@Published private(set) var streamToLoad: Stream!
|
||||||
@Published private(set) var streamLoading = false
|
@Published private(set) var streamLoading = false
|
||||||
|
|
||||||
|
@Published private(set) var currentTime: CMTime?
|
||||||
@Published private(set) var savedTime: CMTime?
|
@Published private(set) var savedTime: CMTime?
|
||||||
|
@Published var currentSegment: Segment?
|
||||||
|
|
||||||
var playerItem: AVPlayerItem {
|
var playerItem: AVPlayerItem {
|
||||||
let playerItem = AVPlayerItem(asset: composition)
|
let playerItem = AVPlayerItem(asset: composition)
|
||||||
|
|
||||||
playerItem.externalMetadata = [makeMetadataItem(.commonIdentifierTitle, value: video.title)]
|
playerItem.externalMetadata = [
|
||||||
|
makeMetadataItem(.commonIdentifierTitle, value: video.title),
|
||||||
|
makeMetadataItem(.quickTimeMetadataGenre, value: video.genre),
|
||||||
|
makeMetadataItem(.commonIdentifierDescription, value: video.description)
|
||||||
|
]
|
||||||
playerItem.preferredForwardBufferDuration = 10
|
playerItem.preferredForwardBufferDuration = 10
|
||||||
|
|
||||||
return playerItem
|
return playerItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var segmentsProvider: SponsorBlockSegmentsProvider
|
||||||
|
var timeObserver: Any?
|
||||||
|
|
||||||
init(_ video: Video) {
|
init(_ video: Video) {
|
||||||
self.video = video
|
self.video = video
|
||||||
|
segmentsProvider = SponsorBlockSegmentsProvider(video.id)
|
||||||
|
|
||||||
|
segmentsProvider.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("destr deinit")
|
|
||||||
destroyPlayer()
|
destroyPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +62,15 @@ final class PlayerState: ObservableObject {
|
|||||||
func streamDidLoad(_ stream: Stream?) {
|
func streamDidLoad(_ stream: Stream?) {
|
||||||
logger.info("didload stream: \(stream!.description)")
|
logger.info("didload stream: \(stream!.description)")
|
||||||
|
|
||||||
|
currentStream?.cancelLoadingAssets()
|
||||||
currentStream = stream
|
currentStream = stream
|
||||||
streamLoading = streamToLoad != stream
|
streamLoading = streamToLoad != stream
|
||||||
|
|
||||||
if streamToLoad == stream {
|
if streamToLoad == stream {
|
||||||
streamToLoad = nil
|
streamToLoad = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTimeObserver()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cancelLoadingStream(_ stream: Stream) {
|
func cancelLoadingStream(_ stream: Stream) {
|
||||||
@ -121,6 +135,18 @@ final class PlayerState: ObservableObject {
|
|||||||
|
|
||||||
player.cancelPendingPrerolls()
|
player.cancelPendingPrerolls()
|
||||||
player.replaceCurrentItem(with: nil)
|
player.replaceCurrentItem(with: nil)
|
||||||
|
|
||||||
|
if timeObserver != nil {
|
||||||
|
player.removeTimeObserver(timeObserver!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTimeObserver() {
|
||||||
|
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) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeMetadataItem(_ identifier: AVMetadataIdentifier, value: Any) -> AVMetadataItem {
|
private func makeMetadataItem(_ identifier: AVMetadataIdentifier, value: Any) -> AVMetadataItem {
|
||||||
|
38
Model/Segment.swift
Normal file
38
Model/Segment.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import CoreMedia
|
||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
// swiftlint:disable:next final_class
|
||||||
|
class Segment: ObservableObject, Hashable {
|
||||||
|
let category: String
|
||||||
|
let segment: [Double]
|
||||||
|
let uuid: String
|
||||||
|
let videoDuration: Int
|
||||||
|
|
||||||
|
init(category: String, segment: [Double], uuid: String, videoDuration: Int) {
|
||||||
|
self.category = category
|
||||||
|
self.segment = segment
|
||||||
|
self.uuid = uuid
|
||||||
|
self.videoDuration = videoDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeInSegment(_ time: CMTime) -> Bool {
|
||||||
|
(segment.first! ... segment.last!).contains(time.seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
var skipTo: CMTime {
|
||||||
|
CMTime(seconds: segment.last!, preferredTimescale: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func == (lhs: Segment, rhs: Segment) -> Bool {
|
||||||
|
lhs.uuid == rhs.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
func title() -> String {
|
||||||
|
category
|
||||||
|
}
|
||||||
|
}
|
24
Model/SponsorBlockSegment.swift
Normal file
24
Model/SponsorBlockSegment.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
final class SponsorBlockSegment: Segment {
|
||||||
|
init(_ json: JSON) {
|
||||||
|
super.init(
|
||||||
|
category: json["category"].string!,
|
||||||
|
segment: json["segment"].array!.map { $0.double! },
|
||||||
|
uuid: json["UUID"].string!,
|
||||||
|
videoDuration: json["videoDuration"].int!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func title() -> String {
|
||||||
|
switch category {
|
||||||
|
case "selfpromo":
|
||||||
|
return "self-promotion"
|
||||||
|
case "music_offtopic":
|
||||||
|
return "to music"
|
||||||
|
default:
|
||||||
|
return category
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Model/SponsorBlockSegmentsProvider.swift
Normal file
35
Model/SponsorBlockSegmentsProvider.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
final class SponsorBlockSegmentsProvider: ObservableObject {
|
||||||
|
let categories = ["sponsor", "selfpromo", "outro", "intro", "music_offtopic", "interaction"]
|
||||||
|
|
||||||
|
@Published var video: Video?
|
||||||
|
|
||||||
|
@Published var segments = [Segment]()
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
|
||||||
|
init(_ id: String) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
func load() {
|
||||||
|
AF.request("https://sponsor.ajay.app/api/skipSegments", parameters: parameters).responseJSON { response in
|
||||||
|
switch response.result {
|
||||||
|
case let .success(value):
|
||||||
|
self.segments = JSON(value).arrayValue.map { SponsorBlockSegment($0) }
|
||||||
|
case let .failure(error):
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var parameters: [String: String] {
|
||||||
|
[
|
||||||
|
"videoID": id,
|
||||||
|
"categories": JSON(categories).rawString(String.Encoding.utf8)!
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,6 @@ final class TrendingCountriesProvider: DataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.query = query
|
self.query = query
|
||||||
countries = Country.searchByName(query)
|
countries = Country.search(query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,29 +12,11 @@ final class Video: Identifiable, ObservableObject {
|
|||||||
var published: String
|
var published: String
|
||||||
var views: Int
|
var views: Int
|
||||||
var channelID: String
|
var channelID: String
|
||||||
|
var description: String
|
||||||
|
var genre: String
|
||||||
|
|
||||||
var streams = [Stream]()
|
var streams = [Stream]()
|
||||||
|
|
||||||
init(
|
|
||||||
id: String,
|
|
||||||
title: String,
|
|
||||||
thumbnailURL: URL?,
|
|
||||||
author: String,
|
|
||||||
length: TimeInterval,
|
|
||||||
published: String,
|
|
||||||
views: Int,
|
|
||||||
channelID: String
|
|
||||||
) {
|
|
||||||
self.id = id
|
|
||||||
self.title = title
|
|
||||||
self.thumbnailURL = thumbnailURL
|
|
||||||
self.author = author
|
|
||||||
self.length = length
|
|
||||||
self.published = published
|
|
||||||
self.views = views
|
|
||||||
self.channelID = channelID
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ json: JSON) {
|
init(_ json: JSON) {
|
||||||
id = json["videoId"].stringValue
|
id = json["videoId"].stringValue
|
||||||
title = json["title"].stringValue
|
title = json["title"].stringValue
|
||||||
@ -43,6 +25,9 @@ final class Video: Identifiable, ObservableObject {
|
|||||||
published = json["publishedText"].stringValue
|
published = json["publishedText"].stringValue
|
||||||
views = json["viewCount"].intValue
|
views = json["viewCount"].intValue
|
||||||
channelID = json["authorId"].stringValue
|
channelID = json["authorId"].stringValue
|
||||||
|
description = json["description"].stringValue
|
||||||
|
genre = json["genre"].stringValue
|
||||||
|
|
||||||
thumbnailURL = extractThumbnailURL(from: json)
|
thumbnailURL = extractThumbnailURL(from: json)
|
||||||
|
|
||||||
streams = extractFormatStreams(from: json["formatStreams"].arrayValue)
|
streams = extractFormatStreams(from: json["formatStreams"].arrayValue)
|
||||||
|
@ -82,6 +82,9 @@
|
|||||||
37B767DC2677C3CA0098BAA8 /* PlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerState.swift */; };
|
37B767DC2677C3CA0098BAA8 /* PlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerState.swift */; };
|
||||||
37B767DD2677C3CA0098BAA8 /* PlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerState.swift */; };
|
37B767DD2677C3CA0098BAA8 /* PlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B767DA2677C3CA0098BAA8 /* PlayerState.swift */; };
|
||||||
37B767E02678C5BF0098BAA8 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 37B767DF2678C5BF0098BAA8 /* Logging */; };
|
37B767E02678C5BF0098BAA8 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 37B767DF2678C5BF0098BAA8 /* Logging */; };
|
||||||
|
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
|
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
|
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
37C7A9042679059200E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
37C7A9042679059200E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
||||||
37C7A905267905AE00E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
37C7A905267905AE00E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
||||||
37C7A906267905AF00E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
37C7A906267905AF00E721B4 /* AVKeyValueStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */; };
|
||||||
@ -128,6 +131,12 @@
|
|||||||
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
37D4B1B62672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
37D4B1B62672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
|
37EAD86B267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift */; };
|
||||||
|
37EAD86C267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift */; };
|
||||||
|
37EAD86D267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86A267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift */; };
|
||||||
|
37EAD86F267B9ED100D9E01B /* Segment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86E267B9ED100D9E01B /* Segment.swift */; };
|
||||||
|
37EAD870267B9ED100D9E01B /* Segment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86E267B9ED100D9E01B /* Segment.swift */; };
|
||||||
|
37EAD871267B9ED100D9E01B /* Segment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EAD86E267B9ED100D9E01B /* Segment.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -177,6 +186,7 @@
|
|||||||
37AAF29B26741B5F007FC770 /* SubscriptionVideosProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionVideosProvider.swift; sourceTree = "<group>"; };
|
37AAF29B26741B5F007FC770 /* SubscriptionVideosProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionVideosProvider.swift; sourceTree = "<group>"; };
|
||||||
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsView.swift; sourceTree = "<group>"; };
|
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsView.swift; sourceTree = "<group>"; };
|
||||||
37B767DA2677C3CA0098BAA8 /* PlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerState.swift; sourceTree = "<group>"; };
|
37B767DA2677C3CA0098BAA8 /* PlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerState.swift; sourceTree = "<group>"; };
|
||||||
|
37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegment.swift; sourceTree = "<group>"; };
|
||||||
37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVKeyValueStatus+String.swift"; sourceTree = "<group>"; };
|
37C7A9032679059200E721B4 /* AVKeyValueStatus+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVKeyValueStatus+String.swift"; sourceTree = "<group>"; };
|
||||||
37CEE4B42677B628005A1EFE /* StreamType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamType.swift; sourceTree = "<group>"; };
|
37CEE4B42677B628005A1EFE /* StreamType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamType.swift; sourceTree = "<group>"; };
|
||||||
37CEE4B82677B63F005A1EFE /* StreamResolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamResolution.swift; sourceTree = "<group>"; };
|
37CEE4B82677B63F005A1EFE /* StreamResolution.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamResolution.swift; sourceTree = "<group>"; };
|
||||||
@ -202,6 +212,8 @@
|
|||||||
37D4B1AE26729DEB00C925CA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
37D4B1AE26729DEB00C925CA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
37D4B1AF2672A01000C925CA /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = "<group>"; };
|
37D4B1AF2672A01000C925CA /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = "<group>"; };
|
||||||
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsProvider.swift; sourceTree = "<group>"; };
|
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsProvider.swift; sourceTree = "<group>"; };
|
||||||
|
37EAD86A267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegmentsProvider.swift; sourceTree = "<group>"; };
|
||||||
|
37EAD86E267B9ED100D9E01B /* Segment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Segment.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -376,16 +388,19 @@
|
|||||||
37B767DA2677C3CA0098BAA8 /* PlayerState.swift */,
|
37B767DA2677C3CA0098BAA8 /* PlayerState.swift */,
|
||||||
37D4B19226717CE100C925CA /* PopularVideosProvider.swift */,
|
37D4B19226717CE100C925CA /* PopularVideosProvider.swift */,
|
||||||
37AAF2812673791F007FC770 /* SearchedVideosProvider.swift */,
|
37AAF2812673791F007FC770 /* SearchedVideosProvider.swift */,
|
||||||
|
37EAD86E267B9ED100D9E01B /* Segment.swift */,
|
||||||
|
37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */,
|
||||||
|
37EAD86A267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift */,
|
||||||
37CEE4C02677B697005A1EFE /* Stream.swift */,
|
37CEE4C02677B697005A1EFE /* Stream.swift */,
|
||||||
37CEE4B82677B63F005A1EFE /* StreamResolution.swift */,
|
37CEE4B82677B63F005A1EFE /* StreamResolution.swift */,
|
||||||
37CEE4B42677B628005A1EFE /* StreamType.swift */,
|
37CEE4B42677B628005A1EFE /* StreamType.swift */,
|
||||||
37AAF29B26741B5F007FC770 /* SubscriptionVideosProvider.swift */,
|
37AAF29B26741B5F007FC770 /* SubscriptionVideosProvider.swift */,
|
||||||
|
3705B181267B4E4900704544 /* TrendingCategory.swift */,
|
||||||
3714167A267AA1CF006CA35D /* TrendingCountriesProvider.swift */,
|
3714167A267AA1CF006CA35D /* TrendingCountriesProvider.swift */,
|
||||||
37141676267A9AAD006CA35D /* TrendingState.swift */,
|
37141676267A9AAD006CA35D /* TrendingState.swift */,
|
||||||
|
3714167E267AB55D006CA35D /* TrendingVideosProvider.swift */,
|
||||||
37D4B19626717E1500C925CA /* Video.swift */,
|
37D4B19626717E1500C925CA /* Video.swift */,
|
||||||
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */,
|
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */,
|
||||||
3714167E267AB55D006CA35D /* TrendingVideosProvider.swift */,
|
|
||||||
3705B181267B4E4900704544 /* TrendingCategory.swift */,
|
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -639,6 +654,7 @@
|
|||||||
37D4B19326717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
37D4B19326717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
||||||
37AAF29C26741B5F007FC770 /* SubscriptionVideosProvider.swift in Sources */,
|
37AAF29C26741B5F007FC770 /* SubscriptionVideosProvider.swift in Sources */,
|
||||||
37141668267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
37141668267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
||||||
|
37EAD86B267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */,
|
||||||
377FC7E6267A085600A6BBAF /* PlayerView.swift in Sources */,
|
377FC7E6267A085600A6BBAF /* PlayerView.swift in Sources */,
|
||||||
37CEE4C12677B697005A1EFE /* Stream.swift in Sources */,
|
37CEE4C12677B697005A1EFE /* Stream.swift in Sources */,
|
||||||
37141677267A9AAD006CA35D /* TrendingState.swift in Sources */,
|
37141677267A9AAD006CA35D /* TrendingState.swift in Sources */,
|
||||||
@ -646,6 +662,7 @@
|
|||||||
377FC7DC267A081800A6BBAF /* PopularVideosView.swift in Sources */,
|
377FC7DC267A081800A6BBAF /* PopularVideosView.swift in Sources */,
|
||||||
3714167F267AB55D006CA35D /* TrendingVideosProvider.swift in Sources */,
|
3714167F267AB55D006CA35D /* TrendingVideosProvider.swift in Sources */,
|
||||||
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||||
|
37EAD86F267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||||
37CEE4B52677B628005A1EFE /* StreamType.swift in Sources */,
|
37CEE4B52677B628005A1EFE /* StreamType.swift in Sources */,
|
||||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
377FC7E3267A084A00A6BBAF /* VideoThumbnailView.swift in Sources */,
|
377FC7E3267A084A00A6BBAF /* VideoThumbnailView.swift in Sources */,
|
||||||
@ -657,6 +674,7 @@
|
|||||||
377FC7E9267A085D00A6BBAF /* PlayerViewController.swift in Sources */,
|
377FC7E9267A085D00A6BBAF /* PlayerViewController.swift in Sources */,
|
||||||
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */,
|
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */,
|
||||||
377FC7E1267A082600A6BBAF /* ChannelView.swift in Sources */,
|
377FC7E1267A082600A6BBAF /* ChannelView.swift in Sources */,
|
||||||
|
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||||
37C7A9042679059200E721B4 /* AVKeyValueStatus+String.swift in Sources */,
|
37C7A9042679059200E721B4 /* AVKeyValueStatus+String.swift in Sources */,
|
||||||
37B767DB2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
37B767DB2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
||||||
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
||||||
@ -678,6 +696,7 @@
|
|||||||
37D4B19426717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
37D4B19426717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
||||||
37AAF29D26741B5F007FC770 /* SubscriptionVideosProvider.swift in Sources */,
|
37AAF29D26741B5F007FC770 /* SubscriptionVideosProvider.swift in Sources */,
|
||||||
37141669267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
37141669267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
||||||
|
37EAD86C267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */,
|
||||||
377FC7E7267A085600A6BBAF /* PlayerView.swift in Sources */,
|
377FC7E7267A085600A6BBAF /* PlayerView.swift in Sources */,
|
||||||
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
|
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
|
||||||
37141678267A9AAD006CA35D /* TrendingState.swift in Sources */,
|
37141678267A9AAD006CA35D /* TrendingState.swift in Sources */,
|
||||||
@ -685,6 +704,7 @@
|
|||||||
377FC7DD267A081A00A6BBAF /* PopularVideosView.swift in Sources */,
|
377FC7DD267A081A00A6BBAF /* PopularVideosView.swift in Sources */,
|
||||||
37141680267AB55D006CA35D /* TrendingVideosProvider.swift in Sources */,
|
37141680267AB55D006CA35D /* TrendingVideosProvider.swift in Sources */,
|
||||||
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||||
|
37EAD870267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||||
37CEE4B62677B628005A1EFE /* StreamType.swift in Sources */,
|
37CEE4B62677B628005A1EFE /* StreamType.swift in Sources */,
|
||||||
37141670267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
37141670267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
377FC7E2267A084A00A6BBAF /* VideoThumbnailView.swift in Sources */,
|
377FC7E2267A084A00A6BBAF /* VideoThumbnailView.swift in Sources */,
|
||||||
@ -696,6 +716,7 @@
|
|||||||
377FC7E8267A085D00A6BBAF /* PlayerViewController.swift in Sources */,
|
377FC7E8267A085D00A6BBAF /* PlayerViewController.swift in Sources */,
|
||||||
377FC7E4267A084E00A6BBAF /* SearchView.swift in Sources */,
|
377FC7E4267A084E00A6BBAF /* SearchView.swift in Sources */,
|
||||||
377FC7E0267A082600A6BBAF /* ChannelView.swift in Sources */,
|
377FC7E0267A082600A6BBAF /* ChannelView.swift in Sources */,
|
||||||
|
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||||
37C7A906267905AF00E721B4 /* AVKeyValueStatus+String.swift in Sources */,
|
37C7A906267905AF00E721B4 /* AVKeyValueStatus+String.swift in Sources */,
|
||||||
37B767DC2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
37B767DC2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
||||||
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
||||||
@ -730,6 +751,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
37AAF28026737550007FC770 /* SearchView.swift in Sources */,
|
37AAF28026737550007FC770 /* SearchView.swift in Sources */,
|
||||||
|
37EAD871267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||||
37CEE4BF2677B670005A1EFE /* AudioVideoStream.swift in Sources */,
|
37CEE4BF2677B670005A1EFE /* AudioVideoStream.swift in Sources */,
|
||||||
37CEE4B72677B628005A1EFE /* StreamType.swift in Sources */,
|
37CEE4B72677B628005A1EFE /* StreamType.swift in Sources */,
|
||||||
3714166A267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
3714166A267A83F9006CA35D /* StreamAVPlayerViewController.swift in Sources */,
|
||||||
@ -742,6 +764,7 @@
|
|||||||
37D4B1B22672A01000C925CA /* DataProvider.swift in Sources */,
|
37D4B1B22672A01000C925CA /* DataProvider.swift in Sources */,
|
||||||
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
37AAF29226740715007FC770 /* AppState.swift in Sources */,
|
37AAF29226740715007FC770 /* AppState.swift in Sources */,
|
||||||
|
37EAD86D267B9C5600D9E01B /* SponsorBlockSegmentsProvider.swift in Sources */,
|
||||||
3705B17C267B4D9A00704544 /* VisualEffectView.swift in Sources */,
|
3705B17C267B4D9A00704544 /* VisualEffectView.swift in Sources */,
|
||||||
3741B5302676213400125C5E /* PlayerViewController.swift in Sources */,
|
3741B5302676213400125C5E /* PlayerViewController.swift in Sources */,
|
||||||
37B767DD2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
37B767DD2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
||||||
@ -751,6 +774,7 @@
|
|||||||
37AAF27E26737323007FC770 /* PopularVideosView.swift in Sources */,
|
37AAF27E26737323007FC770 /* PopularVideosView.swift in Sources */,
|
||||||
37AAF29A26740A01007FC770 /* VideosView.swift in Sources */,
|
37AAF29A26740A01007FC770 /* VideosView.swift in Sources */,
|
||||||
37AAF2962674086B007FC770 /* TabSelection.swift in Sources */,
|
37AAF2962674086B007FC770 /* TabSelection.swift in Sources */,
|
||||||
|
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||||
37CEE4C32677B697005A1EFE /* Stream.swift in Sources */,
|
37CEE4C32677B697005A1EFE /* Stream.swift in Sources */,
|
||||||
37AAF28A2673AB89007FC770 /* ChannelView.swift in Sources */,
|
37AAF28A2673AB89007FC770 /* ChannelView.swift in Sources */,
|
||||||
37AAF28E2673ABD3007FC770 /* ChannelVideosProvider.swift in Sources */,
|
37AAF28E2673ABD3007FC770 /* ChannelVideosProvider.swift in Sources */,
|
||||||
|
@ -521,10 +521,18 @@ extension Country {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
static func searchByName(_ name: String) -> [Country] {
|
static func search(_ query: String) -> [Country] {
|
||||||
let countries = filteredCountries { stringFolding($0) == stringFolding(name) }
|
if let country = searchByCode(query) {
|
||||||
|
return [country]
|
||||||
|
}
|
||||||
|
|
||||||
return countries.isEmpty ? searchByPartialName(name) : countries
|
let countries = filteredCountries { stringFolding($0) == stringFolding(query) }
|
||||||
|
|
||||||
|
return countries.isEmpty ? searchByPartialName(query) : countries
|
||||||
|
}
|
||||||
|
|
||||||
|
static func searchByCode(_ code: String) -> Country? {
|
||||||
|
Country(rawValue: code.uppercased())
|
||||||
}
|
}
|
||||||
|
|
||||||
static func searchByPartialName(_ name: String) -> [Country] {
|
static func searchByPartialName(_ name: String) -> [Country] {
|
||||||
@ -540,7 +548,8 @@ extension Country {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func filteredCountries(_ predicate: (String) -> Bool) -> [Country] {
|
private static func filteredCountries(_ predicate: (String) -> Bool) -> [Country] {
|
||||||
Country.allCases.map { $0.name }
|
Country.allCases
|
||||||
|
.map { $0.name }
|
||||||
.filter(predicate)
|
.filter(predicate)
|
||||||
.compactMap { string in Country.allCases.first { $0.name == string } }
|
.compactMap { string in Country.allCases.first { $0.name == string } }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user