diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index edf1f8d9e7..58947dc20f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -278,7 +278,6 @@ 3706FA97293F65D500E42796 /* WindowDraggingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693954526F04BEA0015B914 /* WindowDraggingView.swift */; }; 3706FA98293F65D500E42796 /* SecureVaultSorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E6EEB27AB5E5100F51793 /* SecureVaultSorting.swift */; }; 3706FA99293F65D500E42796 /* PreferencesSidebarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CD54C627F2FDD100F1F7B9 /* PreferencesSidebarModel.swift */; }; - 3706FA9A293F65D500E42796 /* DuckPlayerURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3767190128E724B2003A2A15 /* DuckPlayerURLExtension.swift */; }; 3706FA9D293F65D500E42796 /* PermissionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6106BAC26A7BF390013B453 /* PermissionState.swift */; }; 3706FA9F293F65D500E42796 /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; }; 3706FAA0293F65D500E42796 /* UserAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14505A07256084EF00272CC6 /* UserAgent.swift */; }; @@ -872,7 +871,6 @@ 3706FE64293F661700E42796 /* DownloadListStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693955C26F19CD70015B914 /* DownloadListStoreTests.swift */; }; 3706FE65293F661700E42796 /* ContentBlockingUpdatingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B610F2E527AA388100FCEBE9 /* ContentBlockingUpdatingTests.swift */; }; 3706FE67293F661700E42796 /* EncryptionMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1A6F5258C4F9600F6F690 /* EncryptionMocks.swift */; }; - 3706FE68293F661700E42796 /* DuckPlayerURLExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376718FF28E58513003A2A15 /* DuckPlayerURLExtensionTests.swift */; }; 3706FE6A293F661700E42796 /* FirefoxKeyReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2975982828285900187C4E /* FirefoxKeyReaderTests.swift */; }; 3706FE6B293F661700E42796 /* AppKitPrivateMethodsAvailabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B698E5032908011E00A746A8 /* AppKitPrivateMethodsAvailabilityTests.swift */; }; 3706FE6D293F661700E42796 /* ChromiumBookmarksReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB99D0C26FE1A83001E4761 /* ChromiumBookmarksReaderTests.swift */; }; @@ -1011,8 +1009,6 @@ 37534CA8281198CD002621E7 /* AdjacentItemEnumerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534CA62811988E002621E7 /* AdjacentItemEnumerator.swift */; }; 376113CC2B29CD5B00E794BB /* CriticalPathsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565E46DF2B2725DD0013AC2A /* CriticalPathsTests.swift */; }; 376705AF27EB488600DD8D76 /* RoundedSelectionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0511B3262CAA5A00F6079C /* RoundedSelectionRowView.swift */; }; - 3767190028E58513003A2A15 /* DuckPlayerURLExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376718FF28E58513003A2A15 /* DuckPlayerURLExtensionTests.swift */; }; - 3767190328E724D8003A2A15 /* DuckPlayerURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3767190128E724B2003A2A15 /* DuckPlayerURLExtension.swift */; }; 3768D8382C24BFF5004120AE /* RemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */; }; 3768D8392C24BFF5004120AE /* RemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */; }; 3768D83B2C24C0A8004120AE /* RemoteMessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D83A2C24C0A8004120AE /* RemoteMessageViewModel.swift */; }; @@ -2588,6 +2584,8 @@ CBDD5DE429A6800300832877 /* MockConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DE229A67F2700832877 /* MockConfigurationStore.swift */; }; D64A5FF82AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A5FF72AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift */; }; D64A5FF92AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A5FF72AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift */; }; + D6BC8AC62C5A95AA0025375B /* DuckPlayer in Frameworks */ = {isa = PBXBuildFile; productRef = D6BC8AC52C5A95AA0025375B /* DuckPlayer */; }; + D6BC8AC82C5A95B10025375B /* DuckPlayer in Frameworks */ = {isa = PBXBuildFile; productRef = D6BC8AC72C5A95B10025375B /* DuckPlayer */; }; EA0BA3A9272217E6002A0B6C /* ClickToLoadUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0BA3A8272217E6002A0B6C /* ClickToLoadUserScript.swift */; }; EA18D1CA272F0DC8006DC101 /* social_images in Resources */ = {isa = PBXBuildFile; fileRef = EA18D1C9272F0DC8006DC101 /* social_images */; }; EA8AE76A279FBDB20078943E /* ClickToLoadTDSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8AE769279FBDB20078943E /* ClickToLoadTDSTests.swift */; }; @@ -3105,8 +3103,6 @@ 376113C52B29BCD600E794BB /* SyncE2EUITests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SyncE2EUITests.xcconfig; sourceTree = ""; }; 376113D42B29CD5B00E794BB /* SyncE2EUITests App Store.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyncE2EUITests App Store.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 376113D72B29D0F800E794BB /* SyncE2EUITestsAppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SyncE2EUITestsAppStore.xcconfig; sourceTree = ""; }; - 376718FF28E58513003A2A15 /* DuckPlayerURLExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerURLExtensionTests.swift; sourceTree = ""; }; - 3767190128E724B2003A2A15 /* DuckPlayerURLExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerURLExtension.swift; sourceTree = ""; }; 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessageView.swift; sourceTree = ""; }; 3768D83A2C24C0A8004120AE /* RemoteMessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessageViewModel.swift; sourceTree = ""; }; 3768D83F2C29C1F1004120AE /* ActiveRemoteMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModel.swift; sourceTree = ""; }; @@ -4328,6 +4324,7 @@ 85E2BBD02B8F534A00DBEC7A /* History in Frameworks */, 4BF97AD52B43C43F00EB4240 /* NetworkProtection in Frameworks */, 3739326529AE4B39009346AE /* DDGSync in Frameworks */, + D6BC8AC82C5A95B10025375B /* DuckPlayer in Frameworks */, 37DF000729F9C061002B7D3E /* SyncDataProviders in Frameworks */, 37BA812F29B3CD6E0053F1A3 /* SyncUI in Frameworks */, 3706FCAF293F65D500E42796 /* PrivacyDashboard in Frameworks */, @@ -4505,6 +4502,7 @@ B6F7128129F681EB00594A45 /* QuickLookUI.framework in Frameworks */, EE7295E32A545B9A008C0991 /* NetworkProtection in Frameworks */, 9807F645278CA16F00E1547B /* BrowserServicesKit in Frameworks */, + D6BC8AC62C5A95AA0025375B /* DuckPlayer in Frameworks */, 987799ED299998B1005D8EB6 /* Bookmarks in Frameworks */, 1E950E3F2912A10D0051A99B /* ContentBlocking in Frameworks */, 31A3A4E32B0C115F0021063C /* DataBrokerProtection in Frameworks */, @@ -4849,7 +4847,6 @@ isa = PBXGroup; children = ( 37F19A6928E2F2D000740DC6 /* DuckPlayer.swift */, - 3767190128E724B2003A2A15 /* DuckPlayerURLExtension.swift */, 31F28C5228C8EECA00119F70 /* DuckURLSchemeHandler.swift */, 315AA06F28CA5CC800200030 /* YoutubePlayerNavigationHandler.swift */, 31F28C4C28C8EEC500119F70 /* YoutubePlayerUserScript.swift */, @@ -4917,7 +4914,6 @@ isa = PBXGroup; children = ( 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */, - 376718FF28E58513003A2A15 /* DuckPlayerURLExtensionTests.swift */, 567DA94429E95C3F008AC5EE /* YoutubeOverlayUserScriptTests.swift */, ); path = YoutubePlayer; @@ -8617,6 +8613,7 @@ F198C71F2BD18D92000BF24D /* SwiftLintTool */, 7B2366852C09FACD002D393F /* VPNAppLauncher */, 371209242C232E6C003ADF3D /* RemoteMessaging */, + D6BC8AC72C5A95B10025375B /* DuckPlayer */, ); productName = DuckDuckGo; productReference = 3706FD05293F65D500E42796 /* DuckDuckGo App Store.app */; @@ -9021,6 +9018,7 @@ F198C71D2BD18D88000BF24D /* SwiftLintTool */, 7B2366832C09FAC2002D393F /* VPNAppLauncher */, 371209222C232E66003ADF3D /* RemoteMessaging */, + D6BC8AC52C5A95AA0025375B /* DuckPlayer */, ); productName = DuckDuckGo; productReference = AA585D7E248FD31100E9A3E2 /* DuckDuckGo.app */; @@ -9856,7 +9854,6 @@ 3706FA98293F65D500E42796 /* SecureVaultSorting.swift in Sources */, 1DEDB3652C19934C006B6D1B /* MoreOptionsMenuButton.swift in Sources */, 3706FA99293F65D500E42796 /* PreferencesSidebarModel.swift in Sources */, - 3706FA9A293F65D500E42796 /* DuckPlayerURLExtension.swift in Sources */, 3706FA9D293F65D500E42796 /* PermissionState.swift in Sources */, 3707C724294B5D2900682A9F /* StringExtension.swift in Sources */, 3706FA9F293F65D500E42796 /* FeedbackPresenter.swift in Sources */, @@ -10880,7 +10877,6 @@ 3706FE65293F661700E42796 /* ContentBlockingUpdatingTests.swift in Sources */, 3706FE67293F661700E42796 /* EncryptionMocks.swift in Sources */, 9F872D9E2B9058D000138637 /* Bookmarks+TabTests.swift in Sources */, - 3706FE68293F661700E42796 /* DuckPlayerURLExtensionTests.swift in Sources */, 3706FE6A293F661700E42796 /* FirefoxKeyReaderTests.swift in Sources */, 1DA860732BE3AE950027B813 /* DockPositionProviderTests.swift in Sources */, 3706FE6B293F661700E42796 /* AppKitPrivateMethodsAvailabilityTests.swift in Sources */, @@ -11299,7 +11295,6 @@ B693955326F04BEC0015B914 /* WindowDraggingView.swift in Sources */, 4B1E6EED27AB5E5100F51793 /* SecureVaultSorting.swift in Sources */, 37CD54CE27F2FDD100F1F7B9 /* PreferencesSidebarModel.swift in Sources */, - 3767190328E724D8003A2A15 /* DuckPlayerURLExtension.swift in Sources */, 1D43EB32292788C70065E5D6 /* BWEncryptionOutput.m in Sources */, 3707EC4A2C47E36A00B67CBE /* CloseButton.swift in Sources */, B6106BAD26A7BF390013B453 /* PermissionState.swift in Sources */, @@ -12337,7 +12332,6 @@ B610F2EB27AA8E4500FCEBE9 /* ContentBlockingUpdatingTests.swift in Sources */, 4BA1A6F6258C4F9600F6F690 /* EncryptionMocks.swift in Sources */, 56A054162C1C37B0007D8FAB /* CapturingOnboardingNavigation.swift in Sources */, - 3767190028E58513003A2A15 /* DuckPlayerURLExtensionTests.swift in Sources */, 9FBD847A2BB3EC3300220859 /* MockAttributionOriginProvider.swift in Sources */, 4B2975992828285900187C4E /* FirefoxKeyReaderTests.swift in Sources */, B698E5042908011E00A746A8 /* AppKitPrivateMethodsAvailabilityTests.swift in Sources */, @@ -13376,7 +13370,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 177.0.2; + version = 178.0.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { @@ -13958,6 +13952,16 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + D6BC8AC52C5A95AA0025375B /* DuckPlayer */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = DuckPlayer; + }; + D6BC8AC72C5A95B10025375B /* DuckPlayer */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = DuckPlayer; + }; DC3F73D49B2D44464AFEFCD8 /* Subscription */ = { isa = XCSwiftPackageProductDependency; package = 3FFD51CF7C19ACBDB9687474 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 03fada48d8..2fa187fb00 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "ece50dc4a946de4d1d82c50a75a7dac101a7fb70", - "version" : "177.0.2" + "revision" : "ebad3db46e09913ac4c0e0c62085dcb40a6d6253", + "version" : "178.0.0" } }, { diff --git a/DuckDuckGo/Common/Extensions/URLExtension.swift b/DuckDuckGo/Common/Extensions/URLExtension.swift index 20658fc58c..3164a21591 100644 --- a/DuckDuckGo/Common/Extensions/URLExtension.swift +++ b/DuckDuckGo/Common/Extensions/URLExtension.swift @@ -23,7 +23,6 @@ import Foundation extension URL.NavigationalScheme { - static let duck = URL.NavigationalScheme(rawValue: "duck") static let javascript = URL.NavigationalScheme(rawValue: "javascript") static var validSchemes: [URL.NavigationalScheme] { diff --git a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift index 923e3edc78..a419a42dbe 100644 --- a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift @@ -22,6 +22,7 @@ import ContentBlocking import Foundation import Navigation import PixelKit +import DuckPlayer protocol YoutubeScriptsProvider { var youtubeOverlayScript: YoutubeOverlayUserScript? { get } diff --git a/DuckDuckGo/YoutubePlayer/DuckPlayer.swift b/DuckDuckGo/YoutubePlayer/DuckPlayer.swift index c657ea76e4..edfb674c87 100644 --- a/DuckDuckGo/YoutubePlayer/DuckPlayer.swift +++ b/DuckDuckGo/YoutubePlayer/DuckPlayer.swift @@ -53,7 +53,7 @@ enum DuckPlayerMode: Equatable, Codable { } /// Values that the Frontend can use to determine the current state. -struct InitialSetupSettings: Codable { +struct InitialPlayerSettings: Codable { struct PlayerSettings: Codable { let pip: PIP let autoplay: Autoplay @@ -63,6 +63,14 @@ struct InitialSetupSettings: Codable { let state: State } + struct Platform: Codable { + let name: String + } + + enum Locale: String, Codable { + case en + } + struct Autoplay: Codable { let state: State } @@ -72,8 +80,20 @@ struct InitialSetupSettings: Codable { case disabled } + enum Environment: String, Codable { + case development + case production + } + let userValues: UserValues let settings: PlayerSettings + let platform: Platform + let environment: Environment + let locale: Locale +} + +struct InitialOverlaySettings: Codable { + let userValues: UserValues } // Values that the YouTube Overlays can use to determine the current state @@ -199,9 +219,15 @@ final class DuckPlayer { encodeUserValues() } - public func initialSetup(with webView: WKWebView?) -> (_ params: Any, _ message: UserScriptMessage) async -> Encodable? { + public func initialPlayerSetup(with webView: WKWebView?) -> (_ params: Any, _ message: UserScriptMessage) async -> Encodable? { return { _, _ in - return await self.encodedSettings(with: webView) + return await self.encodedPlayerSettings(with: webView) + } + } + + public func initialOverlaySetup(with webView: WKWebView?) -> (_ params: Any, _ message: UserScriptMessage) async -> Encodable? { + return { _, _ in + return await self.encodedOverlaySettings(with: webView) } } @@ -213,7 +239,7 @@ final class DuckPlayer { } @MainActor - private func encodedSettings(with webView: WKWebView?) async -> InitialSetupSettings { + private func encodedPlayerSettings(with webView: WKWebView?) async -> InitialPlayerSettings { var isPiPEnabled = webView?.configuration.preferences[.allowsPictureInPictureMediaPlayback] == true var isAutoplayEnabled = DuckPlayerPreferences.shared.duckPlayerAutoplay @@ -230,13 +256,26 @@ final class DuckPlayer { isPiPEnabled = false } - let pip = InitialSetupSettings.PIP(state: isPiPEnabled ? .enabled : .disabled) - let autoplay = InitialSetupSettings.Autoplay(state: isAutoplayEnabled ? .enabled : .disabled) + let pip = InitialPlayerSettings.PIP(state: isPiPEnabled ? .enabled : .disabled) + let autoplay = InitialPlayerSettings.Autoplay(state: isAutoplayEnabled ? .enabled : .disabled) + let platform = InitialPlayerSettings.Platform(name: "ios") + let environment = InitialPlayerSettings.Environment.development + let locale = InitialPlayerSettings.Locale.en + let playerSettings = InitialPlayerSettings.PlayerSettings(pip: pip, autoplay: autoplay) + let userValues = encodeUserValues() + + return InitialPlayerSettings(userValues: userValues, + settings: playerSettings, + platform: platform, + environment: environment, + locale: locale) + } - let playerSettings = InitialSetupSettings.PlayerSettings(pip: pip, autoplay: autoplay) + @MainActor + private func encodedOverlaySettings(with webView: WKWebView?) async -> InitialOverlaySettings { let userValues = encodeUserValues() - return InitialSetupSettings(userValues: userValues, settings: playerSettings) + return InitialOverlaySettings(userValues: userValues) } // MARK: - Private diff --git a/DuckDuckGo/YoutubePlayer/DuckPlayerURLExtension.swift b/DuckDuckGo/YoutubePlayer/DuckPlayerURLExtension.swift deleted file mode 100644 index 585ad99b08..0000000000 --- a/DuckDuckGo/YoutubePlayer/DuckPlayerURLExtension.swift +++ /dev/null @@ -1,158 +0,0 @@ -// -// DuckPlayerURLExtension.swift -// -// Copyright © 2022 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -extension URL { - /** - * Returns the actual URL of the Private Player page. - * - * Depending on the use of simulated requests, it's either the custom scheme URL - * (without simulated requests, macOS <12), or youtube-nocookie.com URL (macOS 12 and newer). - */ - static func effectiveDuckPlayer(_ videoID: String, timestamp: String? = nil) -> URL { - if DuckPlayer.usesSimulatedRequests { - return .youtubeNoCookie(videoID, timestamp: timestamp) - } - return .duckPlayer(videoID, timestamp: timestamp) - } - - static func duckPlayer(_ videoID: String, timestamp: String? = nil) -> URL { - let url = "\(NavigationalScheme.duck.rawValue)://player/\(videoID)".url! - return url.addingTimestamp(timestamp) - } - - static func youtubeNoCookie(_ videoID: String, timestamp: String? = nil) -> URL { - let url = "https://www.youtube-nocookie.com/embed/\(videoID)".url! - return url.addingTimestamp(timestamp) - } - - static func youtube(_ videoID: String, timestamp: String? = nil) -> URL { - let url = "https://www.youtube.com/watch?v=\(videoID)".url! - return url.addingTimestamp(timestamp) - } - - /** - * Returns true if a URL represents a Private Player URL. - * - * It primarily checks for `duck://player/` URL, but on macOS 12 and above (when using simulated requests), - * the Duck Scheme URL is eventually replaced by `www.youtube-nocookie.com/embed/VIDEOID` URL so this - * is checked too and this function returns `true` if any of the two is true on macOS 12. - */ - var isDuckPlayer: Bool { - let isPrivatePlayer = isDuckURLScheme && host == DuckPlayer.duckPlayerHost - if DuckPlayer.usesSimulatedRequests { - return isPrivatePlayer || isYoutubeNoCookie - } else { - return isPrivatePlayer - } - } - - /// Returns true only if the URL represents a playlist itself, i.e. doesn't have `index` query parameter - var isYoutubePlaylist: Bool { - guard isYoutubeWatch, let components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { - return false - } - - let isPlaylistURL = components.queryItems?.contains(where: { $0.name == "list" }) == true && - components.queryItems?.contains(where: { $0.name == "v" }) == true && - components.queryItems?.contains(where: { $0.name == "index" }) == false - - return isPlaylistURL - } - - /// Returns true if the URL represents a YouTube video, but not the playlist (playlists are not supported by Private Player) - var isYoutubeVideo: Bool { - isYoutubeWatch && !isYoutubePlaylist - } - - /** - * Returns true if the URL represents a YouTube video recommendation. - * - * Recommendations are shown at the end of the embedded video or while it's paused. - */ - var isYoutubeVideoRecommendation: Bool { - guard isYoutubeVideo, - let components = URLComponents(url: self, resolvingAgainstBaseURL: false), - let featureQueryParameter = components.queryItems?.first(where: { $0.name == "feature" })?.value - else { - return false - } - - let recommendationFeatures = [ "emb_rel_end", "emb_rel_pause" ] - - return recommendationFeatures.contains(featureQueryParameter) - } - - /// Attempts extracting video ID and timestamp from the URL. Works with all types of YouTube URLs. - var youtubeVideoParams: (videoID: String, timestamp: String?)? { - if isDuckURLScheme { - guard let components = URLComponents(string: absoluteString) else { - return nil - } - let unsafeVideoID = components.path - let timestamp = components.queryItems?.first(where: { $0.name == "t" })?.value - return (unsafeVideoID.removingCharacters(in: .youtubeVideoIDNotAllowed), timestamp) - } - - if isDuckPlayer { - let unsafeVideoID = lastPathComponent - let timestamp = getParameter(named: "t") - return (unsafeVideoID.removingCharacters(in: .youtubeVideoIDNotAllowed), timestamp) - } - - guard isYoutubeVideo, - let components = URLComponents(url: self, resolvingAgainstBaseURL: false), - let unsafeVideoID = components.queryItems?.first(where: { $0.name == "v" })?.value - else { - return nil - } - - let timestamp = components.queryItems?.first(where: { $0.name == "t" })?.value - return (unsafeVideoID.removingCharacters(in: .youtubeVideoIDNotAllowed), timestamp) - } - - var youtubeVideoID: String? { - youtubeVideoParams?.videoID - } - - // MARK: - Private - - private var isYoutubeNoCookie: Bool { - host == "www.youtube-nocookie.com" && pathComponents.count == 3 && pathComponents[safe: 1] == "embed" - } - - private var isYoutubeWatch: Bool { - guard let host else { return false } - return host.contains("youtube.com") && path == "/watch" - } - - private func addingTimestamp(_ timestamp: String?) -> URL { - guard let timestamp = timestamp, - let regex = try? NSRegularExpression(pattern: "^(\\d+[smh]?)+$"), - timestamp.matches(regex) - else { - return self - } - return appendingParameter(name: "t", value: timestamp) - } -} - -extension CharacterSet { - static let youtubeVideoIDNotAllowed = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_").inverted -} diff --git a/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift b/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift index afe0e5a245..79ba756c2d 100644 --- a/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift +++ b/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift @@ -84,7 +84,7 @@ final class YoutubeOverlayUserScript: NSObject, Subfeature { case .getUserValues: return DuckPlayer.shared.handleGetUserValues case .initialSetup: - return handleInitialSetup + return DuckPlayer.shared.initialOverlaySetup(with: webView) case .openDuckPlayer: return handleOpenDuckPlayer case .sendDuckPlayerPixel: @@ -119,21 +119,6 @@ final class YoutubeOverlayUserScript: NSObject, Subfeature { return nil } - private func handleInitialSetup(params: Any, message: UserScriptMessage) -> Encodable? { - encodeInitialSetup() - } - - private func encodeInitialSetup() -> OverlaysInitialSettings { - let userValues = UserValues( - duckPlayerMode: duckPlayerPreferences.duckPlayerMode, - overlayInteracted: duckPlayerPreferences.youtubeOverlayInteracted - ) - - return OverlaysInitialSettings( - userValues: userValues - ) - } - // MARK: - UserValuesNotification struct UserValuesNotification: Encodable { diff --git a/DuckDuckGo/YoutubePlayer/YoutubePlayerNavigationHandler.swift b/DuckDuckGo/YoutubePlayer/YoutubePlayerNavigationHandler.swift index 6c62c9df63..8bdabb4e78 100644 --- a/DuckDuckGo/YoutubePlayer/YoutubePlayerNavigationHandler.swift +++ b/DuckDuckGo/YoutubePlayer/YoutubePlayerNavigationHandler.swift @@ -18,6 +18,7 @@ import Foundation import ContentScopeScripts +import DuckPlayer struct YoutubePlayerNavigationHandler { diff --git a/DuckDuckGo/YoutubePlayer/YoutubePlayerUserScript.swift b/DuckDuckGo/YoutubePlayer/YoutubePlayerUserScript.swift index 2a0af3883c..64ebf4c222 100644 --- a/DuckDuckGo/YoutubePlayer/YoutubePlayerUserScript.swift +++ b/DuckDuckGo/YoutubePlayer/YoutubePlayerUserScript.swift @@ -55,7 +55,7 @@ final class YoutubePlayerUserScript: NSObject, Subfeature { case .setUserValues: return DuckPlayer.shared.handleSetUserValuesMessage(from: .duckPlayer) case .initialSetup: - return DuckPlayer.shared.initialSetup(with: webView) + return DuckPlayer.shared.initialPlayerSetup(with: webView) default: assertionFailure("YoutubePlayerUserScript: Failed to parse User Script message: \(methodName)") return nil diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 2bca97612d..6785bcd498 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "177.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "178.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index ab6e52091c..bb1cbb7e17 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -32,7 +32,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "177.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "178.0.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index c23fe2a0fe..c87fc52740 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "177.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "178.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/UnitTests/YoutubePlayer/DuckPlayerURLExtensionTests.swift b/UnitTests/YoutubePlayer/DuckPlayerURLExtensionTests.swift deleted file mode 100644 index d4e004c0c6..0000000000 --- a/UnitTests/YoutubePlayer/DuckPlayerURLExtensionTests.swift +++ /dev/null @@ -1,142 +0,0 @@ -// -// DuckPlayerURLExtensionTests.swift -// -// Copyright © 2022 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -import os.log -@testable import DuckDuckGo_Privacy_Browser - -final class DuckPlayerURLExtensionTests: XCTestCase { - - func testIsDuckPlayerScheme() { - XCTAssertTrue("duck:player/abcdef12345".url!.isDuckURLScheme) - XCTAssertTrue("duck://player/abcdef12345".url!.isDuckURLScheme) - XCTAssertTrue("duck://player/abcdef".url!.isDuckURLScheme) - XCTAssertTrue("duck://player/12345".url!.isDuckURLScheme) - XCTAssertFalse("http://duckplayer/abcdef12345".url!.isDuckURLScheme) - XCTAssertFalse("https://www.youtube.com/watch?v=abcdef12345".url!.isDuckURLScheme) - XCTAssertFalse("https://www.youtube-nocookie.com/embed/abcdef12345".url!.isDuckURLScheme) - } - - func testIsDuckPlayer() { - XCTAssertTrue("https://www.youtube-nocookie.com/embed/abcdef12345".url!.isDuckPlayer) - XCTAssertTrue("https://www.youtube-nocookie.com/embed/abcdef12345?t=23s".url!.isDuckPlayer) - - XCTAssertFalse("https://www.youtube-nocookie.com/embed".url!.isDuckPlayer) - XCTAssertFalse("https://www.youtube-nocookie.com/embed?t=23s".url!.isDuckPlayer) - - XCTAssertTrue("duck://player/abcdef12345".url!.isDuckPlayer) - XCTAssertFalse("https://www.youtube.com/watch?v=abcdef12345".url!.isDuckPlayer) - XCTAssertFalse("https://duckduckgo.com".url!.isDuckPlayer) - } - - func testIsYoutubePlaylist() { - XCTAssertTrue("https://www.youtube.com/watch?v=abcdef12345&list=abcdefgh12345678".url!.isYoutubePlaylist) - XCTAssertTrue("https://www.youtube.com/watch?list=abcdefgh12345678&v=abcdef12345".url!.isYoutubePlaylist) - - XCTAssertFalse("https://duckduckgo.com/watch?v=abcdef12345&list=abcdefgh12345678".url!.isYoutubePlaylist) - XCTAssertFalse("https://www.youtube.com/watch?list=abcdefgh12345678".url!.isYoutubePlaylist) - XCTAssertFalse("https://www.youtube.com/watch?v=abcdef12345&list=abcdefgh12345678&index=1".url!.isYoutubePlaylist) - } - - func testIsYoutubeVideo() { - XCTAssertTrue("https://www.youtube.com/watch?v=abcdef12345".url!.isYoutubeVideo) - XCTAssertTrue("https://www.youtube.com/watch?v=abcdef12345&list=abcdefgh12345678&index=1".url!.isYoutubeVideo) - XCTAssertTrue("https://www.youtube.com/watch?v=abcdef12345&t=5m".url!.isYoutubeVideo) - - XCTAssertFalse("https://www.youtube.com/watch?v=abcdef12345&list=abcdefgh12345678".url!.isYoutubeVideo) - XCTAssertFalse("https://duckduckgo.com/watch?v=abcdef12345".url!.isYoutubeVideo) - } - - func testYoutubeVideoParamsFromDuckPlayerURL() { - let params = "duck://player/abcdef12345".url!.youtubeVideoParams - XCTAssertEqual(params?.videoID, "abcdef12345") - XCTAssertEqual(params?.timestamp, nil) - - let paramsWithTimestamp = "duck://player/abcdef12345?t=23s".url!.youtubeVideoParams - XCTAssertEqual(paramsWithTimestamp?.videoID, "abcdef12345") - XCTAssertEqual(paramsWithTimestamp?.timestamp, "23s") - } - - func testYoutubeVideoParamsFromYoutubeURL() { - let params = "https://www.youtube.com/watch?v=abcdef12345".url!.youtubeVideoParams - XCTAssertEqual(params?.videoID, "abcdef12345") - XCTAssertEqual(params?.timestamp, nil) - - let paramsWithTimestamp = "https://www.youtube.com/watch?v=abcdef12345&t=23s".url!.youtubeVideoParams - XCTAssertEqual(paramsWithTimestamp?.videoID, "abcdef12345") - XCTAssertEqual(paramsWithTimestamp?.timestamp, "23s") - - let paramsWithTimestampWithoutUnits = "https://www.youtube.com/watch?t=102&v=abcdef12345&feature=youtu.be".url!.youtubeVideoParams - XCTAssertEqual(paramsWithTimestampWithoutUnits?.videoID, "abcdef12345") - XCTAssertEqual(paramsWithTimestampWithoutUnits?.timestamp, "102") - } - - func testYoutubeVideoParamsFromYoutubeNocookieURL() { - let params = "https://www.youtube-nocookie.com/embed/abcdef12345".url!.youtubeVideoParams - XCTAssertEqual(params?.videoID, "abcdef12345") - XCTAssertEqual(params?.timestamp, nil) - - let paramsWithTimestamp = "https://www.youtube-nocookie.com/embed/abcdef12345?t=23s".url!.youtubeVideoParams - XCTAssertEqual(paramsWithTimestamp?.videoID, "abcdef12345") - XCTAssertEqual(paramsWithTimestamp?.timestamp, "23s") - } - - func testYoutubeVideoID() { - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: nil).youtubeVideoID, "abcdef12345") - XCTAssertEqual(URL.youtube("abcd
ef12345", timestamp: nil).youtubeVideoID, "abcdbref12345") - - XCTAssertNil("https://duck.com".url?.youtubeVideoID) - } - - func testDuckPlayerURLTimestampValidation() { - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: nil).absoluteString, "duck://player/abcdef12345") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "23s").absoluteString, "duck://player/abcdef12345?t=23s") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "5m5s").absoluteString, "duck://player/abcdef12345?t=5m5s") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "12h400m100s").absoluteString, "duck://player/abcdef12345?t=12h400m100s") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "12h2s2h").absoluteString, "duck://player/abcdef12345?t=12h2s2h") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "5m5m5m").absoluteString, "duck://player/abcdef12345?t=5m5m5m") - - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "5").absoluteString, "duck://player/abcdef12345?t=5") - XCTAssertEqual(URL.duckPlayer("abcdef12345", timestamp: "10d").absoluteString, "duck://player/abcdef12345") - } - - func testYoutubeURLTimestampValidation() { - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: nil).absoluteString, "https://www.youtube.com/watch?v=abcdef12345") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "23s").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=23s") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "5m5s").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=5m5s") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "12h400m100s").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=12h400m100s") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "12h2s2h").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=12h2s2h") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "5m5m5m").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=5m5m5m") - - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "5").absoluteString, "https://www.youtube.com/watch?v=abcdef12345&t=5") - XCTAssertEqual(URL.youtube("abcdef12345", timestamp: "10d").absoluteString, "https://www.youtube.com/watch?v=abcdef12345") - } - - func testYoutubeNoCookieURLTimestampValidation() { - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: nil).absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "23s").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=23s") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "5m5s").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=5m5s") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "12h400m100s").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=12h400m100s") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "12h2s2h").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=12h2s2h") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "5m5m5m").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=5m5m5m") - - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "5").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345?t=5") - XCTAssertEqual(URL.youtubeNoCookie("abcdef12345", timestamp: "10d").absoluteString, "https://www.youtube-nocookie.com/embed/abcdef12345") - } - -}