mirror of
				https://github.com/yattee/yattee.git
				synced 2025-10-30 20:22:06 +00:00 
			
		
		
		
	Merge pull request #762 from stonerl/allow-username-and-password-in-url
Invidious: propper HTTP basic auth support
This commit is contained in:
		| @@ -10,11 +10,28 @@ struct AccountsBridge: Defaults.Bridge { | ||||
|             return nil | ||||
|         } | ||||
|  | ||||
|         // Parse the urlString to check for embedded username and password | ||||
|         var sanitizedUrlString = value.urlString | ||||
|         if var urlComponents = URLComponents(string: value.urlString) { | ||||
|             if let user = urlComponents.user, let password = urlComponents.password { | ||||
|                 // Sanitize the embedded username and password | ||||
|                 let sanitizedUser = user.addingPercentEncoding(withAllowedCharacters: .urlUserAllowed) ?? user | ||||
|                 let sanitizedPassword = password.addingPercentEncoding(withAllowedCharacters: .urlPasswordAllowed) ?? password | ||||
|  | ||||
|                 // Update the URL components with sanitized credentials | ||||
|                 urlComponents.user = sanitizedUser | ||||
|                 urlComponents.password = sanitizedPassword | ||||
|  | ||||
|                 // Reconstruct the sanitized URL | ||||
|                 sanitizedUrlString = urlComponents.string ?? value.urlString | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             "id": value.id, | ||||
|             "instanceID": value.instanceID ?? "", | ||||
|             "name": value.name, | ||||
|             "apiURL": value.urlString, | ||||
|             "apiURL": sanitizedUrlString, | ||||
|             "username": value.username, | ||||
|             "password": value.password ?? "" | ||||
|         ] | ||||
|   | ||||
| @@ -247,27 +247,27 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|     } | ||||
|  | ||||
|     func feed(_ page: Int?) -> Resource? { | ||||
|         resource(baseURL: account.url, path: "\(Self.basePath)/auth/feed") | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: "\(Self.basePath)/auth/feed") | ||||
|             .withParam("page", String(page ?? 1)) | ||||
|     } | ||||
|  | ||||
|     var feed: Resource? { | ||||
|         resource(baseURL: account.url, path: basePathAppending("auth/feed")) | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/feed")) | ||||
|     } | ||||
|  | ||||
|     var subscriptions: Resource? { | ||||
|         resource(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|     } | ||||
|  | ||||
|     func subscribe(_ channelID: String, onCompletion: @escaping () -> Void = {}) { | ||||
|         resource(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|             .child(channelID) | ||||
|             .request(.post) | ||||
|             .onCompletion { _ in onCompletion() } | ||||
|     } | ||||
|  | ||||
|     func unsubscribe(_ channelID: String, onCompletion: @escaping () -> Void) { | ||||
|         resource(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/subscriptions")) | ||||
|             .child(channelID) | ||||
|             .request(.delete) | ||||
|             .onCompletion { _ in onCompletion() } | ||||
| @@ -308,11 +308,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|             return nil | ||||
|         } | ||||
|  | ||||
|         return resource(baseURL: account.url, path: basePathAppending("auth/playlists")) | ||||
|         return resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/playlists")) | ||||
|     } | ||||
|  | ||||
|     func playlist(_ id: String) -> Resource? { | ||||
|         resource(baseURL: account.url, path: basePathAppending("auth/playlists/\(id)")) | ||||
|         resourceWithAuthCheck(baseURL: account.url, path: basePathAppending("auth/playlists/\(id)")) | ||||
|     } | ||||
|  | ||||
|     func playlistVideos(_ id: String) -> Resource? { | ||||
| @@ -445,6 +445,9 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|  | ||||
|         urlComponents.scheme = instanceURLComponents.scheme | ||||
|         urlComponents.host = instanceURLComponents.host | ||||
|         urlComponents.user = instanceURLComponents.user | ||||
|         urlComponents.password = instanceURLComponents.password | ||||
|         urlComponents.port = instanceURLComponents.port | ||||
|  | ||||
|         guard let url = urlComponents.url else { | ||||
|             return nil | ||||
| @@ -553,6 +556,30 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     // Determines if the request requires Basic Auth credentials to be removed | ||||
|     private func needsBasicAuthRemoval(for path: String) -> Bool { | ||||
|         return path.hasPrefix("\(Self.basePath)/auth/") | ||||
|     } | ||||
|  | ||||
|     // Creates a resource URL with consideration for removing Basic Auth credentials | ||||
|     private func createResourceURL(baseURL: URL, path: String) -> URL { | ||||
|         var resourceURL = baseURL | ||||
|  | ||||
|         // Remove Basic Auth credentials if required | ||||
|         if needsBasicAuthRemoval(for: path), var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) { | ||||
|             urlComponents.user = nil | ||||
|             urlComponents.password = nil | ||||
|             resourceURL = urlComponents.url ?? baseURL | ||||
|         } | ||||
|  | ||||
|         return resourceURL.appendingPathComponent(path) | ||||
|     } | ||||
|  | ||||
|     func resourceWithAuthCheck(baseURL: URL, path: String) -> Resource { | ||||
|         let sanitizedURL = createResourceURL(baseURL: baseURL, path: path) | ||||
|         return super.resource(absoluteURL: sanitizedURL) | ||||
|     } | ||||
|  | ||||
|     private func extractThumbnails(from details: JSON) -> [Thumbnail] { | ||||
|         details["videoThumbnails"].arrayValue.compactMap { json in | ||||
|             guard let url = json["url"].url, | ||||
| @@ -563,13 +590,20 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { | ||||
|                 return nil | ||||
|             } | ||||
|  | ||||
|             // some of instances are not configured properly and return thumbnails links | ||||
|             // with incorrect scheme | ||||
|             // Some instances are not configured properly and return thumbnail links | ||||
|             // with an incorrect scheme or a missing port. | ||||
|             components.scheme = accountUrlComponents.scheme | ||||
|             components.port = accountUrlComponents.port | ||||
|  | ||||
|             // If basic HTTP authentication is used, | ||||
|             // the username and password need to be prepended to the URL. | ||||
|             components.user = accountUrlComponents.user | ||||
|             components.password = accountUrlComponents.password | ||||
|  | ||||
|             guard let thumbnailUrl = components.url else { | ||||
|                 return nil | ||||
|             } | ||||
|             print("Final thumbnail URL: \(thumbnailUrl)") | ||||
|  | ||||
|             return Thumbnail(url: thumbnailUrl, quality: .init(rawValue: quality)!) | ||||
|         } | ||||
|   | ||||
| @@ -86,6 +86,7 @@ struct InstanceForm: View { | ||||
|                 .autocapitalization(.none) | ||||
|                 .keyboardType(.URL) | ||||
|             #endif | ||||
|                 .disableAutocorrection(true) | ||||
|  | ||||
|             #if os(tvOS) | ||||
|                 VStack { | ||||
|   | ||||
| @@ -66,6 +66,11 @@ | ||||
|             value = "Yes" | ||||
|             isEnabled = "YES"> | ||||
|          </EnvironmentVariable> | ||||
|          <EnvironmentVariable | ||||
|             key = "IDELogRedirectionPolicy" | ||||
|             value = "oslogToStdio" | ||||
|             isEnabled = "YES"> | ||||
|          </EnvironmentVariable> | ||||
|       </EnvironmentVariables> | ||||
|    </LaunchAction> | ||||
|    <ProfileAction | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Arkadiusz Fal
					Arkadiusz Fal