mirror of
				https://github.com/yattee/yattee.git
				synced 2025-10-31 04:31:54 +00:00 
			
		
		
		
	Merge pull request #863 from lifo9/add-support-for-invidious-companion
Add support for invidious companion
This commit is contained in:
		| @@ -10,14 +10,16 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable { | ||||
|     let apiURLString: String | ||||
|     var frontendURL: String? | ||||
|     var proxiesVideos: Bool | ||||
|     var invidiousCompanion: Bool | ||||
|  | ||||
|     init(app: VideosApp, id: String? = nil, name: String? = nil, apiURLString: String, frontendURL: String? = nil, proxiesVideos: Bool = false) { | ||||
|     init(app: VideosApp, id: String? = nil, name: String? = nil, apiURLString: String, frontendURL: String? = nil, proxiesVideos: Bool = false, invidiousCompanion: Bool = false) { | ||||
|         self.app = app | ||||
|         self.id = id ?? UUID().uuidString | ||||
|         self.name = name ?? app.rawValue | ||||
|         self.apiURLString = apiURLString | ||||
|         self.frontendURL = frontendURL | ||||
|         self.proxiesVideos = proxiesVideos | ||||
|         self.invidiousCompanion = invidiousCompanion | ||||
|     } | ||||
|  | ||||
|     var apiURL: URL! { | ||||
|   | ||||
| @@ -16,7 +16,8 @@ struct InstancesBridge: Defaults.Bridge { | ||||
|             "name": value.name, | ||||
|             "apiURL": value.apiURLString, | ||||
|             "frontendURL": value.frontendURL ?? "", | ||||
|             "proxiesVideos": value.proxiesVideos ? "true" : "false" | ||||
|             "proxiesVideos": value.proxiesVideos ? "true" : "false", | ||||
|             "invidiousCompanion": value.invidiousCompanion ? "true" : "false" | ||||
|         ] | ||||
|     } | ||||
|  | ||||
| @@ -33,7 +34,8 @@ struct InstancesBridge: Defaults.Bridge { | ||||
|         let name = object["name"] ?? "" | ||||
|         let frontendURL: String? = object["frontendURL"]!.isEmpty ? nil : object["frontendURL"] | ||||
|         let proxiesVideos = object["proxiesVideos"] == "true" | ||||
|         let invidiousCompanion = object["invidiousCompanion"] == "true" | ||||
|  | ||||
|         return Instance(app: app, id: id, name: name, apiURLString: apiURL, frontendURL: frontendURL, proxiesVideos: proxiesVideos) | ||||
|         return Instance(app: app, id: id, name: name, apiURLString: apiURL, frontendURL: frontendURL, proxiesVideos: proxiesVideos, invidiousCompanion: invidiousCompanion) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -79,6 +79,17 @@ final class InstancesModel: ObservableObject { | ||||
|         Defaults[.instances][index] = instance | ||||
|     } | ||||
|  | ||||
|     func setInvidiousCompanion(_ instance: Instance, _ invidiousCompanion: Bool) { | ||||
|         guard let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) else { | ||||
|             return | ||||
|         } | ||||
|  | ||||
|         var instance = Defaults[.instances][index] | ||||
|         instance.invidiousCompanion = invidiousCompanion | ||||
|  | ||||
|         Defaults[.instances][index] = instance | ||||
|     } | ||||
|  | ||||
|     func remove(_ instance: Instance) { | ||||
|         let accounts = accounts(instance.id) | ||||
|         if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) { | ||||
|   | ||||
| @@ -655,21 +655,29 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|         if json["liveNow"].boolValue { | ||||
|             return hls | ||||
|         } | ||||
|         let videoId = json["videoId"].stringValue | ||||
|  | ||||
|         return extractFormatStreams(from: json["formatStreams"].arrayValue) + | ||||
|             extractAdaptiveFormats(from: json["adaptiveFormats"].arrayValue) + | ||||
|         return extractFormatStreams(from: json["formatStreams"].arrayValue, videoId: videoId) + | ||||
|         extractAdaptiveFormats(from: json["adaptiveFormats"].arrayValue, videoId: videoId) + | ||||
|             hls | ||||
|     } | ||||
|  | ||||
|     private func extractFormatStreams(from streams: [JSON]) -> [Stream] { | ||||
|     private func extractFormatStreams(from streams: [JSON], videoId: String?) -> [Stream] { | ||||
|         streams.compactMap { stream in | ||||
|             guard let streamURL = stream["url"].url else { | ||||
|                 return nil | ||||
|             } | ||||
|             let finalURL: URL | ||||
|             if let videoId, let itag = stream["itag"].string, account.instance.invidiousCompanion { | ||||
|                 let companionURLString = "\(account.instance.apiURLString)/latest_version?id=\(videoId)&itag=\(itag)" | ||||
|                 finalURL = URL(string: companionURLString) ?? streamURL | ||||
|             } else { | ||||
|                 finalURL = streamURL | ||||
|             } | ||||
|  | ||||
|             return SingleAssetStream( | ||||
|                 instance: account.instance, | ||||
|                 avAsset: AVURLAsset(url: streamURL), | ||||
|                 avAsset: AVURLAsset(url: finalURL), | ||||
|                 resolution: Stream.Resolution.from(resolution: stream["resolution"].string ?? ""), | ||||
|                 kind: .stream, | ||||
|                 encoding: stream["encoding"].string ?? "" | ||||
| @@ -677,7 +685,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private func extractAdaptiveFormats(from streams: [JSON]) -> [Stream] { | ||||
|     private func extractAdaptiveFormats(from streams: [JSON], videoId: String?) -> [Stream] { | ||||
|         let audioStreams = streams | ||||
|             .filter { $0["type"].stringValue.starts(with: "audio/mp4") } | ||||
|             .sorted { | ||||
| @@ -692,15 +700,29 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|  | ||||
|         return videoStreams.compactMap { videoStream in | ||||
|             guard let audioAssetURL = audioStream["url"].url, | ||||
|                   let videoAssetURL = videoStream["url"].url | ||||
|                   let videoAssetURL = videoStream["url"].url, | ||||
|                   let audioItag = audioStream["itag"].string, | ||||
|                   let videoItag = videoStream["itag"].string | ||||
|             else { | ||||
|                 return nil | ||||
|             } | ||||
|             let finalAudioURL: URL | ||||
|             let finalVideoURL: URL | ||||
|  | ||||
|             if let videoId, account.instance.invidiousCompanion { | ||||
|                 let audioCompanionURLString = "\(account.instance.apiURLString)/latest_version?id=\(videoId)&itag=\(audioItag)" | ||||
|                 let videoCompanionURLString = "\(account.instance.apiURLString)/latest_version?id=\(videoId)&itag=\(videoItag)" | ||||
|                 finalAudioURL = URL(string: audioCompanionURLString) ?? audioAssetURL | ||||
|                 finalVideoURL = URL(string: videoCompanionURLString) ?? videoAssetURL | ||||
|             } else { | ||||
|                 finalAudioURL = audioAssetURL | ||||
|                 finalVideoURL = videoAssetURL | ||||
|             } | ||||
|  | ||||
|             return Stream( | ||||
|                 instance: account.instance, | ||||
|                 audioAsset: AVURLAsset(url: audioAssetURL), | ||||
|                 videoAsset: AVURLAsset(url: videoAssetURL), | ||||
|                 audioAsset: AVURLAsset(url: finalAudioURL), | ||||
|                 videoAsset: AVURLAsset(url: finalVideoURL), | ||||
|                 resolution: Stream.Resolution.from(resolution: videoStream["resolution"].stringValue), | ||||
|                 kind: .adaptive, | ||||
|                 encoding: videoStream["encoding"].string, | ||||
| @@ -711,6 +733,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private func extractHLSStreams(from content: JSON) -> [Stream] { | ||||
|         if let hlsURL = content.dictionaryValue["hlsUrl"]?.url { | ||||
|             return [Stream(instance: account.instance, hlsURL: hlsURL)] | ||||
|   | ||||
| @@ -8,6 +8,7 @@ struct InstanceSettings: View { | ||||
|  | ||||
|     @State private var frontendURL = "" | ||||
|     @State private var proxiesVideos = false | ||||
|     @State private var invidiousCompanion = false | ||||
|  | ||||
|     var body: some View { | ||||
|         List { | ||||
| @@ -87,6 +88,16 @@ struct InstanceSettings: View { | ||||
|                         InstancesModel.shared.setProxiesVideos(instance, newValue) | ||||
|                     } | ||||
|             } | ||||
|  | ||||
|             if instance.app == .invidious { | ||||
|                 invidiousCompanionToggle | ||||
|                     .onAppear { | ||||
|                         invidiousCompanion = instance.invidiousCompanion | ||||
|                     } | ||||
|                     .onChange(of: invidiousCompanion) { newValue in | ||||
|                         InstancesModel.shared.setInvidiousCompanion(instance, newValue) | ||||
|                     } | ||||
|             } | ||||
|         } | ||||
|         #if os(tvOS) | ||||
|         .frame(maxWidth: 1000) | ||||
| @@ -100,6 +111,10 @@ struct InstanceSettings: View { | ||||
|     private var proxiesVideosToggle: some View { | ||||
|         Toggle("Proxy videos", isOn: $proxiesVideos) | ||||
|     } | ||||
|      | ||||
|     private var invidiousCompanionToggle: some View { | ||||
|         Toggle("Invidious companion", isOn: $invidiousCompanion) | ||||
|     } | ||||
|  | ||||
|     private func removeAccount(_ account: Account) { | ||||
|         AccountsModel.remove(account) | ||||
|   | ||||
| @@ -11,6 +11,7 @@ struct InstancesSettings: View { | ||||
|  | ||||
|     @State private var frontendURL = "" | ||||
|     @State private var proxiesVideos = false | ||||
|     @State private var invidiousCompanion = false | ||||
|  | ||||
|     @Environment(\.colorScheme) private var colorScheme | ||||
|     @ObservedObject private var accounts = AccountsModel.shared | ||||
| @@ -105,6 +106,16 @@ struct InstancesSettings: View { | ||||
|                     } | ||||
|             } | ||||
|  | ||||
|             if selectedInstance != nil, selectedInstance.app == .invidious { | ||||
|                 invidiousCompanionToggle | ||||
|                     .onAppear { | ||||
|                         invidiousCompanion = selectedInstance.invidiousCompanion | ||||
|                     } | ||||
|                     .onChange(of: invidiousCompanion) { newValue in | ||||
|                         InstancesModel.shared.setInvidiousCompanion(selectedInstance, newValue) | ||||
|                     } | ||||
|             } | ||||
|  | ||||
|             if selectedInstance != nil, !selectedInstance.app.supportsAccounts { | ||||
|                 Spacer() | ||||
|                 Text("Accounts are not supported for the application of this instance") | ||||
| @@ -191,6 +202,10 @@ struct InstancesSettings: View { | ||||
|     private var proxiesVideosToggle: some View { | ||||
|         Toggle("Proxy videos", isOn: $proxiesVideos) | ||||
|     } | ||||
|  | ||||
|     private var invidiousCompanionToggle: some View { | ||||
|         Toggle("Invidious companion", isOn: $invidiousCompanion) | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct InstancesSettingsView_Previews: PreviewProvider { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Arkadiusz Fal
					Arkadiusz Fal