mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
CC support with Invidious and MPV
This commit is contained in:
parent
3718311a93
commit
e56ab3804e
@ -428,7 +428,8 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
keywords: json["keywords"].arrayValue.compactMap { $0.string },
|
keywords: json["keywords"].arrayValue.compactMap { $0.string },
|
||||||
streams: extractStreams(from: json),
|
streams: extractStreams(from: json),
|
||||||
related: extractRelated(from: json),
|
related: extractRelated(from: json),
|
||||||
chapters: extractChapters(from: description)
|
chapters: extractChapters(from: description),
|
||||||
|
captions: extractCaptions(from: json)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,4 +567,17 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
channel: Channel(id: channelId, name: author)
|
channel: Channel(id: channelId, name: author)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func extractCaptions(from content: JSON) -> [Captions] {
|
||||||
|
content["captions"].arrayValue.compactMap { details in
|
||||||
|
guard let baseURL = account.url,
|
||||||
|
let url = URL(string: baseURL + details["url"].stringValue) else { return nil }
|
||||||
|
|
||||||
|
return Captions(
|
||||||
|
label: details["label"].stringValue,
|
||||||
|
code: details["language_code"].stringValue,
|
||||||
|
url: url
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
Model/Captions.swift
Normal file
12
Model/Captions.swift
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Captions: Hashable, Identifiable {
|
||||||
|
var id = UUID().uuidString
|
||||||
|
let label: String
|
||||||
|
let code: String
|
||||||
|
let url: URL
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
"\(label) (\(code))"
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,13 @@ final class MPVBackend: PlayerBackend {
|
|||||||
|
|
||||||
var stream: Stream?
|
var stream: Stream?
|
||||||
var video: Video?
|
var video: Video?
|
||||||
|
var captions: Captions? { didSet {
|
||||||
|
guard let captions = captions else {
|
||||||
|
client.removeSubs()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addSubTrack(captions.url)
|
||||||
|
}}
|
||||||
var currentTime: CMTime?
|
var currentTime: CMTime?
|
||||||
|
|
||||||
var loadedVideo = false
|
var loadedVideo = false
|
||||||
@ -155,11 +162,18 @@ final class MPVBackend: PlayerBackend {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
var captions: Captions?
|
||||||
|
if let captionsLanguageCode = Defaults[.captionsLanguageCode] {
|
||||||
|
captions = video.captions.first { $0.code == captionsLanguageCode } ??
|
||||||
|
video.captions.first { $0.code.contains(captionsLanguageCode) }
|
||||||
|
}
|
||||||
|
|
||||||
let updateCurrentStream = {
|
let updateCurrentStream = {
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.stream = stream
|
self?.stream = stream
|
||||||
self?.video = video
|
self?.video = video
|
||||||
self?.model.stream = stream
|
self?.model.stream = stream
|
||||||
|
self?.captions = captions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +225,7 @@ final class MPVBackend: PlayerBackend {
|
|||||||
startPlaying()
|
startPlaying()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.client.loadFile(url, time: time) { [weak self] _ in
|
self.client.loadFile(url, sub: captions?.url, time: time) { [weak self] _ in
|
||||||
self?.isLoadingVideo = true
|
self?.isLoadingVideo = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -223,7 +237,7 @@ final class MPVBackend: PlayerBackend {
|
|||||||
let fileToLoad = self.model.musicMode ? stream.audioAsset.url : stream.videoAsset.url
|
let fileToLoad = self.model.musicMode ? stream.audioAsset.url : stream.videoAsset.url
|
||||||
let audioTrack = self.model.musicMode ? nil : stream.audioAsset.url
|
let audioTrack = self.model.musicMode ? nil : stream.audioAsset.url
|
||||||
|
|
||||||
self.client?.loadFile(fileToLoad, audio: audioTrack, time: time) { [weak self] _ in
|
self.client?.loadFile(fileToLoad, audio: audioTrack, sub: captions?.url, time: time) { [weak self] _ in
|
||||||
self?.isLoadingVideo = true
|
self?.isLoadingVideo = true
|
||||||
self?.pause()
|
self?.pause()
|
||||||
}
|
}
|
||||||
@ -454,6 +468,11 @@ final class MPVBackend: PlayerBackend {
|
|||||||
client?.addVideoTrack(url)
|
client?.addVideoTrack(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addSubTrack(_ url: URL) {
|
||||||
|
client?.removeSubs()
|
||||||
|
client?.addSubTrack(url)
|
||||||
|
}
|
||||||
|
|
||||||
func setVideoToAuto() {
|
func setVideoToAuto() {
|
||||||
client?.setVideoToAuto()
|
client?.setVideoToAuto()
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ final class MPVClient: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFile(_ url: URL, audio: URL? = nil, time: CMTime? = nil, completionHandler: ((Int32) -> Void)? = nil) {
|
func loadFile(_ url: URL, audio: URL? = nil, sub: URL? = nil, time: CMTime? = nil, completionHandler: ((Int32) -> Void)? = nil) {
|
||||||
var args = [url.absoluteString]
|
var args = [url.absoluteString]
|
||||||
var options = [String]()
|
var options = [String]()
|
||||||
|
|
||||||
@ -123,6 +123,10 @@ final class MPVClient: ObservableObject {
|
|||||||
options.append("audio-files-append=\"\(audioURL)\"")
|
options.append("audio-files-append=\"\(audioURL)\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let subURL = sub?.absoluteString {
|
||||||
|
options.append("sub-files-append=\"\(subURL)\"")
|
||||||
|
}
|
||||||
|
|
||||||
args.append(options.joined(separator: ","))
|
args.append(options.joined(separator: ","))
|
||||||
|
|
||||||
command("loadfile", args: args, returnValueCallback: completionHandler)
|
command("loadfile", args: args, returnValueCallback: completionHandler)
|
||||||
@ -263,6 +267,14 @@ final class MPVClient: ObservableObject {
|
|||||||
command("video-add", args: [url.absoluteString])
|
command("video-add", args: [url.absoluteString])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addSubTrack(_ url: URL) {
|
||||||
|
command("sub-add", args: [url.absoluteString])
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeSubs() {
|
||||||
|
command("sub-remove")
|
||||||
|
}
|
||||||
|
|
||||||
func setVideoToAuto() {
|
func setVideoToAuto() {
|
||||||
setString("video", "1")
|
setString("video", "1")
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,11 @@ final class PlayerControlsModel: ObservableObject {
|
|||||||
|
|
||||||
func handleOverlayPresentationChange() {
|
func handleOverlayPresentationChange() {
|
||||||
player?.backend.setNeedsNetworkStateUpdates(presentingControlsOverlay)
|
player?.backend.setNeedsNetworkStateUpdates(presentingControlsOverlay)
|
||||||
|
if presentingControlsOverlay {
|
||||||
|
removeTimer()
|
||||||
|
} else {
|
||||||
|
resetTimer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func show() {
|
func show() {
|
||||||
|
@ -34,6 +34,8 @@ struct Video: Identifiable, Equatable, Hashable {
|
|||||||
var related = [Video]()
|
var related = [Video]()
|
||||||
var chapters = [Chapter]()
|
var chapters = [Chapter]()
|
||||||
|
|
||||||
|
var captions = [Captions]()
|
||||||
|
|
||||||
init(
|
init(
|
||||||
id: String? = nil,
|
id: String? = nil,
|
||||||
videoID: String,
|
videoID: String,
|
||||||
@ -55,7 +57,8 @@ struct Video: Identifiable, Equatable, Hashable {
|
|||||||
keywords: [String] = [],
|
keywords: [String] = [],
|
||||||
streams: [Stream] = [],
|
streams: [Stream] = [],
|
||||||
related: [Video] = [],
|
related: [Video] = [],
|
||||||
chapters: [Chapter] = []
|
chapters: [Chapter] = [],
|
||||||
|
captions: [Captions] = []
|
||||||
) {
|
) {
|
||||||
self.id = id ?? UUID().uuidString
|
self.id = id ?? UUID().uuidString
|
||||||
self.videoID = videoID
|
self.videoID = videoID
|
||||||
@ -78,6 +81,7 @@ struct Video: Identifiable, Equatable, Hashable {
|
|||||||
self.streams = streams
|
self.streams = streams
|
||||||
self.related = related
|
self.related = related
|
||||||
self.chapters = chapters
|
self.chapters = chapters
|
||||||
|
self.captions = captions
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishedDate: String? {
|
var publishedDate: String? {
|
||||||
|
@ -48,6 +48,7 @@ extension Defaults.Keys {
|
|||||||
static let roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
|
static let roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
|
||||||
static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)
|
static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)
|
||||||
|
|
||||||
|
static let captionsLanguageCode = Key<String?>("captionsLanguageCode")
|
||||||
static let activeBackend = Key<PlayerBackendType>("activeBackend", default: .mpv)
|
static let activeBackend = Key<PlayerBackendType>("activeBackend", default: .mpv)
|
||||||
static let quality = Key<ResolutionSetting>("quality", default: .best)
|
static let quality = Key<ResolutionSetting>("quality", default: .best)
|
||||||
static let playerSidebar = Key<PlayerSidebarSetting>("playerSidebar", default: PlayerSidebarSetting.defaultValue)
|
static let playerSidebar = Key<PlayerSidebarSetting>("playerSidebar", default: PlayerSidebarSetting.defaultValue)
|
||||||
|
@ -9,24 +9,27 @@ struct ControlsOverlay: View {
|
|||||||
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
|
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 6) {
|
ScrollView {
|
||||||
HStack {
|
VStack(spacing: 6) {
|
||||||
backendButtons
|
HStack {
|
||||||
}
|
backendButtons
|
||||||
qualityButton
|
}
|
||||||
HStack {
|
qualityButton
|
||||||
decreaseRateButton
|
captionsButton
|
||||||
rateButton
|
HStack {
|
||||||
increaseRateButton
|
decreaseRateButton
|
||||||
}
|
rateButton
|
||||||
#if os(iOS)
|
increaseRateButton
|
||||||
.foregroundColor(.white)
|
}
|
||||||
#endif
|
#if os(iOS)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
#endif
|
||||||
|
|
||||||
if player.activeBackend == .mpv,
|
if player.activeBackend == .mpv,
|
||||||
showMPVPlaybackStats
|
showMPVPlaybackStats
|
||||||
{
|
{
|
||||||
mpvPlaybackStats
|
mpvPlaybackStats
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,6 +131,60 @@ struct ControlsOverlay: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var captionsButton: some View {
|
||||||
|
#if os(macOS)
|
||||||
|
captionsPicker
|
||||||
|
.labelsHidden()
|
||||||
|
.frame(maxWidth: 300)
|
||||||
|
#else
|
||||||
|
Menu {
|
||||||
|
captionsPicker
|
||||||
|
.frame(width: 140, height: 30)
|
||||||
|
.mask(RoundedRectangle(cornerRadius: 3))
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: "text.bubble")
|
||||||
|
if let captions = captionsBinding.wrappedValue {
|
||||||
|
Text(captions.code)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: 140, height: 30)
|
||||||
|
}
|
||||||
|
.transaction { t in t.animation = .none }
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.frame(width: 140, height: 30)
|
||||||
|
.modifier(ControlBackgroundModifier())
|
||||||
|
.mask(RoundedRectangle(cornerRadius: 3))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private var captionsPicker: some View {
|
||||||
|
let captions = player.currentVideo?.captions ?? []
|
||||||
|
Picker("Captions", selection: captionsBinding) {
|
||||||
|
if captions.isEmpty {
|
||||||
|
Text("Not available")
|
||||||
|
} else {
|
||||||
|
Text("Disabled").tag(Captions?.none)
|
||||||
|
}
|
||||||
|
ForEach(captions) { caption in
|
||||||
|
Text(caption.description).tag(Optional(caption))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disabled(captions.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var captionsBinding: Binding<Captions?> {
|
||||||
|
.init(
|
||||||
|
get: { player.mpvBackend.captions },
|
||||||
|
set: {
|
||||||
|
player.mpvBackend.captions = $0
|
||||||
|
Defaults[.captionsLanguageCode] = $0?.code
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder private var rateButton: some View {
|
@ViewBuilder private var rateButton: some View {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
ratePicker
|
ratePicker
|
||||||
@ -185,6 +242,7 @@ struct ControlsOverlay: View {
|
|||||||
struct ControlsOverlay_Previews: PreviewProvider {
|
struct ControlsOverlay_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ControlsOverlay()
|
ControlsOverlay()
|
||||||
|
.environmentObject(NetworkStateModel())
|
||||||
.environmentObject(PlayerModel())
|
.environmentObject(PlayerModel())
|
||||||
.environmentObject(PlayerControlsModel())
|
.environmentObject(PlayerControlsModel())
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ struct PlayerControls: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ControlsOverlay()
|
ControlsOverlay()
|
||||||
|
.frame(height: overlayHeight)
|
||||||
.padding()
|
.padding()
|
||||||
.modifier(ControlBackgroundModifier(enabled: true))
|
.modifier(ControlBackgroundModifier(enabled: true))
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||||
@ -127,6 +128,11 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var overlayHeight: Double {
|
||||||
|
guard let player = player, player.playerSize.height.isFinite else { return 0 }
|
||||||
|
return [0, [player.playerSize.height - 80, 140].min()!].max()!
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder var controlsBackground: some View {
|
@ViewBuilder var controlsBackground: some View {
|
||||||
if player.musicMode,
|
if player.musicMode,
|
||||||
let item = self.player.currentItem,
|
let item = self.player.currentItem,
|
||||||
|
@ -199,7 +199,8 @@ struct VideoPlayerView: View {
|
|||||||
.gesture(
|
.gesture(
|
||||||
DragGesture(coordinateSpace: .global)
|
DragGesture(coordinateSpace: .global)
|
||||||
.onChanged { value in
|
.onChanged { value in
|
||||||
guard player.presentingPlayer else { return }
|
guard player.presentingPlayer,
|
||||||
|
!playerControls.presentingControlsOverlay else { return }
|
||||||
|
|
||||||
let drag = value.translation.height
|
let drag = value.translation.height
|
||||||
|
|
||||||
|
@ -397,7 +397,6 @@
|
|||||||
37732FF52703D32400F04329 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37732FF32703D32400F04329 /* Sidebar.swift */; };
|
37732FF52703D32400F04329 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37732FF32703D32400F04329 /* Sidebar.swift */; };
|
||||||
37737786276F9858000521C1 /* Windows.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37737785276F9858000521C1 /* Windows.swift */; };
|
37737786276F9858000521C1 /* Windows.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37737785276F9858000521C1 /* Windows.swift */; };
|
||||||
3774123327387CB000423605 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; };
|
3774123327387CB000423605 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; };
|
||||||
3774123427387CC100423605 /* InvidiousAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37977582268922F600DD52A8 /* InvidiousAPI.swift */; };
|
|
||||||
3774124927387D2300423605 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
3774124927387D2300423605 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
||||||
3774124A27387D2300423605 /* ContentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FB28402721B22200A57617 /* ContentItem.swift */; };
|
3774124A27387D2300423605 /* ContentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FB28402721B22200A57617 /* ContentItem.swift */; };
|
||||||
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C0698127260B2100F7F6CB /* ThumbnailsModel.swift */; };
|
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C0698127260B2100F7F6CB /* ThumbnailsModel.swift */; };
|
||||||
@ -438,6 +437,9 @@
|
|||||||
3774127827387EB000423605 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127727387EB000423605 /* Logging */; };
|
3774127827387EB000423605 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127727387EB000423605 /* Logging */; };
|
||||||
3774127A27387EBC00423605 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127927387EBC00423605 /* Defaults */; };
|
3774127A27387EBC00423605 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127927387EBC00423605 /* Defaults */; };
|
||||||
3774127C27387EC800423605 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127B27387EC800423605 /* Alamofire */; };
|
3774127C27387EC800423605 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 3774127B27387EC800423605 /* Alamofire */; };
|
||||||
|
3776ADD6287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
|
3776ADD7287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
|
3776ADD8287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
377A20AA2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20AA2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
@ -1048,6 +1050,7 @@
|
|||||||
37732FEF2703A26300F04329 /* AccountValidationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidationStatus.swift; sourceTree = "<group>"; };
|
37732FEF2703A26300F04329 /* AccountValidationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidationStatus.swift; sourceTree = "<group>"; };
|
||||||
37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; };
|
37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; };
|
||||||
37737785276F9858000521C1 /* Windows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = "<group>"; };
|
37737785276F9858000521C1 /* Windows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = "<group>"; };
|
||||||
|
3776ADD5287381240078EBC4 /* Captions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Captions.swift; path = Model/Captions.swift; sourceTree = SOURCE_ROOT; };
|
||||||
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; };
|
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; };
|
||||||
377ABC3F286E4AD5009C986F /* InstancesManifest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstancesManifest.swift; sourceTree = "<group>"; };
|
377ABC3F286E4AD5009C986F /* InstancesManifest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstancesManifest.swift; sourceTree = "<group>"; };
|
||||||
377ABC43286E4B74009C986F /* ManifestedInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestedInstance.swift; sourceTree = "<group>"; };
|
377ABC43286E4B74009C986F /* ManifestedInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestedInstance.swift; sourceTree = "<group>"; };
|
||||||
@ -1964,6 +1967,7 @@
|
|||||||
3751BA8127E69131007B1A60 /* ReturnYouTubeDislike */,
|
3751BA8127E69131007B1A60 /* ReturnYouTubeDislike */,
|
||||||
37FB283F2721B20800A57617 /* Search */,
|
37FB283F2721B20800A57617 /* Search */,
|
||||||
374C0539272436DA009BDDBE /* SponsorBlock */,
|
374C0539272436DA009BDDBE /* SponsorBlock */,
|
||||||
|
3776ADD5287381240078EBC4 /* Captions.swift */,
|
||||||
37AAF28F26740715007FC770 /* Channel.swift */,
|
37AAF28F26740715007FC770 /* Channel.swift */,
|
||||||
37C3A24427235DA70087A57A /* ChannelPlaylist.swift */,
|
37C3A24427235DA70087A57A /* ChannelPlaylist.swift */,
|
||||||
37520698285E8DD300CA655F /* Chapter.swift */,
|
37520698285E8DD300CA655F /* Chapter.swift */,
|
||||||
@ -2672,6 +2676,7 @@
|
|||||||
37130A5F277657300033018A /* PersistenceController.swift in Sources */,
|
37130A5F277657300033018A /* PersistenceController.swift in Sources */,
|
||||||
37FD43E32704847C0073EE42 /* View+Fixtures.swift in Sources */,
|
37FD43E32704847C0073EE42 /* View+Fixtures.swift in Sources */,
|
||||||
37BE0BD626A1D4A90092E2DB /* AppleAVPlayerViewController.swift in Sources */,
|
37BE0BD626A1D4A90092E2DB /* AppleAVPlayerViewController.swift in Sources */,
|
||||||
|
3776ADD6287381240078EBC4 /* Captions.swift in Sources */,
|
||||||
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||||
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||||
37484C1926FC837400287258 /* PlayerSettings.swift in Sources */,
|
37484C1926FC837400287258 /* PlayerSettings.swift in Sources */,
|
||||||
@ -2928,6 +2933,7 @@
|
|||||||
3782B9532755667600990149 /* String+Format.swift in Sources */,
|
3782B9532755667600990149 /* String+Format.swift in Sources */,
|
||||||
37E2EEAC270656EC00170416 /* BrowserPlayerControls.swift in Sources */,
|
37E2EEAC270656EC00170416 /* BrowserPlayerControls.swift in Sources */,
|
||||||
37BF662027308884008CCFB0 /* DropFavoriteOutside.swift in Sources */,
|
37BF662027308884008CCFB0 /* DropFavoriteOutside.swift in Sources */,
|
||||||
|
3776ADD7287381240078EBC4 /* Captions.swift in Sources */,
|
||||||
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
||||||
374C0543272496E4009BDDBE /* AppDelegate.swift in Sources */,
|
374C0543272496E4009BDDBE /* AppDelegate.swift in Sources */,
|
||||||
373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||||
@ -3057,7 +3063,6 @@
|
|||||||
3774125627387D2300423605 /* Segment.swift in Sources */,
|
3774125627387D2300423605 /* Segment.swift in Sources */,
|
||||||
373C8FE7275B955100CB5936 /* CommentsPage.swift in Sources */,
|
373C8FE7275B955100CB5936 /* CommentsPage.swift in Sources */,
|
||||||
3774124D27387D2300423605 /* PlaylistsModel.swift in Sources */,
|
3774124D27387D2300423605 /* PlaylistsModel.swift in Sources */,
|
||||||
3774123427387CC100423605 /* InvidiousAPI.swift in Sources */,
|
|
||||||
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */,
|
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */,
|
||||||
3774125427387D2300423605 /* Store.swift in Sources */,
|
3774125427387D2300423605 /* Store.swift in Sources */,
|
||||||
3774125027387D2300423605 /* Video.swift in Sources */,
|
3774125027387D2300423605 /* Video.swift in Sources */,
|
||||||
@ -3119,6 +3124,7 @@
|
|||||||
371B7E632759706A00D21217 /* CommentsView.swift in Sources */,
|
371B7E632759706A00D21217 /* CommentsView.swift in Sources */,
|
||||||
37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||||
3782B95727557E6E00990149 /* SearchSuggestions.swift in Sources */,
|
3782B95727557E6E00990149 /* SearchSuggestions.swift in Sources */,
|
||||||
|
3776ADD8287381240078EBC4 /* Captions.swift in Sources */,
|
||||||
37F0F4EC286F397E00C06C2E /* SettingsModel.swift in Sources */,
|
37F0F4EC286F397E00C06C2E /* SettingsModel.swift in Sources */,
|
||||||
37BD07C92698FBDB003EBB87 /* ContentView.swift in Sources */,
|
37BD07C92698FBDB003EBB87 /* ContentView.swift in Sources */,
|
||||||
37BC50AA2778A84700510953 /* HistorySettings.swift in Sources */,
|
37BC50AA2778A84700510953 /* HistorySettings.swift in Sources */,
|
||||||
|
Loading…
Reference in New Issue
Block a user