diff --git a/Extensions/URL+ByReplacingYatteeProtocol.swift b/Extensions/URL+ByReplacingYatteeProtocol.swift index 6f303810..60d1a9f3 100644 --- a/Extensions/URL+ByReplacingYatteeProtocol.swift +++ b/Extensions/URL+ByReplacingYatteeProtocol.swift @@ -4,11 +4,11 @@ extension URL { func byReplacingYatteeProtocol(with urlProtocol: String = "https") -> URL! { var urlAbsoluteString = absoluteString - guard urlAbsoluteString.hasPrefix(Constants.yatteeProtocol) else { + guard urlAbsoluteString.hasPrefix(Strings.yatteeProtocol) else { return self } - urlAbsoluteString = String(urlAbsoluteString.dropFirst(Constants.yatteeProtocol.count)) + urlAbsoluteString = String(urlAbsoluteString.dropFirst(Strings.yatteeProtocol.count)) if absoluteString.contains("://") { return URL(string: urlAbsoluteString) } diff --git a/Model/Applications/VideosAPI.swift b/Model/Applications/VideosAPI.swift index bf8fbc7b..31f8705c 100644 --- a/Model/Applications/VideosAPI.swift +++ b/Model/Applications/VideosAPI.swift @@ -66,7 +66,7 @@ protocol VideosAPI { failureHandler: ((RequestError) -> Void)?, completionHandler: @escaping (PlayerQueueItem) -> Void ) - func shareURL(_ item: ContentItem, frontendURL: String?, time: CMTime?) -> URL? + func shareURL(_ item: ContentItem, frontendURLString: String?, time: CMTime?) -> URL? func comments(_ id: Video.ID, page: String?) -> Resource? } @@ -113,7 +113,7 @@ extension VideosAPI { if let frontendURLString, let frontendURL = URL(string: frontendURLString) { - urlComponents = URLComponents(URL: frontendURL, resolvingAgainstBaseURL: false) + urlComponents = URLComponents(url: frontendURL, resolvingAgainstBaseURL: false) } else if let instanceComponents = account?.instance?.urlComponents { urlComponents = instanceComponents } diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index adeec484..2ec93a1c 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -625,30 +625,32 @@ final class MPVBackend: PlayerBackend { } } - @objc func handleAudioSessionInterruption(_ notification: Notification) { - logger.info("Audio session interruption received.") + #if !os(macOS) + @objc func handleAudioSessionInterruption(_ notification: Notification) { + logger.info("Audio session interruption received.") - guard let info = notification.userInfo, - let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt - else { - logger.info("AVAudioSessionInterruptionTypeKey is missing or not a UInt in userInfo.") - return + guard let info = notification.userInfo, + let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt + else { + logger.info("AVAudioSessionInterruptionTypeKey is missing or not a UInt in userInfo.") + return + } + + let type = AVAudioSession.InterruptionType(rawValue: typeValue) + + logger.info("Interruption type received: \(String(describing: type))") + + switch type { + case .began: + pause() + logger.info("Audio session interrupted.") + default: + break + } } - let type = AVAudioSession.InterruptionType(rawValue: typeValue) - - logger.info("Interruption type received: \(String(describing: type))") - - switch type { - case .began: - pause() - logger.info("Audio session interrupted.") - default: - break + deinit { + NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil) } - } - - deinit { - NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil) - } + #endif } diff --git a/Shared/Constants.swift b/Shared/Constants.swift index c77e83d4..fbecc16e 100644 --- a/Shared/Constants.swift +++ b/Shared/Constants.swift @@ -3,7 +3,6 @@ import Foundation import SwiftUI enum Constants { - static let yatteeProtocol = "yattee://" static let overlayAnimation = Animation.linear(duration: 0.2) static var isAppleTV: Bool { @@ -98,6 +97,20 @@ enum Constants { #endif } + static var defaultNavigationStyle: NavigationStyle { + #if os(macOS) + return .sidebar + #elseif os(iOS) + if isIPad { + return .sidebar + } else { + return .tab + } + #else + return .tab + #endif + } + static func seekIcon(_ type: String, _ interval: TimeInterval) -> String { let interval = Int(interval) let allVersions = [10, 15, 30, 45, 60, 75, 90] diff --git a/Shared/OpenURLHandler.swift b/Shared/OpenURLHandler.swift index 65a5ef71..32ba19c1 100644 --- a/Shared/OpenURLHandler.swift +++ b/Shared/OpenURLHandler.swift @@ -4,8 +4,6 @@ import Siesta struct OpenURLHandler { static var firstHandle = true - static let yatteeProtocol = "yattee://" - var accounts: AccountsModel { .shared } var navigation: NavigationModel { .shared } var recents: RecentsModel { .shared } diff --git a/Shared/Settings/SponsorBlockSettings.swift b/Shared/Settings/SponsorBlockSettings.swift index e285d5e1..f9253e0d 100644 --- a/Shared/Settings/SponsorBlockSettings.swift +++ b/Shared/Settings/SponsorBlockSettings.swift @@ -1,6 +1,8 @@ import Defaults import SwiftUI -import UIKit +#if canImport(UIKit) + import UIKit +#endif struct SponsorBlockSettings: View { @ObservedObject private var settings = SettingsModel.shared @@ -55,24 +57,27 @@ struct SponsorBlockSettings: View { Section(header: SettingsHeader(text: "Categories to Skip".localized())) { categoryRows } - colorSection - Button { - settings.presentAlert( - Alert( - title: Text("Restore Default Colors?"), - message: Text("This action will reset all custom colors back to their original defaults. " + - "Any custom color changes you've made will be lost."), - primaryButton: .destructive(Text("Restore")) { - resetColors() - }, - secondaryButton: .cancel() + #if os(iOS) + colorSection + + Button { + settings.presentAlert( + Alert( + title: Text("Restore Default Colors?"), + message: Text("This action will reset all custom colors back to their original defaults. " + + "Any custom color changes you've made will be lost."), + primaryButton: .destructive(Text("Restore")) { + resetColors() + }, + secondaryButton: .cancel() + ) ) - ) - } label: { - Text("Restore Default Colors …") - .foregroundColor(.red) - } + } label: { + Text("Restore Default Colors …") + .foregroundColor(.red) + } + #endif Section(footer: categoriesDetails) { EmptyView() @@ -80,21 +85,23 @@ struct SponsorBlockSettings: View { } } - private var colorSection: some View { - Section(header: SettingsHeader(text: "Colors for Categories")) { - ForEach(SponsorBlockAPI.categories, id: \.self) { category in - LazyVStack(alignment: .leading) { - ColorPicker( - SponsorBlockAPI.categoryDescription(category) ?? "Unknown", - selection: Binding( - get: { getColor(for: category) }, - set: { setColor($0, for: category) } + #if os(iOS) + private var colorSection: some View { + Section(header: SettingsHeader(text: "Colors for Categories")) { + ForEach(SponsorBlockAPI.categories, id: \.self) { category in + LazyVStack(alignment: .leading) { + ColorPicker( + SponsorBlockAPI.categoryDescription(category) ?? "Unknown", + selection: Binding( + get: { getColor(for: category) }, + set: { setColor($0, for: category) } + ) ) - ) + } } } } - } + #endif private var categoryRows: some View { ForEach(SponsorBlockAPI.categories, id: \.self) { category in @@ -145,25 +152,27 @@ struct SponsorBlockSettings: View { return Color("AppRedColor") // Fallback color if no match found } - private func setColor(_ color: Color, for category: String) { - let uiColor = UIColor(color) + #if canImport(UIKit) + private func setColor(_ color: Color, for category: String) { + let uiColor = UIColor(color) - // swiftlint:disable no_cgfloat - var red: CGFloat = 0 - var green: CGFloat = 0 - var blue: CGFloat = 0 - var alpha: CGFloat = 0 - // swiftlint:enable no_cgfloat + // swiftlint:disable no_cgfloat + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + // swiftlint:enable no_cgfloat - uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - let r = Int(red * 255.0) - let g = Int(green * 255.0) - let b = Int(blue * 255.0) + let r = Int(red * 255.0) + let g = Int(green * 255.0) + let b = Int(blue * 255.0) - let rgbValue = (r << 16) | (g << 8) | b - sponsorBlockColors[category] = String(format: "#%06x", rgbValue) - } + let rgbValue = (r << 16) | (g << 8) | b + sponsorBlockColors[category] = String(format: "#%06x", rgbValue) + } + #endif private func resetColors() { sponsorBlockColors = SponsorBlockColors.dictionary diff --git a/Shared/Strings.swift b/Shared/Strings.swift new file mode 100644 index 00000000..3f0f9cff --- /dev/null +++ b/Shared/Strings.swift @@ -0,0 +1,3 @@ +enum Strings { + static let yatteeProtocol = "yattee://" +} diff --git a/Shared/Views/ShareButton.swift b/Shared/Views/ShareButton.swift index 9aa716ff..c6f67e00 100644 --- a/Shared/Views/ShareButton.swift +++ b/Shared/Views/ShareButton.swift @@ -77,7 +77,7 @@ struct ShareButton: View { private var youtubeActions: some View { Group { - if let url = accounts.api.shareURL(contentItem, frontendURL: "https://www.youtube.com") { + if let url = accounts.api.shareURL(contentItem, frontendURLString: "https://www.youtube.com") { Button(labelForShareURL("YouTube")) { shareAction(url) } @@ -87,7 +87,7 @@ struct ShareButton: View { shareAction( accounts.api.shareURL( contentItem, - frontendURL: "https://www.youtube.com", + frontendURLString: "https://www.youtube.com", time: player.backend.currentTime )! ) diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 63f2a045..2ede606f 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -82,7 +82,6 @@ 3709528A29283E14001ECA40 /* NoDocumentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3709528929283E14001ECA40 /* NoDocumentsView.swift */; }; 37095E82291DC85400301883 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37095E81291DC85400301883 /* ShareViewController.swift */; }; 37095E89291DC85400301883 /* Open in Yattee.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 37095E7F291DC85400301883 /* Open in Yattee.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */; }; 370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79C8286279810045DB77 /* NSObject+Swizzle.swift */; }; 370B79CC286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370B79CB286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift */; }; 370E990A2A1EA8C500D144E9 /* WatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370E99092A1EA8C500D144E9 /* WatchModel.swift */; }; @@ -378,6 +377,12 @@ 3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; 3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; 3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; + 3762C46D2BF66CDD008E50B8 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; + 3762C4772BF66F04008E50B8 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3762C4762BF66F04008E50B8 /* Strings.swift */; }; + 3762C4782BF66F04008E50B8 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3762C4762BF66F04008E50B8 /* Strings.swift */; }; + 3762C4792BF66F04008E50B8 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3762C4762BF66F04008E50B8 /* Strings.swift */; }; + 3762C47A2BF66F04008E50B8 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3762C4762BF66F04008E50B8 /* Strings.swift */; }; + 3762C47D2BF66FF7008E50B8 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 3762C47C2BF66FF7008E50B8 /* Defaults */; }; 3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; }; 3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; }; 37635FE4291EA6CF00C11E79 /* AccentButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37635FE3291EA6CF00C11E79 /* AccentButton.swift */; }; @@ -1059,8 +1064,6 @@ 37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD76FF2932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift */; }; 37FD77012932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD76FF2932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift */; }; 37FD77022932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD76FF2932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift */; }; - 37FD77032932C5EC00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD76FF2932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift */; }; - 37FD77042932C5FC00D91A5F /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B01428B7F84D009717C8 /* Constants.swift */; }; 37FEF11327EFD8580033912F /* PlaceholderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FEF11227EFD8580033912F /* PlaceholderCell.swift */; }; 37FEF11427EFD8580033912F /* PlaceholderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FEF11227EFD8580033912F /* PlaceholderCell.swift */; }; 37FEF11527EFD8580033912F /* PlaceholderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FEF11227EFD8580033912F /* PlaceholderCell.swift */; }; @@ -1265,6 +1268,7 @@ 375EC971289F2ABF00751258 /* MultiselectRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiselectRow.swift; sourceTree = ""; }; 375F740F289DC35A00747050 /* PlayerBackendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerBackendView.swift; sourceTree = ""; }; 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = ""; }; + 3762C4762BF66F04008E50B8 /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecents.swift; sourceTree = ""; }; 37635FE3291EA6CF00C11E79 /* AccentButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentButton.swift; sourceTree = ""; }; 3763C988290C7A50004D3B5F /* OpenVideosView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenVideosView.swift; sourceTree = ""; }; @@ -1542,6 +1546,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3762C47D2BF66FF7008E50B8 /* Defaults in Frameworks */, 378CC2E32B669489006309F4 /* Logging in Frameworks */, 377F9F85294417FA0043F856 /* SwiftyJSON in Frameworks */, 377F9F83294417B40043F856 /* Cache in Frameworks */, @@ -2276,6 +2281,7 @@ 371AAE2726CEBF4700901972 /* Videos */, 371AAE2826CEC7D900901972 /* Views */, 3754B01428B7F84D009717C8 /* Constants.swift */, + 3762C4762BF66F04008E50B8 /* Strings.swift */, 375168D52700FAFF008F96A6 /* Debounce.swift */, 372915E52687E3B900F5A35B /* Defaults.swift */, 37D2E0D328B67EFC00F64D52 /* Delay.swift */, @@ -2501,6 +2507,7 @@ 377F9F82294417B40043F856 /* Cache */, 377F9F84294417FA0043F856 /* SwiftyJSON */, 378CC2E22B669489006309F4 /* Logging */, + 3762C47C2BF66FF7008E50B8 /* Defaults */, ); productName = "Open in Yattee"; productReference = 37095E7F291DC85400301883 /* Open in Yattee.appex */; @@ -3000,10 +3007,9 @@ buildActionMask = 2147483647; files = ( 3738535729451E0C00D2D0CB /* BookmarksCacheModel.swift in Sources */, - 37FD77042932C5FC00D91A5F /* Constants.swift in Sources */, + 3762C46D2BF66CDD008E50B8 /* EnvironmentValues.swift in Sources */, 37095E82291DC85400301883 /* ShareViewController.swift in Sources */, - 37FD77032932C5EC00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */, - 37095E8D291DD5DA00301883 /* URLBookmarkModel.swift in Sources */, + 3762C47A2BF66F04008E50B8 /* Strings.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3330,6 +3336,7 @@ 37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */, 37C3A241272359900087A57A /* Double+Format.swift in Sources */, 3784CDE227772EE40055BBF2 /* Watch.swift in Sources */, + 3762C4772BF66F04008E50B8 /* Strings.swift in Sources */, 37FB285E272225E800A57617 /* ContentItemView.swift in Sources */, 3797758B2689345500DD52A8 /* Store.swift in Sources */, 3773B80D2ADC076800B5FEF3 /* FramePreferenceKey.swift in Sources */, @@ -3401,6 +3408,7 @@ 37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */, 37E75CCC2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */, 3784CDE327772EE40055BBF2 /* Watch.swift in Sources */, + 3762C4782BF66F04008E50B8 /* Strings.swift in Sources */, 371AC0B7294D1D6E0085989E /* PlayingIndicatorView.swift in Sources */, 3773B8182ADC081300B5FEF3 /* VisualEffectBlur-macOS.swift in Sources */, 37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */, @@ -3826,6 +3834,7 @@ 37769250294630110055EC18 /* ChannelAvatarView.swift in Sources */, 37030FFD27B0398000ECDDAA /* MPVClient.swift in Sources */, 378E9C4229455A5800B2D696 /* ChannelsView.swift in Sources */, + 3762C4792BF66F04008E50B8 /* Strings.swift in Sources */, 37192D5928B179D60012EEDD /* ChaptersView.swift in Sources */, 37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */, 373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */, @@ -5110,6 +5119,11 @@ package = 375B8AAF28B57F4200397B31 /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; + 3762C47C2BF66FF7008E50B8 /* Defaults */ = { + isa = XCSwiftPackageProductDependency; + package = 372915E22687E33E00F5A35B /* XCRemoteSwiftPackageReference "Defaults" */; + productName = Defaults; + }; 3765917B27237D21009F956E /* PINCache */ = { isa = XCSwiftPackageProductDependency; package = 3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */; diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index cae4c934..c3247e72 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -22,7 +22,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { if url.scheme == "yattee" { - OpenURLHandler.handle(url) + OpenURLHandler(navigationStyle: Constants.defaultNavigationStyle).handle(url) return true } return false