diff --git a/DuckDuckGo/Statistics/PixelEvent.swift b/DuckDuckGo/Statistics/PixelEvent.swift index 0e861faa9e..ad24f14b1b 100644 --- a/DuckDuckGo/Statistics/PixelEvent.swift +++ b/DuckDuckGo/Statistics/PixelEvent.swift @@ -146,6 +146,17 @@ extension Pixel { case incrementalRolloutTest + // Duck Player + case duckPlayerDailyUniqueView + case duckPlayerViewFromYoutubeViaMainOverlay + case duckPlayerViewFromYoutubeViaHoverButton + case duckPlayerViewFromYoutubeAutomatic + case duckPlayerViewFromSERP + case duckPlayerViewFromOther + case duckPlayerSettingAlways + case duckPlayerSettingNever + case duckPlayerSettingBackToDefault + enum Debug { case assertionFailure(message: String, file: StaticString, line: UInt) @@ -404,6 +415,26 @@ extension Pixel.Event { case .incrementalRolloutTest: return "m_mac_netp_ev_incremental_rollout_test" + + case .duckPlayerDailyUniqueView: + return "m_mac_duck-player_daily-unique-view" + case .duckPlayerViewFromYoutubeViaMainOverlay: + return "m_mac_duck-player_view-from_youtube_main-overlay" + case .duckPlayerViewFromYoutubeViaHoverButton: + return "m_mac_duck-player_view-from_youtube_hover-button" + case .duckPlayerViewFromYoutubeAutomatic: + return "m_mac_duck-player_view-from_youtube_automatic" + case .duckPlayerViewFromSERP: + return "m_mac_duck-player_view-from_serp" + case .duckPlayerViewFromOther: + return "m_mac_duck-player_view-from_other" + case .duckPlayerSettingAlways: + return "m_mac_duck-player_setting_always" + case .duckPlayerSettingNever: + return "m_mac_duck-player_setting_never" + case .duckPlayerSettingBackToDefault: + return "m_mac_duck-player_setting_back-to-default" + } } diff --git a/DuckDuckGo/Statistics/PixelParameters.swift b/DuckDuckGo/Statistics/PixelParameters.swift index e495ab8053..b80257ca10 100644 --- a/DuckDuckGo/Statistics/PixelParameters.swift +++ b/DuckDuckGo/Statistics/PixelParameters.swift @@ -134,7 +134,16 @@ extension Pixel.Event { .userHasPinnedTab, .fireButtonFirstBurn, .fireButton, - .incrementalRolloutTest: + .incrementalRolloutTest, + .duckPlayerDailyUniqueView, + .duckPlayerViewFromYoutubeViaMainOverlay, + .duckPlayerViewFromYoutubeViaHoverButton, + .duckPlayerViewFromYoutubeAutomatic, + .duckPlayerViewFromSERP, + .duckPlayerViewFromOther, + .duckPlayerSettingAlways, + .duckPlayerSettingNever, + .duckPlayerSettingBackToDefault: return nil } } diff --git a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift index 6a72a60cbe..1711a0d75e 100644 --- a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift @@ -131,6 +131,11 @@ final class DuckPlayerTabExtension { extension DuckPlayerTabExtension: YoutubeOverlayUserScriptDelegate { func youtubeOverlayUserScriptDidRequestDuckPlayer(with url: URL, in webView: WKWebView) { + if duckPlayer.mode == .enabled { + Pixel.fire(.duckPlayerViewFromYoutubeAutomatic) + } else { + Pixel.fire(.duckPlayerViewFromYoutubeViaHoverButton) + } // to be standardised across the app let isRequestingNewTab = NSApp.isCommandPressed if isRequestingNewTab { @@ -206,6 +211,9 @@ extension DuckPlayerTabExtension: NavigationResponder { // Always allow loading Private Player URLs (local HTML) if navigationAction.url.isDuckPlayerScheme || navigationAction.url.isDuckPlayer { + if navigationAction.request.allHTTPHeaderFields?["Referer"] == URL.duckDuckGo.absoluteString { + Pixel.fire(.duckPlayerViewFromSERP) + } return .allow } @@ -276,6 +284,17 @@ extension DuckPlayerTabExtension: NavigationResponder { || (navigationAction.sourceFrame.url.isDuckPlayer && navigationAction.url.isYoutubeVideoRecommendation), let mainFrame = navigationAction.mainFrameTarget { + switch navigationAction.navigationType { + case .custom, .redirect(.server): + Pixel.fire(.duckPlayerViewFromOther) + case .other: + if navigationAction.request.allHTTPHeaderFields?["Referer"] == URL.duckDuckGo.absoluteString { + Pixel.fire(.duckPlayerViewFromSERP) + } + default: + break + } + return .redirect(mainFrame) { navigator in navigator.load(URLRequest(url: .duckPlayer(videoID, timestamp: timestamp))) } @@ -284,6 +303,15 @@ extension DuckPlayerTabExtension: NavigationResponder { return .next } + func didCommit(_ navigation: Navigation) { + guard duckPlayer.isAvailable, duckPlayer.mode != .disabled else { + return + } + if navigation.url.isDuckPlayer { + Pixel.fire(.duckPlayerDailyUniqueView, limitToOnceADay: true) + } + } + @MainActor func navigationDidFinish(_ navigation: Navigation) { setUpYoutubeScriptsIfNeeded(for: navigation.url) diff --git a/DuckDuckGo/YoutubePlayer/DuckPlayer.swift b/DuckDuckGo/YoutubePlayer/DuckPlayer.swift index 9a277f12e0..30567cd4c3 100644 --- a/DuckDuckGo/YoutubePlayer/DuckPlayer.swift +++ b/DuckDuckGo/YoutubePlayer/DuckPlayer.swift @@ -155,6 +155,18 @@ final class DuckPlayer { if isFeatureEnabled { modeCancellable = preferences.$duckPlayerMode .removeDuplicates() + .dropFirst(1) + .handleEvents(receiveOutput: { mode in + switch mode { + case .enabled: + Pixel.fire(.duckPlayerSettingAlways) + case .alwaysAsk: + Pixel.fire(.duckPlayerSettingBackToDefault) + case .disabled: + Pixel.fire(.duckPlayerSettingNever) + } + }) + .prepend(preferences.duckPlayerMode) .assign(to: \.mode, onWeaklyHeld: self) } else { modeCancellable = nil diff --git a/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift b/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift index e60c083ea8..19be7e87e6 100644 --- a/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift +++ b/DuckDuckGo/YoutubePlayer/YoutubeOverlayUserScript.swift @@ -111,6 +111,9 @@ extension YoutubeOverlayUserScript { let pixelName = parameters["pixelName"] as? String if pixelName == "play.use" || pixelName == "play.do_not_use" { duckPlayerPreferences.youtubeOverlayAnyButtonPressed = true + if pixelName == "play.use" { + Pixel.fire(.duckPlayerViewFromYoutubeViaMainOverlay) + } } // Temporary pixel for first time user uses Duck Player