diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 93a652b7e..48a3deb0f 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,2 @@
-- Windows support for Malicious website protection (#1423)
-- history: adding item selection (#1486)
-- Duck Player screenshot update (#1489)
\ No newline at end of file
+- Duck Player Custom Error Translations (#1500)
+- Custom error messages for Duck Player (#1421)
\ No newline at end of file
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.css b/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.css
index cff98b790..7e8fbf3a0 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.css
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.css
@@ -61,6 +61,7 @@ body[data-display=app] {
/* pages/duckplayer/app/components/Components.module.css */
.Components_main {
+ background-color: #000;
color: white;
max-width: 3840px;
margin: 0 auto;
@@ -112,7 +113,7 @@ body[data-display=app] {
text-decoration: none;
}
[data-layout=mobile] .Button_button {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
.Button_button:hover,
.Button_button:focus-visible {
@@ -188,7 +189,7 @@ body[data-display=app] {
.SwitchBarMobile_switchBar {
display: grid;
border-radius: 8px;
- background: #2f2f2f;
+ background: rgba(255, 255, 255, 0.12);
padding-inline: 16px;
height: 100%;
line-height: 1.1;
@@ -518,6 +519,9 @@ body[data-display=app] {
.Wordmark_mobile_logo {
height: 100px;
}
+ [data-youtube-error=true] .Wordmark_mobile_logo {
+ height: 44px;
+ }
}
.Wordmark_mobile_logoSvg img {
display: block;
@@ -589,6 +593,127 @@ body[data-display=app] {
}
}
+/* pages/duckplayer/app/components/YouTubeError.module.css */
+.YouTubeError_error {
+ align-items: center;
+ background: rgba(0, 0, 0, 0.6);
+ display: grid;
+ height: 100%;
+ justify-items: center;
+}
+.YouTubeError_error.YouTubeError_desktop {
+ height: var(--frame-height);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+.YouTubeError_error.YouTubeError_mobile {
+ border-radius: var(--inner-radius);
+ height: 100%;
+ overflow: auto;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_error.YouTubeError_mobile {
+ aspect-ratio: 16 / 9;
+ }
+}
+.YouTubeError_desktop {
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.YouTubeError_container {
+ column-gap: 24px;
+ display: flex;
+ flex-flow: row;
+ margin: 0;
+ max-width: 680px;
+ padding: 0 40px;
+ row-gap: 4px;
+}
+.YouTubeError_mobile .YouTubeError_container {
+ flex-flow: column;
+ padding: 0 24px;
+}
+@media screen and (min-height: 320px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 16px 0;
+ }
+}
+@media screen and (min-width: 375px) and (min-height: 400px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 36px 0;
+ }
+}
+.YouTubeError_content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 16px 0;
+}
+@media screen and (min-width: 600px) {
+ .YouTubeError_content {
+ margin: 24px 0;
+ }
+}
+.YouTubeError_icon {
+ align-self: center;
+ display: flex;
+ justify-content: center;
+}
+.YouTubeError_icon::before {
+ content: " ";
+ display: block;
+ background: url('data:image/svg+xml,%0A') no-repeat;
+ height: 48px;
+ width: 48px;
+}
+@media screen and (max-width: 320px) {
+ .YouTubeError_icon {
+ display: none;
+ }
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_icon {
+ justify-content: start;
+ }
+ .YouTubeError_icon::before {
+ background-image: url('data:image/svg+xml,%0A');
+ height: 96px;
+ width: 128px;
+ }
+}
+.YouTubeError_heading {
+ color: #fff;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: calc(24 / 20);
+ margin: 0;
+}
+.YouTubeError_messages {
+ color: #ccc;
+ font-size: 16px;
+ line-height: calc(24 / 16);
+}
+div.YouTubeError_messages {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+div.YouTubeError_messages p {
+ margin: 0;
+}
+p.YouTubeError_messages {
+ margin: 0;
+}
+ul.YouTubeError_messages li {
+ list-style: disc;
+ margin-left: 24px;
+}
+
/* pages/duckplayer/app/components/MobileApp.module.css */
body[data-display=app] {
padding: 8px;
@@ -626,6 +751,7 @@ html[data-focus-mode=on] .MobileApp_hideInFocus {
--inner-radius: 12px;
--logo-width: 157px;
--inner-padding: 8px;
+ --mobile-buttons-padding: 8px;
position: relative;
max-width: 100vh;
margin: 0 auto;
@@ -681,6 +807,15 @@ html[data-focus-mode=on] .MobileApp_embed {
grid-area: switch;
height: 44px;
}
+.MobileApp_detachedControls {
+ grid-area: detached;
+ display: flex;
+ flex-flow: column;
+ gap: 8px;
+ padding: 8px;
+ background: #2f2f2f;
+ border-radius: 12px;
+}
@media screen and (min-width: 425px) and (max-height: 600px) {
.MobileApp_main {
grid-template-rows: max-content auto max-content max-content 12px max-content auto;
@@ -774,6 +909,71 @@ html[data-focus-mode=on] .MobileApp_embed {
justify-content: end;
}
}
+@media screen and (max-width: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ --bg-color: transparent;
+ --inner-padding: 4px;
+ grid-template-areas: "logo" "gap3" "embed" "gap4" "switch" "buttons";
+ grid-template-rows: max-content 16px auto 12px max-content max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ background: #2f2f2f;
+ border-radius: var(--outer-radius);
+ padding: 4px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_switch {
+ background: #2f2f2f;
+ padding: 8px 8px 0 8px;
+ height: 60px;
+ max-height: 60px;
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ background: #2f2f2f;
+ padding: 8px;
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_switch {
+ background: transparent;
+ max-height: 0;
+ }
+}
+@media screen and (max-width: 599px) and (max-height: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ max-width: unset;
+ grid-template-rows: 0 0 auto 12px 0 max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] :is(.MobileApp_logo, .MobileApp_switch) {
+ display: none;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+}
+@media screen and (min-width: 600px) and (max-height: 450px) {
+ .MobileApp_main[data-youtube-error=true] {
+ grid-template-areas: "embed" "buttons" "gap5";
+ grid-template-rows: auto max-content 8px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ display: block;
+ }
+}
+@media screen and (max-height: 320px) {
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ overflow-y: auto;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ bottom: 0;
+ position: sticky;
+ }
+}
/* pages/duckplayer/app/components/MobileButtons.module.css */
.MobileButtons_buttons {
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.js b/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.js
index 2e8aed055..2f5e4faad 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.js
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/dist/index.js
@@ -1982,12 +1982,33 @@
title: "ERROR: Invalid video id",
note: "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ blockedVideoErrorHeading: {
+ title: "YouTube won\u2019t let Duck Player load this video",
+ note: "Message shown when YouTube has blocked playback of a video"
+ },
+ blockedVideoErrorMessage1: {
+ title: "YouTube doesn\u2019t allow this video to be viewed outside of YouTube.",
+ note: "Explanation on why the error is happening."
+ },
+ blockedVideoErrorMessage2: {
+ title: "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ signInRequiredErrorMessage1: {
+ title: "YouTube is blocking this video from loading. If you\u2019re using a VPN, try turning it off and reloading this page.",
+ note: "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ signInRequiredErrorMessage2: {
+ title: "If this doesn\u2019t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "More troubleshooting tips for this specific error"
+ },
tooltipInfo: {
title: "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
};
// pages/duckplayer/app/settings.js
+ var DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
var Settings = class _Settings {
/**
* @param {object} params
@@ -1995,17 +2016,20 @@
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
+ * @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
*/
constructor({
platform = { name: "macos" },
pip = { state: "disabled" },
autoplay = { state: "enabled" },
- focusMode = { state: "enabled" }
+ focusMode = { state: "enabled" },
+ customError = { state: "disabled", signInRequiredSelector: "" }
}) {
this.platform = platform;
this.pip = pip;
this.autoplay = autoplay;
this.focusMode = focusMode;
+ this.customError = customError;
}
/**
* @param {keyof import("../types/duckplayer.js").DuckPlayerPageSettings} named
@@ -2014,7 +2038,7 @@
*/
withFeatureState(named, settings) {
if (!settings) return this;
- const valid = ["pip", "autoplay", "focusMode"];
+ const valid = ["pip", "autoplay", "focusMode", "customError"];
if (!valid.includes(named)) {
console.warn(`Excluding invalid feature key ${named}`);
return this;
@@ -2053,6 +2077,28 @@
}
return this;
}
+ /**
+ * @param {string|null|undefined} newState
+ * @return {Settings}
+ */
+ withCustomError(newState) {
+ if (newState === "disabled") {
+ return new _Settings({
+ ...this,
+ customError: { state: "disabled" }
+ });
+ }
+ if (newState === "enabled") {
+ return new _Settings({
+ ...this,
+ customError: {
+ state: "enabled",
+ signOnRequiredSelector: DEFAULT_SIGN_IN_REQURED_HREF
+ }
+ });
+ }
+ return this;
+ }
/**
* @return {string}
*/
@@ -2959,6 +3005,162 @@
}
};
+ // pages/duckplayer/app/providers/YouTubeErrorProvider.jsx
+ var YOUTUBE_ERROR_EVENT = "ddg-duckplayer-youtube-error";
+ var YOUTUBE_ERRORS = {
+ ageRestricted: "age-restricted",
+ signInRequired: "sign-in-required",
+ noEmbed: "no-embed",
+ unknown: "unknown"
+ };
+ var YOUTUBE_ERROR_IDS = Object.values(YOUTUBE_ERRORS);
+ var YouTubeErrorContext = J({
+ /** @type {YouTubeError|null} */
+ error: null
+ });
+ function YouTubeErrorProvider({ initial = null, children }) {
+ let initialError = null;
+ if (initial && YOUTUBE_ERROR_IDS.includes(initial)) {
+ initialError = initial;
+ }
+ const [error, setError] = h2(initialError);
+ const messaging2 = useMessaging();
+ const platformName = usePlatformName();
+ const setFocusMode = useSetFocusMode();
+ y2(() => {
+ const errorEventHandler = (event) => {
+ const eventError = event.detail?.error;
+ if (YOUTUBE_ERROR_IDS.includes(eventError) || eventError === null) {
+ if (eventError && eventError !== error) {
+ setFocusMode("paused");
+ if (platformName === "macos" || platformName === "ios") {
+ messaging2.reportYouTubeError({ error: eventError });
+ }
+ } else {
+ setFocusMode("enabled");
+ }
+ setError(eventError);
+ }
+ };
+ window.addEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ return () => window.removeEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ }, []);
+ return /* @__PURE__ */ g(YouTubeErrorContext.Provider, { value: { error } }, children);
+ }
+ function useYouTubeError() {
+ return x2(YouTubeErrorContext).error;
+ }
+
+ // pages/duckplayer/app/features/error-detection.js
+ var ErrorDetection = class {
+ /** @type {HTMLIFrameElement} */
+ iframe;
+ /** @type {CustomErrorOptions} */
+ options;
+ /**
+ * @param {CustomErrorOptions} options
+ */
+ constructor(options) {
+ this.options = options;
+ }
+ /**
+ * @param {HTMLIFrameElement} iframe
+ */
+ iframeDidLoad(iframe) {
+ this.iframe = iframe;
+ if (!this.options || !this.options.signInRequiredSelector) {
+ console.log("Missing Custom Error options");
+ return null;
+ }
+ const documentBody = iframe.contentWindow?.document?.body;
+ if (documentBody) {
+ if (this.checkForError(documentBody)) {
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ return null;
+ }
+ const observer = new MutationObserver(this.handleMutation.bind(this));
+ observer.observe(documentBody, {
+ childList: true,
+ subtree: true
+ // Observe all descendants of the body
+ });
+ return () => {
+ observer.disconnect();
+ };
+ }
+ return null;
+ }
+ /**
+ * Mutation handler that checks new nodes for error states
+ *
+ * @type {MutationCallback}
+ */
+ handleMutation(mutationsList) {
+ for (const mutation of mutationsList) {
+ if (mutation.type === "childList") {
+ mutation.addedNodes.forEach((node) => {
+ if (this.checkForError(node)) {
+ console.log("A node with an error has been added to the document:", node);
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ }
+ });
+ }
+ }
+ }
+ /**
+ * Attempts to detect the type of error in the YouTube embed iframe
+ * @returns {YouTubeError}
+ */
+ getErrorType() {
+ const iframeWindow = (
+ /** @type {Window & { ytcfg: object }} */
+ this.iframe.contentWindow
+ );
+ let playerResponse;
+ try {
+ playerResponse = JSON.parse(iframeWindow.ytcfg?.get("PLAYER_VARS")?.embedded_player_response);
+ } catch (e3) {
+ console.log("Could not parse player response", e3);
+ }
+ if (typeof playerResponse === "object") {
+ const {
+ previewPlayabilityStatus: { desktopLegacyAgeGateReason, status }
+ } = playerResponse;
+ if (status === "UNPLAYABLE") {
+ if (desktopLegacyAgeGateReason === 1) {
+ return YOUTUBE_ERRORS.ageRestricted;
+ }
+ return YOUTUBE_ERRORS.noEmbed;
+ }
+ try {
+ if (this.options?.signInRequiredSelector && !!iframeWindow.document.querySelector(this.options.signInRequiredSelector)) {
+ return YOUTUBE_ERRORS.signInRequired;
+ }
+ } catch (e3) {
+ console.log("Sign-in required query failed", e3);
+ }
+ }
+ return YOUTUBE_ERRORS.unknown;
+ }
+ /**
+ * Analyses a node and its children to determine if it contains an error state
+ *
+ * @param {Node} [node]
+ */
+ checkForError(node) {
+ if (node?.nodeType === Node.ELEMENT_NODE) {
+ const element = (
+ /** @type {HTMLElement} */
+ node
+ );
+ return element.classList.contains("ytp-error") || !!element.querySelector("ytp-error");
+ }
+ return false;
+ }
+ };
+
// pages/duckplayer/app/features/iframe.js
var IframeFeature = class {
/**
@@ -3015,6 +3217,12 @@
*/
mouseCapture: () => {
return new MouseCapture();
+ },
+ /**
+ * @return {IframeFeature}
+ */
+ errorDetection: () => {
+ return new ErrorDetection(settings.customError);
}
};
}
@@ -3083,7 +3291,8 @@
features.pip(),
features.clickCapture(),
features.titleCapture(),
- features.mouseCapture()
+ features.mouseCapture(),
+ features.errorDetection()
];
const cleanups = [];
const loadHandler = () => {
@@ -3110,6 +3319,48 @@
return { ref, didLoad: () => didLoad.current = true };
}
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ var import_classnames10 = __toESM(require_classnames(), 1);
+
+ // pages/duckplayer/app/components/YouTubeError.module.css
+ var YouTubeError_default = {
+ error: "YouTubeError_error",
+ desktop: "YouTubeError_desktop",
+ mobile: "YouTubeError_mobile",
+ container: "YouTubeError_container",
+ content: "YouTubeError_content",
+ icon: "YouTubeError_icon",
+ heading: "YouTubeError_heading",
+ messages: "YouTubeError_messages"
+ };
+
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ function useErrorStrings(kind) {
+ const { t: t3 } = useTypedTranslation();
+ switch (kind) {
+ case "sign-in-required":
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("signInRequiredErrorMessage1"), t3("signInRequiredErrorMessage2")],
+ variant: "paragraphs"
+ };
+ default:
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("blockedVideoErrorMessage1"), t3("blockedVideoErrorMessage2")],
+ variant: "paragraphs"
+ };
+ }
+ }
+ function YouTubeError({ kind, layout }) {
+ const { heading, messages, variant } = useErrorStrings(kind);
+ const classes = (0, import_classnames10.default)(YouTubeError_default.error, {
+ [YouTubeError_default.desktop]: layout === "desktop",
+ [YouTubeError_default.mobile]: layout === "mobile"
+ });
+ return /* @__PURE__ */ g("div", { className: classes }, /* @__PURE__ */ g("div", { className: YouTubeError_default.container }, /* @__PURE__ */ g("span", { className: YouTubeError_default.icon }), /* @__PURE__ */ g("div", { className: YouTubeError_default.content }, /* @__PURE__ */ g("h1", { className: YouTubeError_default.heading }, heading), messages && variant === "inline" && /* @__PURE__ */ g("p", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("span", { key: item }, item))), messages && variant === "paragraphs" && /* @__PURE__ */ g("div", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("p", { key: item }, item))), messages && variant === "list" && /* @__PURE__ */ g("ul", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("li", { key: item }, item))))));
+ }
+
// pages/duckplayer/app/components/Components.jsx
function Components() {
const settings = new Settings({
@@ -3118,11 +3369,11 @@
let embed = EmbedSettings.fromHref("https://localhost?videoID=123");
let url = embed?.toEmbedUrl();
if (!url) throw new Error("unreachable");
- return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
+ return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "sign-in-required" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "no-embed" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "sign-in-required" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "no-embed" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
}
// pages/duckplayer/app/components/MobileApp.jsx
- var import_classnames10 = __toESM(require_classnames(), 1);
+ var import_classnames11 = __toESM(require_classnames(), 1);
// pages/duckplayer/app/components/MobileApp.module.css
var MobileApp_default = {
@@ -3133,7 +3384,8 @@
switch: "MobileApp_switch",
embed: "MobileApp_embed",
logo: "MobileApp_logo",
- buttons: "MobileApp_buttons"
+ buttons: "MobileApp_buttons",
+ detachedControls: "MobileApp_detachedControls"
};
// pages/duckplayer/app/features/app.js
@@ -3231,11 +3483,13 @@
function MobileApp({ embed }) {
const settings = useSettings();
const telemetry2 = useTelemetry();
+ const youtubeError = useYouTubeError();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g(
+ return /* @__PURE__ */ g(k, null, !youtubeError && features.focusMode(), /* @__PURE__ */ g(
OrientationProvider,
{
onChange: (orientation) => {
+ if (youtubeError) return;
if (orientation === "portrait") {
return FocusMode.enable();
}
@@ -3251,7 +3505,10 @@
}
function MobileLayout({ embed }) {
const platformName = usePlatformName();
- return /* @__PURE__ */ g("main", { class: MobileApp_default.main }, /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("main", { class: MobileApp_default.main, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
}
// pages/duckplayer/app/components/DesktopApp.module.css
@@ -3272,10 +3529,14 @@
function DesktopApp({ embed }) {
const settings = useSettings();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app }, /* @__PURE__ */ g(DesktopLayout, { embed })));
+ const youtubeError = useYouTubeError();
+ return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g(DesktopLayout, { embed })));
}
function DesktopLayout({ embed }) {
- return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
}
// pages/duckplayer/app/index.js
@@ -3291,7 +3552,11 @@
console.log("locale:", environment.locale);
document.body.dataset.display = environment.display;
const strings = environment.locale === "en" ? duckplayer_default : await getTranslationsFromStringOrLoadDynamically(init2.localeStrings, environment.locale) || duckplayer_default;
- const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode"));
+ const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withFeatureState("customError", init2.settings.customError).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode")).withCustomError(baseEnvironment2.urlParams.get("customError"));
+ const initialYouTubeError = (
+ /** @type {YouTubeError} */
+ baseEnvironment2.urlParams.get("youtubeError")
+ );
console.log(settings);
const embed = createEmbedSettings(window.location.href, settings);
const didCatch = (error) => {
@@ -3303,7 +3568,7 @@
if (!root) throw new Error("could not render, root element missing");
if (environment.display === "app") {
D(
- /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
+ /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(YouTubeErrorProvider, { initial: initialYouTubeError }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
TranslationProvider,
{
translationObject: duckplayer_default,
@@ -3319,7 +3584,7 @@
textLength: environment.textLength
},
/* @__PURE__ */ g(MobileApp, { embed })
- ), /* @__PURE__ */ g(WillThrow, null))))))),
+ ), /* @__PURE__ */ g(WillThrow, null)))))))),
root
);
} else if (environment.display === "components") {
@@ -3467,6 +3732,13 @@
onUserValuesChanged(cb) {
return this.messaging.subscribe("onUserValuesChanged", cb);
}
+ /**
+ * This will be sent if the application fails to load.
+ * @param {{error: import('../types/duckplayer.ts').YouTubeError}} params
+ */
+ reportYouTubeError(params) {
+ this.messaging.notify("reportYouTubeError", params);
+ }
/**
* This will be sent if the application has loaded, but a client-side error
* has occurred that cannot be recovered from
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/index.html b/Sources/ContentScopeScripts/dist/pages/duckplayer/index.html
index de7e16964..e1771b67b 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/index.html
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/index.html
@@ -85,6 +85,7 @@
/* pages/duckplayer/app/components/Components.module.css */
.Components_main {
+ background-color: #000;
color: white;
max-width: 3840px;
margin: 0 auto;
@@ -136,7 +137,7 @@
text-decoration: none;
}
[data-layout=mobile] .Button_button {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
.Button_button:hover,
.Button_button:focus-visible {
@@ -212,7 +213,7 @@
.SwitchBarMobile_switchBar {
display: grid;
border-radius: 8px;
- background: #2f2f2f;
+ background: rgba(255, 255, 255, 0.12);
padding-inline: 16px;
height: 100%;
line-height: 1.1;
@@ -542,6 +543,9 @@
.Wordmark_mobile_logo {
height: 100px;
}
+ [data-youtube-error=true] .Wordmark_mobile_logo {
+ height: 44px;
+ }
}
.Wordmark_mobile_logoSvg img {
display: block;
@@ -613,6 +617,127 @@
}
}
+/* pages/duckplayer/app/components/YouTubeError.module.css */
+.YouTubeError_error {
+ align-items: center;
+ background: rgba(0, 0, 0, 0.6);
+ display: grid;
+ height: 100%;
+ justify-items: center;
+}
+.YouTubeError_error.YouTubeError_desktop {
+ height: var(--frame-height);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+.YouTubeError_error.YouTubeError_mobile {
+ border-radius: var(--inner-radius);
+ height: 100%;
+ overflow: auto;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_error.YouTubeError_mobile {
+ aspect-ratio: 16 / 9;
+ }
+}
+.YouTubeError_desktop {
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.YouTubeError_container {
+ column-gap: 24px;
+ display: flex;
+ flex-flow: row;
+ margin: 0;
+ max-width: 680px;
+ padding: 0 40px;
+ row-gap: 4px;
+}
+.YouTubeError_mobile .YouTubeError_container {
+ flex-flow: column;
+ padding: 0 24px;
+}
+@media screen and (min-height: 320px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 16px 0;
+ }
+}
+@media screen and (min-width: 375px) and (min-height: 400px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 36px 0;
+ }
+}
+.YouTubeError_content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 16px 0;
+}
+@media screen and (min-width: 600px) {
+ .YouTubeError_content {
+ margin: 24px 0;
+ }
+}
+.YouTubeError_icon {
+ align-self: center;
+ display: flex;
+ justify-content: center;
+}
+.YouTubeError_icon::before {
+ content: " ";
+ display: block;
+ background: url('data:image/svg+xml,%0A') no-repeat;
+ height: 48px;
+ width: 48px;
+}
+@media screen and (max-width: 320px) {
+ .YouTubeError_icon {
+ display: none;
+ }
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_icon {
+ justify-content: start;
+ }
+ .YouTubeError_icon::before {
+ background-image: url('data:image/svg+xml,%0A');
+ height: 96px;
+ width: 128px;
+ }
+}
+.YouTubeError_heading {
+ color: #fff;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: calc(24 / 20);
+ margin: 0;
+}
+.YouTubeError_messages {
+ color: #ccc;
+ font-size: 16px;
+ line-height: calc(24 / 16);
+}
+div.YouTubeError_messages {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+div.YouTubeError_messages p {
+ margin: 0;
+}
+p.YouTubeError_messages {
+ margin: 0;
+}
+ul.YouTubeError_messages li {
+ list-style: disc;
+ margin-left: 24px;
+}
+
/* pages/duckplayer/app/components/MobileApp.module.css */
body[data-display=app] {
padding: 8px;
@@ -650,6 +775,7 @@
--inner-radius: 12px;
--logo-width: 157px;
--inner-padding: 8px;
+ --mobile-buttons-padding: 8px;
position: relative;
max-width: 100vh;
margin: 0 auto;
@@ -705,6 +831,15 @@
grid-area: switch;
height: 44px;
}
+.MobileApp_detachedControls {
+ grid-area: detached;
+ display: flex;
+ flex-flow: column;
+ gap: 8px;
+ padding: 8px;
+ background: #2f2f2f;
+ border-radius: 12px;
+}
@media screen and (min-width: 425px) and (max-height: 600px) {
.MobileApp_main {
grid-template-rows: max-content auto max-content max-content 12px max-content auto;
@@ -798,6 +933,71 @@
justify-content: end;
}
}
+@media screen and (max-width: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ --bg-color: transparent;
+ --inner-padding: 4px;
+ grid-template-areas: "logo" "gap3" "embed" "gap4" "switch" "buttons";
+ grid-template-rows: max-content 16px auto 12px max-content max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ background: #2f2f2f;
+ border-radius: var(--outer-radius);
+ padding: 4px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_switch {
+ background: #2f2f2f;
+ padding: 8px 8px 0 8px;
+ height: 60px;
+ max-height: 60px;
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ background: #2f2f2f;
+ padding: 8px;
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_switch {
+ background: transparent;
+ max-height: 0;
+ }
+}
+@media screen and (max-width: 599px) and (max-height: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ max-width: unset;
+ grid-template-rows: 0 0 auto 12px 0 max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] :is(.MobileApp_logo, .MobileApp_switch) {
+ display: none;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+}
+@media screen and (min-width: 600px) and (max-height: 450px) {
+ .MobileApp_main[data-youtube-error=true] {
+ grid-template-areas: "embed" "buttons" "gap5";
+ grid-template-rows: auto max-content 8px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ display: block;
+ }
+}
+@media screen and (max-height: 320px) {
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ overflow-y: auto;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ bottom: 0;
+ position: sticky;
+ }
+}
/* pages/duckplayer/app/components/MobileButtons.module.css */
.MobileButtons_buttons {
@@ -2916,12 +3116,33 @@
title: "ERROR: Invalid video id",
note: "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ blockedVideoErrorHeading: {
+ title: "YouTube won\u2019t let Duck Player load this video",
+ note: "Message shown when YouTube has blocked playback of a video"
+ },
+ blockedVideoErrorMessage1: {
+ title: "YouTube doesn\u2019t allow this video to be viewed outside of YouTube.",
+ note: "Explanation on why the error is happening."
+ },
+ blockedVideoErrorMessage2: {
+ title: "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ signInRequiredErrorMessage1: {
+ title: "YouTube is blocking this video from loading. If you\u2019re using a VPN, try turning it off and reloading this page.",
+ note: "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ signInRequiredErrorMessage2: {
+ title: "If this doesn\u2019t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "More troubleshooting tips for this specific error"
+ },
tooltipInfo: {
title: "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
};
// pages/duckplayer/app/settings.js
+ var DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
var Settings = class _Settings {
/**
* @param {object} params
@@ -2929,17 +3150,20 @@
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
+ * @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
*/
constructor({
platform = { name: "macos" },
pip = { state: "disabled" },
autoplay = { state: "enabled" },
- focusMode = { state: "enabled" }
+ focusMode = { state: "enabled" },
+ customError = { state: "disabled", signInRequiredSelector: "" }
}) {
this.platform = platform;
this.pip = pip;
this.autoplay = autoplay;
this.focusMode = focusMode;
+ this.customError = customError;
}
/**
* @param {keyof import("../types/duckplayer.js").DuckPlayerPageSettings} named
@@ -2948,7 +3172,7 @@
*/
withFeatureState(named, settings) {
if (!settings) return this;
- const valid = ["pip", "autoplay", "focusMode"];
+ const valid = ["pip", "autoplay", "focusMode", "customError"];
if (!valid.includes(named)) {
console.warn(`Excluding invalid feature key ${named}`);
return this;
@@ -2987,6 +3211,28 @@
}
return this;
}
+ /**
+ * @param {string|null|undefined} newState
+ * @return {Settings}
+ */
+ withCustomError(newState) {
+ if (newState === "disabled") {
+ return new _Settings({
+ ...this,
+ customError: { state: "disabled" }
+ });
+ }
+ if (newState === "enabled") {
+ return new _Settings({
+ ...this,
+ customError: {
+ state: "enabled",
+ signOnRequiredSelector: DEFAULT_SIGN_IN_REQURED_HREF
+ }
+ });
+ }
+ return this;
+ }
/**
* @return {string}
*/
@@ -3893,6 +4139,162 @@
}
};
+ // pages/duckplayer/app/providers/YouTubeErrorProvider.jsx
+ var YOUTUBE_ERROR_EVENT = "ddg-duckplayer-youtube-error";
+ var YOUTUBE_ERRORS = {
+ ageRestricted: "age-restricted",
+ signInRequired: "sign-in-required",
+ noEmbed: "no-embed",
+ unknown: "unknown"
+ };
+ var YOUTUBE_ERROR_IDS = Object.values(YOUTUBE_ERRORS);
+ var YouTubeErrorContext = J({
+ /** @type {YouTubeError|null} */
+ error: null
+ });
+ function YouTubeErrorProvider({ initial = null, children }) {
+ let initialError = null;
+ if (initial && YOUTUBE_ERROR_IDS.includes(initial)) {
+ initialError = initial;
+ }
+ const [error, setError] = h2(initialError);
+ const messaging2 = useMessaging();
+ const platformName = usePlatformName();
+ const setFocusMode = useSetFocusMode();
+ y2(() => {
+ const errorEventHandler = (event) => {
+ const eventError = event.detail?.error;
+ if (YOUTUBE_ERROR_IDS.includes(eventError) || eventError === null) {
+ if (eventError && eventError !== error) {
+ setFocusMode("paused");
+ if (platformName === "macos" || platformName === "ios") {
+ messaging2.reportYouTubeError({ error: eventError });
+ }
+ } else {
+ setFocusMode("enabled");
+ }
+ setError(eventError);
+ }
+ };
+ window.addEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ return () => window.removeEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ }, []);
+ return /* @__PURE__ */ g(YouTubeErrorContext.Provider, { value: { error } }, children);
+ }
+ function useYouTubeError() {
+ return x2(YouTubeErrorContext).error;
+ }
+
+ // pages/duckplayer/app/features/error-detection.js
+ var ErrorDetection = class {
+ /** @type {HTMLIFrameElement} */
+ iframe;
+ /** @type {CustomErrorOptions} */
+ options;
+ /**
+ * @param {CustomErrorOptions} options
+ */
+ constructor(options) {
+ this.options = options;
+ }
+ /**
+ * @param {HTMLIFrameElement} iframe
+ */
+ iframeDidLoad(iframe) {
+ this.iframe = iframe;
+ if (!this.options || !this.options.signInRequiredSelector) {
+ console.log("Missing Custom Error options");
+ return null;
+ }
+ const documentBody = iframe.contentWindow?.document?.body;
+ if (documentBody) {
+ if (this.checkForError(documentBody)) {
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ return null;
+ }
+ const observer = new MutationObserver(this.handleMutation.bind(this));
+ observer.observe(documentBody, {
+ childList: true,
+ subtree: true
+ // Observe all descendants of the body
+ });
+ return () => {
+ observer.disconnect();
+ };
+ }
+ return null;
+ }
+ /**
+ * Mutation handler that checks new nodes for error states
+ *
+ * @type {MutationCallback}
+ */
+ handleMutation(mutationsList) {
+ for (const mutation of mutationsList) {
+ if (mutation.type === "childList") {
+ mutation.addedNodes.forEach((node) => {
+ if (this.checkForError(node)) {
+ console.log("A node with an error has been added to the document:", node);
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ }
+ });
+ }
+ }
+ }
+ /**
+ * Attempts to detect the type of error in the YouTube embed iframe
+ * @returns {YouTubeError}
+ */
+ getErrorType() {
+ const iframeWindow = (
+ /** @type {Window & { ytcfg: object }} */
+ this.iframe.contentWindow
+ );
+ let playerResponse;
+ try {
+ playerResponse = JSON.parse(iframeWindow.ytcfg?.get("PLAYER_VARS")?.embedded_player_response);
+ } catch (e3) {
+ console.log("Could not parse player response", e3);
+ }
+ if (typeof playerResponse === "object") {
+ const {
+ previewPlayabilityStatus: { desktopLegacyAgeGateReason, status }
+ } = playerResponse;
+ if (status === "UNPLAYABLE") {
+ if (desktopLegacyAgeGateReason === 1) {
+ return YOUTUBE_ERRORS.ageRestricted;
+ }
+ return YOUTUBE_ERRORS.noEmbed;
+ }
+ try {
+ if (this.options?.signInRequiredSelector && !!iframeWindow.document.querySelector(this.options.signInRequiredSelector)) {
+ return YOUTUBE_ERRORS.signInRequired;
+ }
+ } catch (e3) {
+ console.log("Sign-in required query failed", e3);
+ }
+ }
+ return YOUTUBE_ERRORS.unknown;
+ }
+ /**
+ * Analyses a node and its children to determine if it contains an error state
+ *
+ * @param {Node} [node]
+ */
+ checkForError(node) {
+ if (node?.nodeType === Node.ELEMENT_NODE) {
+ const element = (
+ /** @type {HTMLElement} */
+ node
+ );
+ return element.classList.contains("ytp-error") || !!element.querySelector("ytp-error");
+ }
+ return false;
+ }
+ };
+
// pages/duckplayer/app/features/iframe.js
var IframeFeature = class {
/**
@@ -3949,6 +4351,12 @@
*/
mouseCapture: () => {
return new MouseCapture();
+ },
+ /**
+ * @return {IframeFeature}
+ */
+ errorDetection: () => {
+ return new ErrorDetection(settings.customError);
}
};
}
@@ -4017,7 +4425,8 @@
features.pip(),
features.clickCapture(),
features.titleCapture(),
- features.mouseCapture()
+ features.mouseCapture(),
+ features.errorDetection()
];
const cleanups = [];
const loadHandler = () => {
@@ -4044,6 +4453,48 @@
return { ref, didLoad: () => didLoad.current = true };
}
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ var import_classnames10 = __toESM(require_classnames(), 1);
+
+ // pages/duckplayer/app/components/YouTubeError.module.css
+ var YouTubeError_default = {
+ error: "YouTubeError_error",
+ desktop: "YouTubeError_desktop",
+ mobile: "YouTubeError_mobile",
+ container: "YouTubeError_container",
+ content: "YouTubeError_content",
+ icon: "YouTubeError_icon",
+ heading: "YouTubeError_heading",
+ messages: "YouTubeError_messages"
+ };
+
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ function useErrorStrings(kind) {
+ const { t: t3 } = useTypedTranslation();
+ switch (kind) {
+ case "sign-in-required":
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("signInRequiredErrorMessage1"), t3("signInRequiredErrorMessage2")],
+ variant: "paragraphs"
+ };
+ default:
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("blockedVideoErrorMessage1"), t3("blockedVideoErrorMessage2")],
+ variant: "paragraphs"
+ };
+ }
+ }
+ function YouTubeError({ kind, layout }) {
+ const { heading, messages, variant } = useErrorStrings(kind);
+ const classes = (0, import_classnames10.default)(YouTubeError_default.error, {
+ [YouTubeError_default.desktop]: layout === "desktop",
+ [YouTubeError_default.mobile]: layout === "mobile"
+ });
+ return /* @__PURE__ */ g("div", { className: classes }, /* @__PURE__ */ g("div", { className: YouTubeError_default.container }, /* @__PURE__ */ g("span", { className: YouTubeError_default.icon }), /* @__PURE__ */ g("div", { className: YouTubeError_default.content }, /* @__PURE__ */ g("h1", { className: YouTubeError_default.heading }, heading), messages && variant === "inline" && /* @__PURE__ */ g("p", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("span", { key: item }, item))), messages && variant === "paragraphs" && /* @__PURE__ */ g("div", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("p", { key: item }, item))), messages && variant === "list" && /* @__PURE__ */ g("ul", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("li", { key: item }, item))))));
+ }
+
// pages/duckplayer/app/components/Components.jsx
function Components() {
const settings = new Settings({
@@ -4052,11 +4503,11 @@
let embed = EmbedSettings.fromHref("https://localhost?videoID=123");
let url = embed?.toEmbedUrl();
if (!url) throw new Error("unreachable");
- return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
+ return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "sign-in-required" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "no-embed" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "sign-in-required" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "no-embed" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
}
// pages/duckplayer/app/components/MobileApp.jsx
- var import_classnames10 = __toESM(require_classnames(), 1);
+ var import_classnames11 = __toESM(require_classnames(), 1);
// pages/duckplayer/app/components/MobileApp.module.css
var MobileApp_default = {
@@ -4067,7 +4518,8 @@
switch: "MobileApp_switch",
embed: "MobileApp_embed",
logo: "MobileApp_logo",
- buttons: "MobileApp_buttons"
+ buttons: "MobileApp_buttons",
+ detachedControls: "MobileApp_detachedControls"
};
// pages/duckplayer/app/features/app.js
@@ -4165,11 +4617,13 @@
function MobileApp({ embed }) {
const settings = useSettings();
const telemetry2 = useTelemetry();
+ const youtubeError = useYouTubeError();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g(
+ return /* @__PURE__ */ g(k, null, !youtubeError && features.focusMode(), /* @__PURE__ */ g(
OrientationProvider,
{
onChange: (orientation) => {
+ if (youtubeError) return;
if (orientation === "portrait") {
return FocusMode.enable();
}
@@ -4185,7 +4639,10 @@
}
function MobileLayout({ embed }) {
const platformName = usePlatformName();
- return /* @__PURE__ */ g("main", { class: MobileApp_default.main }, /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("main", { class: MobileApp_default.main, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
}
// pages/duckplayer/app/components/DesktopApp.module.css
@@ -4206,10 +4663,14 @@
function DesktopApp({ embed }) {
const settings = useSettings();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app }, /* @__PURE__ */ g(DesktopLayout, { embed })));
+ const youtubeError = useYouTubeError();
+ return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g(DesktopLayout, { embed })));
}
function DesktopLayout({ embed }) {
- return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
}
// pages/duckplayer/app/index.js
@@ -4225,7 +4686,11 @@
console.log("locale:", environment.locale);
document.body.dataset.display = environment.display;
const strings = environment.locale === "en" ? duckplayer_default : await getTranslationsFromStringOrLoadDynamically(init2.localeStrings, environment.locale) || duckplayer_default;
- const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode"));
+ const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withFeatureState("customError", init2.settings.customError).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode")).withCustomError(baseEnvironment2.urlParams.get("customError"));
+ const initialYouTubeError = (
+ /** @type {YouTubeError} */
+ baseEnvironment2.urlParams.get("youtubeError")
+ );
console.log(settings);
const embed = createEmbedSettings(window.location.href, settings);
const didCatch = (error) => {
@@ -4237,7 +4702,7 @@
if (!root) throw new Error("could not render, root element missing");
if (environment.display === "app") {
D(
- /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
+ /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(YouTubeErrorProvider, { initial: initialYouTubeError }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
TranslationProvider,
{
translationObject: duckplayer_default,
@@ -4253,7 +4718,7 @@
textLength: environment.textLength
},
/* @__PURE__ */ g(MobileApp, { embed })
- ), /* @__PURE__ */ g(WillThrow, null))))))),
+ ), /* @__PURE__ */ g(WillThrow, null)))))))),
root
);
} else if (environment.display === "components") {
@@ -4401,6 +4866,13 @@
onUserValuesChanged(cb) {
return this.messaging.subscribe("onUserValuesChanged", cb);
}
+ /**
+ * This will be sent if the application fails to load.
+ * @param {{error: import('../types/duckplayer.ts').YouTubeError}} params
+ */
+ reportYouTubeError(params) {
+ this.messaging.notify("reportYouTubeError", params);
+ }
/**
* This will be sent if the application has loaded, but a client-side error
* has occurred that cannot be recovered from
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/bg/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/bg/duckplayer.json
index d7f3d20ef..c807a2f78 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/bg/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/bg/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ГРЕШКА: невалиден идентификатор на видеоклипа",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube няма да позволи на Duck Player да зареди това видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволява това видео да бъде гледано извън YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Все пак можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокира зареждането на това видео. Ако използвате VPN, опитайте да го изключите и презаредете тази страница.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ако това не свърши работа, можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player осигурява чисто изживяване без персонализирани реклами в YouTube и предотвратява влиянието на вече гледаните видеоклипове върху препоръките на YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/cs/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/cs/duckplayer.json
index 8d24c5726..690569a5d 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/cs/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/cs/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatné ID videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovoluje přehrávači Duck Player načíst tohle video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nedovoluje spuštění videa mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Na tohle video se můžeš pořád podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítání tohohle videa. Pokud používáš VPN, zkus ji vypnout a stránku znovu načíst.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Pokud to nefunguje, můžeš se na video podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Přehrávač Duck Player nabízí sledování v minimalistickém prostředí bez personalizovaných reklam a brání tomu, aby sledovaná videa ovlivňovala tvoje doporučení na YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/da/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/da/duckplayer.json
index f99ab298e..aae6004c2 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/da/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/da/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEJL: Ugyldigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube vil ikke lade Duck Player indlæse denne video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillader ikke, at denne video vises uden for YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokerer for, at denne video kan indlæses. Hvis du bruger en VPN, så prøv at slå den fra og genindlæse denne side.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis dette ikke virker, kan du stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player giver en ren seeroplevelse uden målrettede annoncer og forhindrer, at visningsaktivitet påvirker dine YouTube-anbefalinger."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/de/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/de/duckplayer.json
index 0ddca103a..abd163119 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/de/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/de/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEHLER: Ungültige Video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lässt den Duck Player dieses Video nicht laden",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube erlaubt nicht, dass dieses Video außerhalb von YouTube angesehen wird.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kannst dieses Video auf YouTube ansehen, aber ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockiert das Laden dieses Videos. Falls du ein VPN benutzt, deaktiviere es und lade diese Seite neu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Falls das nicht funktioniert, kannst du das Video dennoch auf YouTube ansehen, jedoch ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Mit Duck Player kannst du dir ungestört und ohne personalisierte Werbung Inhalte ansehen. Er verhindert, dass das, was du dir ansiehst, deine YouTube-Empfehlungen beeinflussen."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/el/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/el/duckplayer.json
index d00195f5e..2d8fa43c5 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/el/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/el/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ΣΦΑΛΜΑ: Μη έγκυρο αναγνωριστικό βίντεο",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "Το YouTube δεν θα αφήσει το Duck Player να φορτώσει το βίντεο αυτό",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Το YouTube δεν επιτρέπει την προβολή αυτού του βίντεο εκτός YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Μπορείτε ακόμα να παρακολουθήσετε αυτό το βίντεο στο YouTube, αλλά χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "Το YouTube μπλοκάρει τη φόρτωση αυτού του βίντεο. Εάν χρησιμοποιείτε VPN, δοκιμάστε να το απενεργοποιήσετε και να φορτώσετε εκ νέου αυτήν τη σελίδα.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Εάν δεν λειτουργήσει αυτό, μπορείτε να παρακολουθήσετε αυτό το βίντεο στο YouTube, ωστόσο χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Το Duck Player παρέχει μια καθαρή εμπειρία προβολής χωρίς εξατομικευμένες διαφημίσεις, ενώ εμποδίζει τη δραστηριότητα προβολής να επηρεάσει τις συστάσεις που θα λαμβάνετε στο YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/en/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/en/duckplayer.json
index c2b5683b9..fe94917c4 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/en/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/en/duckplayer.json
@@ -33,6 +33,26 @@
"title": "ERROR: Invalid video id",
"note": "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading": {
+ "title": "YouTube won’t let Duck Player load this video",
+ "note": "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1": {
+ "title": "YouTube doesn’t allow this video to be viewed outside of YouTube.",
+ "note": "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2": {
+ "title": "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1": {
+ "title": "YouTube is blocking this video from loading. If you’re using a VPN, try turning it off and reloading this page.",
+ "note": "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2": {
+ "title": "If this doesn’t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "More troubleshooting tips for this specific error"
+ },
"tooltipInfo": {
"title": "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/es/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/es/duckplayer.json
index 1b5d8b958..f5af0d046 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/es/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/es/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERROR: ID de vídeo no válida",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube no permite que Duck Player cargue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube no permite que este vídeo se vea fuera de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional que ofrece Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube está bloqueando la carga de este vídeo. Si estás usando una VPN, intenta desactivarla y volver a cargar la página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si esto no funciona, sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ofrece una experiencia de visualización limpia sin anuncios personalizados e impide que la actividad de visualización influya en tus recomendaciones de YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/et/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/et/duckplayer.json
index c9863f4f5..70b30efee 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/et/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/et/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIGA: vale video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei luba Duck Playeril seda videot laadida",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei luba seda videot väljaspool YouTube'i vaadata.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Saate seda videot endiselt YouTube'is vaadata, kuid ilma Duck Player'i lisatud privaatsuseta.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokeerib selle video laadimise. Kui kasutate VPN-i, proovige see välja lülitada ning leht uuesti laadida.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Kui see ei aita, saate seda videot ikkagi YouTube'is vaadata, kuid ilma Duck Playeri lisatud privaatsuseta.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pakub isikupärastatud reklaamidest vaba vaatamiskogemust ja takistab, et vaatamisaktiivsus mõjutaks sinu YouTube'i soovitusi."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fi/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fi/duckplayer.json
index e73022b8f..4c830a739 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fi/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fi/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIRHE: virheellinen videotunnus",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei salli Duck Playerin ladata tätä videota.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei salli tämän videon katsomista YouTuben ulkopuolella.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Voit yhä katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube estää tämän videon latautumisen. Jos käytät VPN:ää, kytke se pois päältä ja lataa tämä sivu uudelleen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jos tämä ei toimi, voit silti katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tarjoaa puhtaan katselukokemuksen ilman kohdennettuja mainoksia ja estää katseluhistoriaa vaikuttamasta YouTube-suosituksiisi."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fr/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fr/duckplayer.json
index 716f0c071..12eb0ac92 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fr/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/fr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERREUR : identifiant vidéo non valide",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne permet pas à Duck Player de charger cette vidéo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube n'autorise pas le visionnage de cette vidéo en dehors de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloque le chargement de cette vidéo. Si vous utilisez un VPN, essayez de le désactiver et de recharger cette page.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si cela ne fonctionne pas, vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre une expérience de visionnage épurée, sans publicités personnalisées, et empêche l'activité de visionnage d'influencer vos recommandations YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hr/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hr/duckplayer.json
index 3f0e8aeae..48fdbf997 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hr/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "POGREŠKA: Nevažeći ID videozapisa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dopušta Duck Playeru da učita ovaj videozapis.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dopušta da se ovaj videozapis gleda izvan YouTubea.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Još uvijek možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokira učitavanje ovog videozapisa. Ako koristiš VPN, pokušaj ga isključiti i ponovno učitati ovu stranicu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ako to ne uspije, i dalje možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pruža čisti doživljaj gledanja bez personaliziranih oglasa i sprječava da aktivnosti gledanja utječu na tvoje preporuke na YouTubeu."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hu/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hu/duckplayer.json
index 3bbe06210..c8faf475f 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hu/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/hu/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HIBA: Érvénytelen videoazonosító",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "A YouTube nem engedi, hogy a Duck Player betöltse ezt a videót",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "A YouTube nem engedi, hogy ezt a videót a YouTube-on kívül nézd meg.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Megnézheted a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "A YouTube blokkolja ennek a videónak a betöltését. Ha VPN-t használsz, próbáld meg, hogy kikapcsolod, majd újra betöltöd ezt az oldalt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ha ez nem működik, akkor is megnézheted ezt a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "A Duck Player személyre szabott hirdetések nélküli, letisztult megtekintési élményt nyújt, és megakadályozza, hogy a megtekintési tevékenységed befolyásolja a neked szóló YouTube-ajánlásokat."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/it/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/it/duckplayer.json
index 9ce216677..464303fca 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/it/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/it/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRORE: ID video non valido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube non consente a Duck Player di caricare questo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Questo video si può vedere solo su YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Puoi ancora guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube sta impedendo il caricamento di questo video. Se stai utilizzando una VPN, prova a disattivarla e a ricaricare questa pagina.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se il problema non si risolve, puoi comunque guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre un'esperienza di visualizzazione pulita, senza annunci personalizzati, e impedisce che l'attività di visualizzazione incida sulle raccomandazioni di YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lt/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lt/duckplayer.json
index 1b282dd2c..c8dd25e38 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lt/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KLAIDA: netinkamas vaizdo įrašo ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "„YouTube“ neleidžia „Duck Player“ įkelti šio vaizdo įrašo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "„YouTube“ neleidžia šio vaizdo įrašo žiūrėti ne „YouTube“ platformoje.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šį vaizdo įrašą vis dar gali žiūrėti „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "„YouTube“ blokuoja šio vaizdo įrašo įkėlimą. Jei naudoji VPN, pabandyk jį išjungti ir iš naujo įkelti šį puslapį.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jei tai neveikia, vis tiek gali žiūrėti šį vaizdo įrašą „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "„Duck Player“ užtikrina nepriekaištingą žiūrėjimo patirtį be suasmenintų reklamų ir neleidžia žiūrėjimo veiklai daryti įtakos „YouTube“ rekomendacijoms."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lv/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lv/duckplayer.json
index 46d0bee8e..3ae376c31 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lv/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/lv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KĻŪDA: Nederīgs video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube neļauj Duck Player ielādēt šo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube neļauj skatīties šo video ārpus YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šo videoklipu joprojām vari skatīties vietnē YouTube, taču bez papildu Duck Player konfidencialitātes.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloķē šī video ielādi. Ja tu izmanto VPN, mēģini to izslēgt un pārlādēt šo lapu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ja tas nedarbojas, joprojām vari skatīties šo video vietnē YouTube, taču bez papildu privātuma, ko nodrošina Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player nodrošina netraucētu skatīšanās pieredzi bez personalizētām reklāmām un neļauj skatīšanās darbībām ietekmēt tavus YouTube ieteikumus."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nb/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nb/duckplayer.json
index 4c1d826f6..fef475447 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nb/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nb/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEIL: Ugyldig video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lar ikke Duck Player laste denne videoen",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillater ikke visning av denne videoen utenfor YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fremdeles se videoen på YouTube, men uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkerer denne videoen fra å lastes. Hvis du bruker en VPN, kan du prøve å slå den av og laste denne siden på nytt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis ikke det virker, kan du fremdeles se videoen på YouTube, bare uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tilbyr en ren seeropplevelse uten tilpassede annonser og forhindrer at seeraktiviteten din påvirker YouTube-anbefalingene dine."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nl/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nl/duckplayer.json
index a1be8669e..51a0ece78 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nl/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/nl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FOUT: ongeldige video-id",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube staat Duck Player niet toe om deze video te laden.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube staat niet toe dat je deze video buiten YouTube bekijkt.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Je kunt deze video nog steeds bekijken op YouTube, maar zonder de extra privacy van Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkeert het laden van deze video. Als je een VPN gebruikt, probeer deze dan uit te schakelen en deze pagina opnieuw te laden.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Als dit niet werkt, kun je deze video nog steeds op YouTube bekijken, maar dan zonder de extra privacy van Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player biedt puur kijkplezier zonder gepersonaliseerde advertenties en voorkomt dat de dingen die je bekijkt je YouTube-aanbevelingen beïnvloeden."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pl/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pl/duckplayer.json
index accd3fde5..8b633d415 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pl/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "BŁĄD: nieprawidłowy identyfikator filmu",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nie zezwala na załadowanie tego filmu przez Duck Player",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nie zezwala na oglądanie tego filmu poza YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Nadal możesz oglądać ten film na YouTube, ale bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje ładowanie tego filmu. Jeśli korzystasz z sieci VPN, spróbuj ją wyłączyć i ponownie załadować tę stronę.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jeśli to nie pomoże, nadal możesz oglądać ten film na YouTube, jednak bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player zapewnia czyste środowisko oglądania bez spersonalizowanych reklam i sprawia, że aktywność związana z oglądaniem filmów nie wpływa na rekomendacje YouTube'a."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pt/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pt/duckplayer.json
index a5bfca188..2ff6fad9c 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pt/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/pt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRO: ID de vídeo inválido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "O YouTube não permite que o Duck Player carregue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "O YouTube não permite que vejas este vídeo fora do YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "O YouTube está a bloquear o carregamento deste vídeo. Se estiveres a usar uma VPN, tenta desativá-la e recarregar esta página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se isto não funcionar, continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "O Duck Player oferece uma experiência de visualização limpa sem anúncios personalizados e evita que as atividades de visualização influenciem as recomendações do YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ro/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ro/duckplayer.json
index bfafec70e..25ffac535 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ro/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ro/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "EROARE: ID video incorect",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nu permite Duck Player să încarce acest videoclip",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nu permite ca acest videoclip să fie vizionat în afara YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blochează încărcarea acestui videoclip. Dacă folosești un VPN, încearcă să-l dezactivezi și să reîncarci această pagină.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Dacă acest lucru nu funcționează, poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player oferă o experiență de vizionare fără perturbări, fără reclame personalizate și împiedică activitatea de vizionare să îți influențeze recomandările YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ru/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ru/duckplayer.json
index 4bf5cc0c1..d1a3fc528 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ru/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/ru/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ОШИБКА: Неверный идентификатор видео",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube не позволяет проигрывателю Duck Player загрузить это видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволяет смотреть это видео вне своей платформы.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Вы по-прежнему можете посмотреть этот ролик на YouTube, но уже без дополнительной защиты Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокирует загрузку этого видео. Если вы используете VPN, отключите ее и перезагрузите страницу.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Если это не даст результата, вы все равно сможете просмотреть это видео на YouTube, но без дополнительной защиты конфиденциальности, обеспечиваемой Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Проигрыватель Duck Player обеспечивает беспрепятственный просмотр без персонализированной рекламы и влияния просмотренных роликов на рекомендации в YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sk/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sk/duckplayer.json
index 88a2cc3a5..a68cdeb89 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sk/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sk/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatný identifikátor videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovolí Duck Playeru načítať toto video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nepovoľuje, aby sa toto video pozeralo mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Toto video si môžeš pozrieť aj na YouTube, ale bez dodatočného súkromia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítanie tohto videa. Ak používaš sieť VPN, skús ju vypnúť a znova načítať túto stránku.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ak to nefunguje, môžeš si toto video pozrieť aj na YouTube, ale bez dodatočnej ochrany súkromia, ktorú poskytuje Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player poskytuje čisté zobrazenie bez personalizovaných reklám a zabraňuje tomu, aby aktivita pri sledovaní ovplyvňovala vaše odporúčania v službe YouTube."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sl/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sl/duckplayer.json
index 7d4a89155..977830ef4 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sl/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "NAPAKA: Neveljaven ID videoposnetka",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dovoli predvajalniku Duck Player naložiti tega videa",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dovoljuje, da si ta videoposnetek ogledate zunaj YouTuba.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Ta videoposnetek si lahko še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube preprečuje nalaganje tega videoposnetka. Če uporabljate omrežje VPN, ga poskusite izklopiti in znova naložite to stran.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Če to ne deluje, si lahko ta videoposnetek še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Predvajalnik Duck Player zagotavlja čisto izkušnjo gledanja brez prilagojenih oglasov in preprečuje, da bi dejavnost gledanja vplivala na vaša priporočila v YouTubu."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sv/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sv/duckplayer.json
index cb5b75e4c..444e91750 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sv/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/sv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEL: Ogiltigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube låter inte Duck Player läsa in den här videon",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillåter inte att den här videon ses utanför YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockerar den här videon från att läsas in. Om du använder ett VPN kan du prova stänga av det och läsa in sidan igen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Om det inte fungerar kan du fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ger en störningsfri visningsupplevelse utan personliga annonser och förhindrar att din tittaraktivitet påverkar YouTube-rekommendationer."
}
diff --git a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/tr/duckplayer.json b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/tr/duckplayer.json
index c23cab2bb..e733fcaa6 100644
--- a/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/tr/duckplayer.json
+++ b/Sources/ContentScopeScripts/dist/pages/duckplayer/locales/tr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HATA: Geçersiz video kimliği",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube, Duck Player'ın bu videoyu yüklemesine izin vermiyor",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube, bu videonun YouTube dışında izlenmesine izin vermiyor.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Bu videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bu videonun yüklenmesini engelliyor. VPN kullanıyorsanız, VPN'i kapatıp bu sayfayı yeniden yüklemeyi deneyin.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Bu işe yaramazsa, videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player, kişiselleştirilmiş reklamlar olmadan temiz bir görüntüleme deneyimi sağlar ve görüntüleme etkinliğinin YouTube önerilerinizi etkilemesini önler."
}
diff --git a/build/android/pages/duckplayer/dist/index.css b/build/android/pages/duckplayer/dist/index.css
index cff98b790..7e8fbf3a0 100644
--- a/build/android/pages/duckplayer/dist/index.css
+++ b/build/android/pages/duckplayer/dist/index.css
@@ -61,6 +61,7 @@ body[data-display=app] {
/* pages/duckplayer/app/components/Components.module.css */
.Components_main {
+ background-color: #000;
color: white;
max-width: 3840px;
margin: 0 auto;
@@ -112,7 +113,7 @@ body[data-display=app] {
text-decoration: none;
}
[data-layout=mobile] .Button_button {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
.Button_button:hover,
.Button_button:focus-visible {
@@ -188,7 +189,7 @@ body[data-display=app] {
.SwitchBarMobile_switchBar {
display: grid;
border-radius: 8px;
- background: #2f2f2f;
+ background: rgba(255, 255, 255, 0.12);
padding-inline: 16px;
height: 100%;
line-height: 1.1;
@@ -518,6 +519,9 @@ body[data-display=app] {
.Wordmark_mobile_logo {
height: 100px;
}
+ [data-youtube-error=true] .Wordmark_mobile_logo {
+ height: 44px;
+ }
}
.Wordmark_mobile_logoSvg img {
display: block;
@@ -589,6 +593,127 @@ body[data-display=app] {
}
}
+/* pages/duckplayer/app/components/YouTubeError.module.css */
+.YouTubeError_error {
+ align-items: center;
+ background: rgba(0, 0, 0, 0.6);
+ display: grid;
+ height: 100%;
+ justify-items: center;
+}
+.YouTubeError_error.YouTubeError_desktop {
+ height: var(--frame-height);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+.YouTubeError_error.YouTubeError_mobile {
+ border-radius: var(--inner-radius);
+ height: 100%;
+ overflow: auto;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_error.YouTubeError_mobile {
+ aspect-ratio: 16 / 9;
+ }
+}
+.YouTubeError_desktop {
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.YouTubeError_container {
+ column-gap: 24px;
+ display: flex;
+ flex-flow: row;
+ margin: 0;
+ max-width: 680px;
+ padding: 0 40px;
+ row-gap: 4px;
+}
+.YouTubeError_mobile .YouTubeError_container {
+ flex-flow: column;
+ padding: 0 24px;
+}
+@media screen and (min-height: 320px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 16px 0;
+ }
+}
+@media screen and (min-width: 375px) and (min-height: 400px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 36px 0;
+ }
+}
+.YouTubeError_content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 16px 0;
+}
+@media screen and (min-width: 600px) {
+ .YouTubeError_content {
+ margin: 24px 0;
+ }
+}
+.YouTubeError_icon {
+ align-self: center;
+ display: flex;
+ justify-content: center;
+}
+.YouTubeError_icon::before {
+ content: " ";
+ display: block;
+ background: url('data:image/svg+xml,%0A') no-repeat;
+ height: 48px;
+ width: 48px;
+}
+@media screen and (max-width: 320px) {
+ .YouTubeError_icon {
+ display: none;
+ }
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_icon {
+ justify-content: start;
+ }
+ .YouTubeError_icon::before {
+ background-image: url('data:image/svg+xml,%0A');
+ height: 96px;
+ width: 128px;
+ }
+}
+.YouTubeError_heading {
+ color: #fff;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: calc(24 / 20);
+ margin: 0;
+}
+.YouTubeError_messages {
+ color: #ccc;
+ font-size: 16px;
+ line-height: calc(24 / 16);
+}
+div.YouTubeError_messages {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+div.YouTubeError_messages p {
+ margin: 0;
+}
+p.YouTubeError_messages {
+ margin: 0;
+}
+ul.YouTubeError_messages li {
+ list-style: disc;
+ margin-left: 24px;
+}
+
/* pages/duckplayer/app/components/MobileApp.module.css */
body[data-display=app] {
padding: 8px;
@@ -626,6 +751,7 @@ html[data-focus-mode=on] .MobileApp_hideInFocus {
--inner-radius: 12px;
--logo-width: 157px;
--inner-padding: 8px;
+ --mobile-buttons-padding: 8px;
position: relative;
max-width: 100vh;
margin: 0 auto;
@@ -681,6 +807,15 @@ html[data-focus-mode=on] .MobileApp_embed {
grid-area: switch;
height: 44px;
}
+.MobileApp_detachedControls {
+ grid-area: detached;
+ display: flex;
+ flex-flow: column;
+ gap: 8px;
+ padding: 8px;
+ background: #2f2f2f;
+ border-radius: 12px;
+}
@media screen and (min-width: 425px) and (max-height: 600px) {
.MobileApp_main {
grid-template-rows: max-content auto max-content max-content 12px max-content auto;
@@ -774,6 +909,71 @@ html[data-focus-mode=on] .MobileApp_embed {
justify-content: end;
}
}
+@media screen and (max-width: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ --bg-color: transparent;
+ --inner-padding: 4px;
+ grid-template-areas: "logo" "gap3" "embed" "gap4" "switch" "buttons";
+ grid-template-rows: max-content 16px auto 12px max-content max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ background: #2f2f2f;
+ border-radius: var(--outer-radius);
+ padding: 4px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_switch {
+ background: #2f2f2f;
+ padding: 8px 8px 0 8px;
+ height: 60px;
+ max-height: 60px;
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ background: #2f2f2f;
+ padding: 8px;
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_switch {
+ background: transparent;
+ max-height: 0;
+ }
+}
+@media screen and (max-width: 599px) and (max-height: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ max-width: unset;
+ grid-template-rows: 0 0 auto 12px 0 max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] :is(.MobileApp_logo, .MobileApp_switch) {
+ display: none;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+}
+@media screen and (min-width: 600px) and (max-height: 450px) {
+ .MobileApp_main[data-youtube-error=true] {
+ grid-template-areas: "embed" "buttons" "gap5";
+ grid-template-rows: auto max-content 8px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ display: block;
+ }
+}
+@media screen and (max-height: 320px) {
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ overflow-y: auto;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ bottom: 0;
+ position: sticky;
+ }
+}
/* pages/duckplayer/app/components/MobileButtons.module.css */
.MobileButtons_buttons {
diff --git a/build/android/pages/duckplayer/dist/index.js b/build/android/pages/duckplayer/dist/index.js
index ac97968e3..f6486c5da 100644
--- a/build/android/pages/duckplayer/dist/index.js
+++ b/build/android/pages/duckplayer/dist/index.js
@@ -1982,12 +1982,33 @@
title: "ERROR: Invalid video id",
note: "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ blockedVideoErrorHeading: {
+ title: "YouTube won\u2019t let Duck Player load this video",
+ note: "Message shown when YouTube has blocked playback of a video"
+ },
+ blockedVideoErrorMessage1: {
+ title: "YouTube doesn\u2019t allow this video to be viewed outside of YouTube.",
+ note: "Explanation on why the error is happening."
+ },
+ blockedVideoErrorMessage2: {
+ title: "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ signInRequiredErrorMessage1: {
+ title: "YouTube is blocking this video from loading. If you\u2019re using a VPN, try turning it off and reloading this page.",
+ note: "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ signInRequiredErrorMessage2: {
+ title: "If this doesn\u2019t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "More troubleshooting tips for this specific error"
+ },
tooltipInfo: {
title: "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
};
// pages/duckplayer/app/settings.js
+ var DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
var Settings = class _Settings {
/**
* @param {object} params
@@ -1995,17 +2016,20 @@
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
+ * @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
*/
constructor({
platform = { name: "macos" },
pip = { state: "disabled" },
autoplay = { state: "enabled" },
- focusMode = { state: "enabled" }
+ focusMode = { state: "enabled" },
+ customError = { state: "disabled", signInRequiredSelector: "" }
}) {
this.platform = platform;
this.pip = pip;
this.autoplay = autoplay;
this.focusMode = focusMode;
+ this.customError = customError;
}
/**
* @param {keyof import("../types/duckplayer.js").DuckPlayerPageSettings} named
@@ -2014,7 +2038,7 @@
*/
withFeatureState(named, settings) {
if (!settings) return this;
- const valid = ["pip", "autoplay", "focusMode"];
+ const valid = ["pip", "autoplay", "focusMode", "customError"];
if (!valid.includes(named)) {
console.warn(`Excluding invalid feature key ${named}`);
return this;
@@ -2053,6 +2077,28 @@
}
return this;
}
+ /**
+ * @param {string|null|undefined} newState
+ * @return {Settings}
+ */
+ withCustomError(newState) {
+ if (newState === "disabled") {
+ return new _Settings({
+ ...this,
+ customError: { state: "disabled" }
+ });
+ }
+ if (newState === "enabled") {
+ return new _Settings({
+ ...this,
+ customError: {
+ state: "enabled",
+ signOnRequiredSelector: DEFAULT_SIGN_IN_REQURED_HREF
+ }
+ });
+ }
+ return this;
+ }
/**
* @return {string}
*/
@@ -2959,6 +3005,162 @@
}
};
+ // pages/duckplayer/app/providers/YouTubeErrorProvider.jsx
+ var YOUTUBE_ERROR_EVENT = "ddg-duckplayer-youtube-error";
+ var YOUTUBE_ERRORS = {
+ ageRestricted: "age-restricted",
+ signInRequired: "sign-in-required",
+ noEmbed: "no-embed",
+ unknown: "unknown"
+ };
+ var YOUTUBE_ERROR_IDS = Object.values(YOUTUBE_ERRORS);
+ var YouTubeErrorContext = J({
+ /** @type {YouTubeError|null} */
+ error: null
+ });
+ function YouTubeErrorProvider({ initial = null, children }) {
+ let initialError = null;
+ if (initial && YOUTUBE_ERROR_IDS.includes(initial)) {
+ initialError = initial;
+ }
+ const [error, setError] = h2(initialError);
+ const messaging2 = useMessaging();
+ const platformName = usePlatformName();
+ const setFocusMode = useSetFocusMode();
+ y2(() => {
+ const errorEventHandler = (event) => {
+ const eventError = event.detail?.error;
+ if (YOUTUBE_ERROR_IDS.includes(eventError) || eventError === null) {
+ if (eventError && eventError !== error) {
+ setFocusMode("paused");
+ if (platformName === "macos" || platformName === "ios") {
+ messaging2.reportYouTubeError({ error: eventError });
+ }
+ } else {
+ setFocusMode("enabled");
+ }
+ setError(eventError);
+ }
+ };
+ window.addEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ return () => window.removeEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ }, []);
+ return /* @__PURE__ */ g(YouTubeErrorContext.Provider, { value: { error } }, children);
+ }
+ function useYouTubeError() {
+ return x2(YouTubeErrorContext).error;
+ }
+
+ // pages/duckplayer/app/features/error-detection.js
+ var ErrorDetection = class {
+ /** @type {HTMLIFrameElement} */
+ iframe;
+ /** @type {CustomErrorOptions} */
+ options;
+ /**
+ * @param {CustomErrorOptions} options
+ */
+ constructor(options) {
+ this.options = options;
+ }
+ /**
+ * @param {HTMLIFrameElement} iframe
+ */
+ iframeDidLoad(iframe) {
+ this.iframe = iframe;
+ if (!this.options || !this.options.signInRequiredSelector) {
+ console.log("Missing Custom Error options");
+ return null;
+ }
+ const documentBody = iframe.contentWindow?.document?.body;
+ if (documentBody) {
+ if (this.checkForError(documentBody)) {
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ return null;
+ }
+ const observer = new MutationObserver(this.handleMutation.bind(this));
+ observer.observe(documentBody, {
+ childList: true,
+ subtree: true
+ // Observe all descendants of the body
+ });
+ return () => {
+ observer.disconnect();
+ };
+ }
+ return null;
+ }
+ /**
+ * Mutation handler that checks new nodes for error states
+ *
+ * @type {MutationCallback}
+ */
+ handleMutation(mutationsList) {
+ for (const mutation of mutationsList) {
+ if (mutation.type === "childList") {
+ mutation.addedNodes.forEach((node) => {
+ if (this.checkForError(node)) {
+ console.log("A node with an error has been added to the document:", node);
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ }
+ });
+ }
+ }
+ }
+ /**
+ * Attempts to detect the type of error in the YouTube embed iframe
+ * @returns {YouTubeError}
+ */
+ getErrorType() {
+ const iframeWindow = (
+ /** @type {Window & { ytcfg: object }} */
+ this.iframe.contentWindow
+ );
+ let playerResponse;
+ try {
+ playerResponse = JSON.parse(iframeWindow.ytcfg?.get("PLAYER_VARS")?.embedded_player_response);
+ } catch (e3) {
+ console.log("Could not parse player response", e3);
+ }
+ if (typeof playerResponse === "object") {
+ const {
+ previewPlayabilityStatus: { desktopLegacyAgeGateReason, status }
+ } = playerResponse;
+ if (status === "UNPLAYABLE") {
+ if (desktopLegacyAgeGateReason === 1) {
+ return YOUTUBE_ERRORS.ageRestricted;
+ }
+ return YOUTUBE_ERRORS.noEmbed;
+ }
+ try {
+ if (this.options?.signInRequiredSelector && !!iframeWindow.document.querySelector(this.options.signInRequiredSelector)) {
+ return YOUTUBE_ERRORS.signInRequired;
+ }
+ } catch (e3) {
+ console.log("Sign-in required query failed", e3);
+ }
+ }
+ return YOUTUBE_ERRORS.unknown;
+ }
+ /**
+ * Analyses a node and its children to determine if it contains an error state
+ *
+ * @param {Node} [node]
+ */
+ checkForError(node) {
+ if (node?.nodeType === Node.ELEMENT_NODE) {
+ const element = (
+ /** @type {HTMLElement} */
+ node
+ );
+ return element.classList.contains("ytp-error") || !!element.querySelector("ytp-error");
+ }
+ return false;
+ }
+ };
+
// pages/duckplayer/app/features/iframe.js
var IframeFeature = class {
/**
@@ -3015,6 +3217,12 @@
*/
mouseCapture: () => {
return new MouseCapture();
+ },
+ /**
+ * @return {IframeFeature}
+ */
+ errorDetection: () => {
+ return new ErrorDetection(settings.customError);
}
};
}
@@ -3083,7 +3291,8 @@
features.pip(),
features.clickCapture(),
features.titleCapture(),
- features.mouseCapture()
+ features.mouseCapture(),
+ features.errorDetection()
];
const cleanups = [];
const loadHandler = () => {
@@ -3110,6 +3319,48 @@
return { ref, didLoad: () => didLoad.current = true };
}
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ var import_classnames10 = __toESM(require_classnames(), 1);
+
+ // pages/duckplayer/app/components/YouTubeError.module.css
+ var YouTubeError_default = {
+ error: "YouTubeError_error",
+ desktop: "YouTubeError_desktop",
+ mobile: "YouTubeError_mobile",
+ container: "YouTubeError_container",
+ content: "YouTubeError_content",
+ icon: "YouTubeError_icon",
+ heading: "YouTubeError_heading",
+ messages: "YouTubeError_messages"
+ };
+
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ function useErrorStrings(kind) {
+ const { t: t3 } = useTypedTranslation();
+ switch (kind) {
+ case "sign-in-required":
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("signInRequiredErrorMessage1"), t3("signInRequiredErrorMessage2")],
+ variant: "paragraphs"
+ };
+ default:
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("blockedVideoErrorMessage1"), t3("blockedVideoErrorMessage2")],
+ variant: "paragraphs"
+ };
+ }
+ }
+ function YouTubeError({ kind, layout }) {
+ const { heading, messages, variant } = useErrorStrings(kind);
+ const classes = (0, import_classnames10.default)(YouTubeError_default.error, {
+ [YouTubeError_default.desktop]: layout === "desktop",
+ [YouTubeError_default.mobile]: layout === "mobile"
+ });
+ return /* @__PURE__ */ g("div", { className: classes }, /* @__PURE__ */ g("div", { className: YouTubeError_default.container }, /* @__PURE__ */ g("span", { className: YouTubeError_default.icon }), /* @__PURE__ */ g("div", { className: YouTubeError_default.content }, /* @__PURE__ */ g("h1", { className: YouTubeError_default.heading }, heading), messages && variant === "inline" && /* @__PURE__ */ g("p", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("span", { key: item }, item))), messages && variant === "paragraphs" && /* @__PURE__ */ g("div", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("p", { key: item }, item))), messages && variant === "list" && /* @__PURE__ */ g("ul", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("li", { key: item }, item))))));
+ }
+
// pages/duckplayer/app/components/Components.jsx
function Components() {
const settings = new Settings({
@@ -3118,11 +3369,11 @@
let embed = EmbedSettings.fromHref("https://localhost?videoID=123");
let url = embed?.toEmbedUrl();
if (!url) throw new Error("unreachable");
- return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
+ return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "sign-in-required" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "no-embed" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "sign-in-required" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "no-embed" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
}
// pages/duckplayer/app/components/MobileApp.jsx
- var import_classnames10 = __toESM(require_classnames(), 1);
+ var import_classnames11 = __toESM(require_classnames(), 1);
// pages/duckplayer/app/components/MobileApp.module.css
var MobileApp_default = {
@@ -3133,7 +3384,8 @@
switch: "MobileApp_switch",
embed: "MobileApp_embed",
logo: "MobileApp_logo",
- buttons: "MobileApp_buttons"
+ buttons: "MobileApp_buttons",
+ detachedControls: "MobileApp_detachedControls"
};
// pages/duckplayer/app/features/app.js
@@ -3231,11 +3483,13 @@
function MobileApp({ embed }) {
const settings = useSettings();
const telemetry2 = useTelemetry();
+ const youtubeError = useYouTubeError();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g(
+ return /* @__PURE__ */ g(k, null, !youtubeError && features.focusMode(), /* @__PURE__ */ g(
OrientationProvider,
{
onChange: (orientation) => {
+ if (youtubeError) return;
if (orientation === "portrait") {
return FocusMode.enable();
}
@@ -3251,7 +3505,10 @@
}
function MobileLayout({ embed }) {
const platformName = usePlatformName();
- return /* @__PURE__ */ g("main", { class: MobileApp_default.main }, /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("main", { class: MobileApp_default.main, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
}
// pages/duckplayer/app/components/DesktopApp.module.css
@@ -3272,10 +3529,14 @@
function DesktopApp({ embed }) {
const settings = useSettings();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app }, /* @__PURE__ */ g(DesktopLayout, { embed })));
+ const youtubeError = useYouTubeError();
+ return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g(DesktopLayout, { embed })));
}
function DesktopLayout({ embed }) {
- return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
}
// pages/duckplayer/app/index.js
@@ -3291,7 +3552,11 @@
console.log("locale:", environment.locale);
document.body.dataset.display = environment.display;
const strings = environment.locale === "en" ? duckplayer_default : await getTranslationsFromStringOrLoadDynamically(init2.localeStrings, environment.locale) || duckplayer_default;
- const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode"));
+ const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withFeatureState("customError", init2.settings.customError).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode")).withCustomError(baseEnvironment2.urlParams.get("customError"));
+ const initialYouTubeError = (
+ /** @type {YouTubeError} */
+ baseEnvironment2.urlParams.get("youtubeError")
+ );
console.log(settings);
const embed = createEmbedSettings(window.location.href, settings);
const didCatch = (error) => {
@@ -3303,7 +3568,7 @@
if (!root) throw new Error("could not render, root element missing");
if (environment.display === "app") {
D(
- /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
+ /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(YouTubeErrorProvider, { initial: initialYouTubeError }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
TranslationProvider,
{
translationObject: duckplayer_default,
@@ -3319,7 +3584,7 @@
textLength: environment.textLength
},
/* @__PURE__ */ g(MobileApp, { embed })
- ), /* @__PURE__ */ g(WillThrow, null))))))),
+ ), /* @__PURE__ */ g(WillThrow, null)))))))),
root
);
} else if (environment.display === "components") {
@@ -3467,6 +3732,13 @@
onUserValuesChanged(cb) {
return this.messaging.subscribe("onUserValuesChanged", cb);
}
+ /**
+ * This will be sent if the application fails to load.
+ * @param {{error: import('../types/duckplayer.ts').YouTubeError}} params
+ */
+ reportYouTubeError(params) {
+ this.messaging.notify("reportYouTubeError", params);
+ }
/**
* This will be sent if the application has loaded, but a client-side error
* has occurred that cannot be recovered from
diff --git a/build/android/pages/duckplayer/locales/bg/duckplayer.json b/build/android/pages/duckplayer/locales/bg/duckplayer.json
index d7f3d20ef..c807a2f78 100644
--- a/build/android/pages/duckplayer/locales/bg/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/bg/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ГРЕШКА: невалиден идентификатор на видеоклипа",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube няма да позволи на Duck Player да зареди това видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволява това видео да бъде гледано извън YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Все пак можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокира зареждането на това видео. Ако използвате VPN, опитайте да го изключите и презаредете тази страница.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ако това не свърши работа, можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player осигурява чисто изживяване без персонализирани реклами в YouTube и предотвратява влиянието на вече гледаните видеоклипове върху препоръките на YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/cs/duckplayer.json b/build/android/pages/duckplayer/locales/cs/duckplayer.json
index 8d24c5726..690569a5d 100644
--- a/build/android/pages/duckplayer/locales/cs/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/cs/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatné ID videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovoluje přehrávači Duck Player načíst tohle video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nedovoluje spuštění videa mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Na tohle video se můžeš pořád podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítání tohohle videa. Pokud používáš VPN, zkus ji vypnout a stránku znovu načíst.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Pokud to nefunguje, můžeš se na video podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Přehrávač Duck Player nabízí sledování v minimalistickém prostředí bez personalizovaných reklam a brání tomu, aby sledovaná videa ovlivňovala tvoje doporučení na YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/da/duckplayer.json b/build/android/pages/duckplayer/locales/da/duckplayer.json
index f99ab298e..aae6004c2 100644
--- a/build/android/pages/duckplayer/locales/da/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/da/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEJL: Ugyldigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube vil ikke lade Duck Player indlæse denne video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillader ikke, at denne video vises uden for YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokerer for, at denne video kan indlæses. Hvis du bruger en VPN, så prøv at slå den fra og genindlæse denne side.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis dette ikke virker, kan du stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player giver en ren seeroplevelse uden målrettede annoncer og forhindrer, at visningsaktivitet påvirker dine YouTube-anbefalinger."
}
diff --git a/build/android/pages/duckplayer/locales/de/duckplayer.json b/build/android/pages/duckplayer/locales/de/duckplayer.json
index 0ddca103a..abd163119 100644
--- a/build/android/pages/duckplayer/locales/de/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/de/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEHLER: Ungültige Video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lässt den Duck Player dieses Video nicht laden",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube erlaubt nicht, dass dieses Video außerhalb von YouTube angesehen wird.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kannst dieses Video auf YouTube ansehen, aber ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockiert das Laden dieses Videos. Falls du ein VPN benutzt, deaktiviere es und lade diese Seite neu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Falls das nicht funktioniert, kannst du das Video dennoch auf YouTube ansehen, jedoch ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Mit Duck Player kannst du dir ungestört und ohne personalisierte Werbung Inhalte ansehen. Er verhindert, dass das, was du dir ansiehst, deine YouTube-Empfehlungen beeinflussen."
}
diff --git a/build/android/pages/duckplayer/locales/el/duckplayer.json b/build/android/pages/duckplayer/locales/el/duckplayer.json
index d00195f5e..2d8fa43c5 100644
--- a/build/android/pages/duckplayer/locales/el/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/el/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ΣΦΑΛΜΑ: Μη έγκυρο αναγνωριστικό βίντεο",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "Το YouTube δεν θα αφήσει το Duck Player να φορτώσει το βίντεο αυτό",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Το YouTube δεν επιτρέπει την προβολή αυτού του βίντεο εκτός YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Μπορείτε ακόμα να παρακολουθήσετε αυτό το βίντεο στο YouTube, αλλά χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "Το YouTube μπλοκάρει τη φόρτωση αυτού του βίντεο. Εάν χρησιμοποιείτε VPN, δοκιμάστε να το απενεργοποιήσετε και να φορτώσετε εκ νέου αυτήν τη σελίδα.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Εάν δεν λειτουργήσει αυτό, μπορείτε να παρακολουθήσετε αυτό το βίντεο στο YouTube, ωστόσο χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Το Duck Player παρέχει μια καθαρή εμπειρία προβολής χωρίς εξατομικευμένες διαφημίσεις, ενώ εμποδίζει τη δραστηριότητα προβολής να επηρεάσει τις συστάσεις που θα λαμβάνετε στο YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/en/duckplayer.json b/build/android/pages/duckplayer/locales/en/duckplayer.json
index c2b5683b9..fe94917c4 100644
--- a/build/android/pages/duckplayer/locales/en/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/en/duckplayer.json
@@ -33,6 +33,26 @@
"title": "ERROR: Invalid video id",
"note": "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading": {
+ "title": "YouTube won’t let Duck Player load this video",
+ "note": "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1": {
+ "title": "YouTube doesn’t allow this video to be viewed outside of YouTube.",
+ "note": "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2": {
+ "title": "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1": {
+ "title": "YouTube is blocking this video from loading. If you’re using a VPN, try turning it off and reloading this page.",
+ "note": "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2": {
+ "title": "If this doesn’t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "More troubleshooting tips for this specific error"
+ },
"tooltipInfo": {
"title": "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
diff --git a/build/android/pages/duckplayer/locales/es/duckplayer.json b/build/android/pages/duckplayer/locales/es/duckplayer.json
index 1b5d8b958..f5af0d046 100644
--- a/build/android/pages/duckplayer/locales/es/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/es/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERROR: ID de vídeo no válida",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube no permite que Duck Player cargue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube no permite que este vídeo se vea fuera de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional que ofrece Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube está bloqueando la carga de este vídeo. Si estás usando una VPN, intenta desactivarla y volver a cargar la página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si esto no funciona, sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ofrece una experiencia de visualización limpia sin anuncios personalizados e impide que la actividad de visualización influya en tus recomendaciones de YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/et/duckplayer.json b/build/android/pages/duckplayer/locales/et/duckplayer.json
index c9863f4f5..70b30efee 100644
--- a/build/android/pages/duckplayer/locales/et/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/et/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIGA: vale video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei luba Duck Playeril seda videot laadida",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei luba seda videot väljaspool YouTube'i vaadata.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Saate seda videot endiselt YouTube'is vaadata, kuid ilma Duck Player'i lisatud privaatsuseta.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokeerib selle video laadimise. Kui kasutate VPN-i, proovige see välja lülitada ning leht uuesti laadida.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Kui see ei aita, saate seda videot ikkagi YouTube'is vaadata, kuid ilma Duck Playeri lisatud privaatsuseta.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pakub isikupärastatud reklaamidest vaba vaatamiskogemust ja takistab, et vaatamisaktiivsus mõjutaks sinu YouTube'i soovitusi."
}
diff --git a/build/android/pages/duckplayer/locales/fi/duckplayer.json b/build/android/pages/duckplayer/locales/fi/duckplayer.json
index e73022b8f..4c830a739 100644
--- a/build/android/pages/duckplayer/locales/fi/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/fi/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIRHE: virheellinen videotunnus",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei salli Duck Playerin ladata tätä videota.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei salli tämän videon katsomista YouTuben ulkopuolella.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Voit yhä katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube estää tämän videon latautumisen. Jos käytät VPN:ää, kytke se pois päältä ja lataa tämä sivu uudelleen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jos tämä ei toimi, voit silti katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tarjoaa puhtaan katselukokemuksen ilman kohdennettuja mainoksia ja estää katseluhistoriaa vaikuttamasta YouTube-suosituksiisi."
}
diff --git a/build/android/pages/duckplayer/locales/fr/duckplayer.json b/build/android/pages/duckplayer/locales/fr/duckplayer.json
index 716f0c071..12eb0ac92 100644
--- a/build/android/pages/duckplayer/locales/fr/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/fr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERREUR : identifiant vidéo non valide",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne permet pas à Duck Player de charger cette vidéo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube n'autorise pas le visionnage de cette vidéo en dehors de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloque le chargement de cette vidéo. Si vous utilisez un VPN, essayez de le désactiver et de recharger cette page.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si cela ne fonctionne pas, vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre une expérience de visionnage épurée, sans publicités personnalisées, et empêche l'activité de visionnage d'influencer vos recommandations YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/hr/duckplayer.json b/build/android/pages/duckplayer/locales/hr/duckplayer.json
index 3f0e8aeae..48fdbf997 100644
--- a/build/android/pages/duckplayer/locales/hr/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/hr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "POGREŠKA: Nevažeći ID videozapisa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dopušta Duck Playeru da učita ovaj videozapis.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dopušta da se ovaj videozapis gleda izvan YouTubea.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Još uvijek možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokira učitavanje ovog videozapisa. Ako koristiš VPN, pokušaj ga isključiti i ponovno učitati ovu stranicu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ako to ne uspije, i dalje možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pruža čisti doživljaj gledanja bez personaliziranih oglasa i sprječava da aktivnosti gledanja utječu na tvoje preporuke na YouTubeu."
}
diff --git a/build/android/pages/duckplayer/locales/hu/duckplayer.json b/build/android/pages/duckplayer/locales/hu/duckplayer.json
index 3bbe06210..c8faf475f 100644
--- a/build/android/pages/duckplayer/locales/hu/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/hu/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HIBA: Érvénytelen videoazonosító",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "A YouTube nem engedi, hogy a Duck Player betöltse ezt a videót",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "A YouTube nem engedi, hogy ezt a videót a YouTube-on kívül nézd meg.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Megnézheted a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "A YouTube blokkolja ennek a videónak a betöltését. Ha VPN-t használsz, próbáld meg, hogy kikapcsolod, majd újra betöltöd ezt az oldalt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ha ez nem működik, akkor is megnézheted ezt a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "A Duck Player személyre szabott hirdetések nélküli, letisztult megtekintési élményt nyújt, és megakadályozza, hogy a megtekintési tevékenységed befolyásolja a neked szóló YouTube-ajánlásokat."
}
diff --git a/build/android/pages/duckplayer/locales/it/duckplayer.json b/build/android/pages/duckplayer/locales/it/duckplayer.json
index 9ce216677..464303fca 100644
--- a/build/android/pages/duckplayer/locales/it/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/it/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRORE: ID video non valido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube non consente a Duck Player di caricare questo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Questo video si può vedere solo su YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Puoi ancora guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube sta impedendo il caricamento di questo video. Se stai utilizzando una VPN, prova a disattivarla e a ricaricare questa pagina.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se il problema non si risolve, puoi comunque guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre un'esperienza di visualizzazione pulita, senza annunci personalizzati, e impedisce che l'attività di visualizzazione incida sulle raccomandazioni di YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/lt/duckplayer.json b/build/android/pages/duckplayer/locales/lt/duckplayer.json
index 1b282dd2c..c8dd25e38 100644
--- a/build/android/pages/duckplayer/locales/lt/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/lt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KLAIDA: netinkamas vaizdo įrašo ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "„YouTube“ neleidžia „Duck Player“ įkelti šio vaizdo įrašo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "„YouTube“ neleidžia šio vaizdo įrašo žiūrėti ne „YouTube“ platformoje.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šį vaizdo įrašą vis dar gali žiūrėti „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "„YouTube“ blokuoja šio vaizdo įrašo įkėlimą. Jei naudoji VPN, pabandyk jį išjungti ir iš naujo įkelti šį puslapį.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jei tai neveikia, vis tiek gali žiūrėti šį vaizdo įrašą „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "„Duck Player“ užtikrina nepriekaištingą žiūrėjimo patirtį be suasmenintų reklamų ir neleidžia žiūrėjimo veiklai daryti įtakos „YouTube“ rekomendacijoms."
}
diff --git a/build/android/pages/duckplayer/locales/lv/duckplayer.json b/build/android/pages/duckplayer/locales/lv/duckplayer.json
index 46d0bee8e..3ae376c31 100644
--- a/build/android/pages/duckplayer/locales/lv/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/lv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KĻŪDA: Nederīgs video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube neļauj Duck Player ielādēt šo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube neļauj skatīties šo video ārpus YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šo videoklipu joprojām vari skatīties vietnē YouTube, taču bez papildu Duck Player konfidencialitātes.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloķē šī video ielādi. Ja tu izmanto VPN, mēģini to izslēgt un pārlādēt šo lapu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ja tas nedarbojas, joprojām vari skatīties šo video vietnē YouTube, taču bez papildu privātuma, ko nodrošina Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player nodrošina netraucētu skatīšanās pieredzi bez personalizētām reklāmām un neļauj skatīšanās darbībām ietekmēt tavus YouTube ieteikumus."
}
diff --git a/build/android/pages/duckplayer/locales/nb/duckplayer.json b/build/android/pages/duckplayer/locales/nb/duckplayer.json
index 4c1d826f6..fef475447 100644
--- a/build/android/pages/duckplayer/locales/nb/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/nb/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEIL: Ugyldig video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lar ikke Duck Player laste denne videoen",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillater ikke visning av denne videoen utenfor YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fremdeles se videoen på YouTube, men uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkerer denne videoen fra å lastes. Hvis du bruker en VPN, kan du prøve å slå den av og laste denne siden på nytt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis ikke det virker, kan du fremdeles se videoen på YouTube, bare uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tilbyr en ren seeropplevelse uten tilpassede annonser og forhindrer at seeraktiviteten din påvirker YouTube-anbefalingene dine."
}
diff --git a/build/android/pages/duckplayer/locales/nl/duckplayer.json b/build/android/pages/duckplayer/locales/nl/duckplayer.json
index a1be8669e..51a0ece78 100644
--- a/build/android/pages/duckplayer/locales/nl/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/nl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FOUT: ongeldige video-id",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube staat Duck Player niet toe om deze video te laden.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube staat niet toe dat je deze video buiten YouTube bekijkt.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Je kunt deze video nog steeds bekijken op YouTube, maar zonder de extra privacy van Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkeert het laden van deze video. Als je een VPN gebruikt, probeer deze dan uit te schakelen en deze pagina opnieuw te laden.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Als dit niet werkt, kun je deze video nog steeds op YouTube bekijken, maar dan zonder de extra privacy van Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player biedt puur kijkplezier zonder gepersonaliseerde advertenties en voorkomt dat de dingen die je bekijkt je YouTube-aanbevelingen beïnvloeden."
}
diff --git a/build/android/pages/duckplayer/locales/pl/duckplayer.json b/build/android/pages/duckplayer/locales/pl/duckplayer.json
index accd3fde5..8b633d415 100644
--- a/build/android/pages/duckplayer/locales/pl/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/pl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "BŁĄD: nieprawidłowy identyfikator filmu",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nie zezwala na załadowanie tego filmu przez Duck Player",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nie zezwala na oglądanie tego filmu poza YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Nadal możesz oglądać ten film na YouTube, ale bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje ładowanie tego filmu. Jeśli korzystasz z sieci VPN, spróbuj ją wyłączyć i ponownie załadować tę stronę.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jeśli to nie pomoże, nadal możesz oglądać ten film na YouTube, jednak bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player zapewnia czyste środowisko oglądania bez spersonalizowanych reklam i sprawia, że aktywność związana z oglądaniem filmów nie wpływa na rekomendacje YouTube'a."
}
diff --git a/build/android/pages/duckplayer/locales/pt/duckplayer.json b/build/android/pages/duckplayer/locales/pt/duckplayer.json
index a5bfca188..2ff6fad9c 100644
--- a/build/android/pages/duckplayer/locales/pt/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/pt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRO: ID de vídeo inválido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "O YouTube não permite que o Duck Player carregue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "O YouTube não permite que vejas este vídeo fora do YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "O YouTube está a bloquear o carregamento deste vídeo. Se estiveres a usar uma VPN, tenta desativá-la e recarregar esta página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se isto não funcionar, continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "O Duck Player oferece uma experiência de visualização limpa sem anúncios personalizados e evita que as atividades de visualização influenciem as recomendações do YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/ro/duckplayer.json b/build/android/pages/duckplayer/locales/ro/duckplayer.json
index bfafec70e..25ffac535 100644
--- a/build/android/pages/duckplayer/locales/ro/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/ro/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "EROARE: ID video incorect",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nu permite Duck Player să încarce acest videoclip",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nu permite ca acest videoclip să fie vizionat în afara YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blochează încărcarea acestui videoclip. Dacă folosești un VPN, încearcă să-l dezactivezi și să reîncarci această pagină.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Dacă acest lucru nu funcționează, poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player oferă o experiență de vizionare fără perturbări, fără reclame personalizate și împiedică activitatea de vizionare să îți influențeze recomandările YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/ru/duckplayer.json b/build/android/pages/duckplayer/locales/ru/duckplayer.json
index 4bf5cc0c1..d1a3fc528 100644
--- a/build/android/pages/duckplayer/locales/ru/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/ru/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ОШИБКА: Неверный идентификатор видео",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube не позволяет проигрывателю Duck Player загрузить это видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволяет смотреть это видео вне своей платформы.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Вы по-прежнему можете посмотреть этот ролик на YouTube, но уже без дополнительной защиты Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокирует загрузку этого видео. Если вы используете VPN, отключите ее и перезагрузите страницу.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Если это не даст результата, вы все равно сможете просмотреть это видео на YouTube, но без дополнительной защиты конфиденциальности, обеспечиваемой Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Проигрыватель Duck Player обеспечивает беспрепятственный просмотр без персонализированной рекламы и влияния просмотренных роликов на рекомендации в YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/sk/duckplayer.json b/build/android/pages/duckplayer/locales/sk/duckplayer.json
index 88a2cc3a5..a68cdeb89 100644
--- a/build/android/pages/duckplayer/locales/sk/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/sk/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatný identifikátor videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovolí Duck Playeru načítať toto video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nepovoľuje, aby sa toto video pozeralo mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Toto video si môžeš pozrieť aj na YouTube, ale bez dodatočného súkromia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítanie tohto videa. Ak používaš sieť VPN, skús ju vypnúť a znova načítať túto stránku.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ak to nefunguje, môžeš si toto video pozrieť aj na YouTube, ale bez dodatočnej ochrany súkromia, ktorú poskytuje Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player poskytuje čisté zobrazenie bez personalizovaných reklám a zabraňuje tomu, aby aktivita pri sledovaní ovplyvňovala vaše odporúčania v službe YouTube."
}
diff --git a/build/android/pages/duckplayer/locales/sl/duckplayer.json b/build/android/pages/duckplayer/locales/sl/duckplayer.json
index 7d4a89155..977830ef4 100644
--- a/build/android/pages/duckplayer/locales/sl/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/sl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "NAPAKA: Neveljaven ID videoposnetka",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dovoli predvajalniku Duck Player naložiti tega videa",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dovoljuje, da si ta videoposnetek ogledate zunaj YouTuba.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Ta videoposnetek si lahko še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube preprečuje nalaganje tega videoposnetka. Če uporabljate omrežje VPN, ga poskusite izklopiti in znova naložite to stran.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Če to ne deluje, si lahko ta videoposnetek še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Predvajalnik Duck Player zagotavlja čisto izkušnjo gledanja brez prilagojenih oglasov in preprečuje, da bi dejavnost gledanja vplivala na vaša priporočila v YouTubu."
}
diff --git a/build/android/pages/duckplayer/locales/sv/duckplayer.json b/build/android/pages/duckplayer/locales/sv/duckplayer.json
index cb5b75e4c..444e91750 100644
--- a/build/android/pages/duckplayer/locales/sv/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/sv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEL: Ogiltigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube låter inte Duck Player läsa in den här videon",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillåter inte att den här videon ses utanför YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockerar den här videon från att läsas in. Om du använder ett VPN kan du prova stänga av det och läsa in sidan igen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Om det inte fungerar kan du fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ger en störningsfri visningsupplevelse utan personliga annonser och förhindrar att din tittaraktivitet påverkar YouTube-rekommendationer."
}
diff --git a/build/android/pages/duckplayer/locales/tr/duckplayer.json b/build/android/pages/duckplayer/locales/tr/duckplayer.json
index c23cab2bb..e733fcaa6 100644
--- a/build/android/pages/duckplayer/locales/tr/duckplayer.json
+++ b/build/android/pages/duckplayer/locales/tr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HATA: Geçersiz video kimliği",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube, Duck Player'ın bu videoyu yüklemesine izin vermiyor",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube, bu videonun YouTube dışında izlenmesine izin vermiyor.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Bu videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bu videonun yüklenmesini engelliyor. VPN kullanıyorsanız, VPN'i kapatıp bu sayfayı yeniden yüklemeyi deneyin.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Bu işe yaramazsa, videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player, kişiselleştirilmiş reklamlar olmadan temiz bir görüntüleme deneyimi sağlar ve görüntüleme etkinliğinin YouTube önerilerinizi etkilemesini önler."
}
diff --git a/build/integration/pages/duckplayer/dist/index.css b/build/integration/pages/duckplayer/dist/index.css
index cff98b790..7e8fbf3a0 100644
--- a/build/integration/pages/duckplayer/dist/index.css
+++ b/build/integration/pages/duckplayer/dist/index.css
@@ -61,6 +61,7 @@ body[data-display=app] {
/* pages/duckplayer/app/components/Components.module.css */
.Components_main {
+ background-color: #000;
color: white;
max-width: 3840px;
margin: 0 auto;
@@ -112,7 +113,7 @@ body[data-display=app] {
text-decoration: none;
}
[data-layout=mobile] .Button_button {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
.Button_button:hover,
.Button_button:focus-visible {
@@ -188,7 +189,7 @@ body[data-display=app] {
.SwitchBarMobile_switchBar {
display: grid;
border-radius: 8px;
- background: #2f2f2f;
+ background: rgba(255, 255, 255, 0.12);
padding-inline: 16px;
height: 100%;
line-height: 1.1;
@@ -518,6 +519,9 @@ body[data-display=app] {
.Wordmark_mobile_logo {
height: 100px;
}
+ [data-youtube-error=true] .Wordmark_mobile_logo {
+ height: 44px;
+ }
}
.Wordmark_mobile_logoSvg img {
display: block;
@@ -589,6 +593,127 @@ body[data-display=app] {
}
}
+/* pages/duckplayer/app/components/YouTubeError.module.css */
+.YouTubeError_error {
+ align-items: center;
+ background: rgba(0, 0, 0, 0.6);
+ display: grid;
+ height: 100%;
+ justify-items: center;
+}
+.YouTubeError_error.YouTubeError_desktop {
+ height: var(--frame-height);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+.YouTubeError_error.YouTubeError_mobile {
+ border-radius: var(--inner-radius);
+ height: 100%;
+ overflow: auto;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_error.YouTubeError_mobile {
+ aspect-ratio: 16 / 9;
+ }
+}
+.YouTubeError_desktop {
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.YouTubeError_container {
+ column-gap: 24px;
+ display: flex;
+ flex-flow: row;
+ margin: 0;
+ max-width: 680px;
+ padding: 0 40px;
+ row-gap: 4px;
+}
+.YouTubeError_mobile .YouTubeError_container {
+ flex-flow: column;
+ padding: 0 24px;
+}
+@media screen and (min-height: 320px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 16px 0;
+ }
+}
+@media screen and (min-width: 375px) and (min-height: 400px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 36px 0;
+ }
+}
+.YouTubeError_content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 16px 0;
+}
+@media screen and (min-width: 600px) {
+ .YouTubeError_content {
+ margin: 24px 0;
+ }
+}
+.YouTubeError_icon {
+ align-self: center;
+ display: flex;
+ justify-content: center;
+}
+.YouTubeError_icon::before {
+ content: " ";
+ display: block;
+ background: url('data:image/svg+xml,%0A') no-repeat;
+ height: 48px;
+ width: 48px;
+}
+@media screen and (max-width: 320px) {
+ .YouTubeError_icon {
+ display: none;
+ }
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_icon {
+ justify-content: start;
+ }
+ .YouTubeError_icon::before {
+ background-image: url('data:image/svg+xml,%0A');
+ height: 96px;
+ width: 128px;
+ }
+}
+.YouTubeError_heading {
+ color: #fff;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: calc(24 / 20);
+ margin: 0;
+}
+.YouTubeError_messages {
+ color: #ccc;
+ font-size: 16px;
+ line-height: calc(24 / 16);
+}
+div.YouTubeError_messages {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+div.YouTubeError_messages p {
+ margin: 0;
+}
+p.YouTubeError_messages {
+ margin: 0;
+}
+ul.YouTubeError_messages li {
+ list-style: disc;
+ margin-left: 24px;
+}
+
/* pages/duckplayer/app/components/MobileApp.module.css */
body[data-display=app] {
padding: 8px;
@@ -626,6 +751,7 @@ html[data-focus-mode=on] .MobileApp_hideInFocus {
--inner-radius: 12px;
--logo-width: 157px;
--inner-padding: 8px;
+ --mobile-buttons-padding: 8px;
position: relative;
max-width: 100vh;
margin: 0 auto;
@@ -681,6 +807,15 @@ html[data-focus-mode=on] .MobileApp_embed {
grid-area: switch;
height: 44px;
}
+.MobileApp_detachedControls {
+ grid-area: detached;
+ display: flex;
+ flex-flow: column;
+ gap: 8px;
+ padding: 8px;
+ background: #2f2f2f;
+ border-radius: 12px;
+}
@media screen and (min-width: 425px) and (max-height: 600px) {
.MobileApp_main {
grid-template-rows: max-content auto max-content max-content 12px max-content auto;
@@ -774,6 +909,71 @@ html[data-focus-mode=on] .MobileApp_embed {
justify-content: end;
}
}
+@media screen and (max-width: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ --bg-color: transparent;
+ --inner-padding: 4px;
+ grid-template-areas: "logo" "gap3" "embed" "gap4" "switch" "buttons";
+ grid-template-rows: max-content 16px auto 12px max-content max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ background: #2f2f2f;
+ border-radius: var(--outer-radius);
+ padding: 4px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_switch {
+ background: #2f2f2f;
+ padding: 8px 8px 0 8px;
+ height: 60px;
+ max-height: 60px;
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ background: #2f2f2f;
+ padding: 8px;
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_switch {
+ background: transparent;
+ max-height: 0;
+ }
+}
+@media screen and (max-width: 599px) and (max-height: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ max-width: unset;
+ grid-template-rows: 0 0 auto 12px 0 max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] :is(.MobileApp_logo, .MobileApp_switch) {
+ display: none;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+}
+@media screen and (min-width: 600px) and (max-height: 450px) {
+ .MobileApp_main[data-youtube-error=true] {
+ grid-template-areas: "embed" "buttons" "gap5";
+ grid-template-rows: auto max-content 8px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ display: block;
+ }
+}
+@media screen and (max-height: 320px) {
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ overflow-y: auto;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ bottom: 0;
+ position: sticky;
+ }
+}
/* pages/duckplayer/app/components/MobileButtons.module.css */
.MobileButtons_buttons {
diff --git a/build/integration/pages/duckplayer/dist/index.js b/build/integration/pages/duckplayer/dist/index.js
index 44d80fa71..a6e01d2b1 100644
--- a/build/integration/pages/duckplayer/dist/index.js
+++ b/build/integration/pages/duckplayer/dist/index.js
@@ -1982,12 +1982,33 @@
title: "ERROR: Invalid video id",
note: "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ blockedVideoErrorHeading: {
+ title: "YouTube won\u2019t let Duck Player load this video",
+ note: "Message shown when YouTube has blocked playback of a video"
+ },
+ blockedVideoErrorMessage1: {
+ title: "YouTube doesn\u2019t allow this video to be viewed outside of YouTube.",
+ note: "Explanation on why the error is happening."
+ },
+ blockedVideoErrorMessage2: {
+ title: "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ signInRequiredErrorMessage1: {
+ title: "YouTube is blocking this video from loading. If you\u2019re using a VPN, try turning it off and reloading this page.",
+ note: "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ signInRequiredErrorMessage2: {
+ title: "If this doesn\u2019t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "More troubleshooting tips for this specific error"
+ },
tooltipInfo: {
title: "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
};
// pages/duckplayer/app/settings.js
+ var DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
var Settings = class _Settings {
/**
* @param {object} params
@@ -1995,17 +2016,20 @@
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
+ * @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
*/
constructor({
platform = { name: "macos" },
pip = { state: "disabled" },
autoplay = { state: "enabled" },
- focusMode = { state: "enabled" }
+ focusMode = { state: "enabled" },
+ customError = { state: "disabled", signInRequiredSelector: "" }
}) {
this.platform = platform;
this.pip = pip;
this.autoplay = autoplay;
this.focusMode = focusMode;
+ this.customError = customError;
}
/**
* @param {keyof import("../types/duckplayer.js").DuckPlayerPageSettings} named
@@ -2014,7 +2038,7 @@
*/
withFeatureState(named, settings) {
if (!settings) return this;
- const valid = ["pip", "autoplay", "focusMode"];
+ const valid = ["pip", "autoplay", "focusMode", "customError"];
if (!valid.includes(named)) {
console.warn(`Excluding invalid feature key ${named}`);
return this;
@@ -2053,6 +2077,28 @@
}
return this;
}
+ /**
+ * @param {string|null|undefined} newState
+ * @return {Settings}
+ */
+ withCustomError(newState) {
+ if (newState === "disabled") {
+ return new _Settings({
+ ...this,
+ customError: { state: "disabled" }
+ });
+ }
+ if (newState === "enabled") {
+ return new _Settings({
+ ...this,
+ customError: {
+ state: "enabled",
+ signOnRequiredSelector: DEFAULT_SIGN_IN_REQURED_HREF
+ }
+ });
+ }
+ return this;
+ }
/**
* @return {string}
*/
@@ -2959,6 +3005,162 @@
}
};
+ // pages/duckplayer/app/providers/YouTubeErrorProvider.jsx
+ var YOUTUBE_ERROR_EVENT = "ddg-duckplayer-youtube-error";
+ var YOUTUBE_ERRORS = {
+ ageRestricted: "age-restricted",
+ signInRequired: "sign-in-required",
+ noEmbed: "no-embed",
+ unknown: "unknown"
+ };
+ var YOUTUBE_ERROR_IDS = Object.values(YOUTUBE_ERRORS);
+ var YouTubeErrorContext = J({
+ /** @type {YouTubeError|null} */
+ error: null
+ });
+ function YouTubeErrorProvider({ initial = null, children }) {
+ let initialError = null;
+ if (initial && YOUTUBE_ERROR_IDS.includes(initial)) {
+ initialError = initial;
+ }
+ const [error, setError] = h2(initialError);
+ const messaging2 = useMessaging();
+ const platformName = usePlatformName();
+ const setFocusMode = useSetFocusMode();
+ y2(() => {
+ const errorEventHandler = (event) => {
+ const eventError = event.detail?.error;
+ if (YOUTUBE_ERROR_IDS.includes(eventError) || eventError === null) {
+ if (eventError && eventError !== error) {
+ setFocusMode("paused");
+ if (platformName === "macos" || platformName === "ios") {
+ messaging2.reportYouTubeError({ error: eventError });
+ }
+ } else {
+ setFocusMode("enabled");
+ }
+ setError(eventError);
+ }
+ };
+ window.addEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ return () => window.removeEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ }, []);
+ return /* @__PURE__ */ g(YouTubeErrorContext.Provider, { value: { error } }, children);
+ }
+ function useYouTubeError() {
+ return x2(YouTubeErrorContext).error;
+ }
+
+ // pages/duckplayer/app/features/error-detection.js
+ var ErrorDetection = class {
+ /** @type {HTMLIFrameElement} */
+ iframe;
+ /** @type {CustomErrorOptions} */
+ options;
+ /**
+ * @param {CustomErrorOptions} options
+ */
+ constructor(options) {
+ this.options = options;
+ }
+ /**
+ * @param {HTMLIFrameElement} iframe
+ */
+ iframeDidLoad(iframe) {
+ this.iframe = iframe;
+ if (!this.options || !this.options.signInRequiredSelector) {
+ console.log("Missing Custom Error options");
+ return null;
+ }
+ const documentBody = iframe.contentWindow?.document?.body;
+ if (documentBody) {
+ if (this.checkForError(documentBody)) {
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ return null;
+ }
+ const observer = new MutationObserver(this.handleMutation.bind(this));
+ observer.observe(documentBody, {
+ childList: true,
+ subtree: true
+ // Observe all descendants of the body
+ });
+ return () => {
+ observer.disconnect();
+ };
+ }
+ return null;
+ }
+ /**
+ * Mutation handler that checks new nodes for error states
+ *
+ * @type {MutationCallback}
+ */
+ handleMutation(mutationsList) {
+ for (const mutation of mutationsList) {
+ if (mutation.type === "childList") {
+ mutation.addedNodes.forEach((node) => {
+ if (this.checkForError(node)) {
+ console.log("A node with an error has been added to the document:", node);
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ }
+ });
+ }
+ }
+ }
+ /**
+ * Attempts to detect the type of error in the YouTube embed iframe
+ * @returns {YouTubeError}
+ */
+ getErrorType() {
+ const iframeWindow = (
+ /** @type {Window & { ytcfg: object }} */
+ this.iframe.contentWindow
+ );
+ let playerResponse;
+ try {
+ playerResponse = JSON.parse(iframeWindow.ytcfg?.get("PLAYER_VARS")?.embedded_player_response);
+ } catch (e3) {
+ console.log("Could not parse player response", e3);
+ }
+ if (typeof playerResponse === "object") {
+ const {
+ previewPlayabilityStatus: { desktopLegacyAgeGateReason, status }
+ } = playerResponse;
+ if (status === "UNPLAYABLE") {
+ if (desktopLegacyAgeGateReason === 1) {
+ return YOUTUBE_ERRORS.ageRestricted;
+ }
+ return YOUTUBE_ERRORS.noEmbed;
+ }
+ try {
+ if (this.options?.signInRequiredSelector && !!iframeWindow.document.querySelector(this.options.signInRequiredSelector)) {
+ return YOUTUBE_ERRORS.signInRequired;
+ }
+ } catch (e3) {
+ console.log("Sign-in required query failed", e3);
+ }
+ }
+ return YOUTUBE_ERRORS.unknown;
+ }
+ /**
+ * Analyses a node and its children to determine if it contains an error state
+ *
+ * @param {Node} [node]
+ */
+ checkForError(node) {
+ if (node?.nodeType === Node.ELEMENT_NODE) {
+ const element = (
+ /** @type {HTMLElement} */
+ node
+ );
+ return element.classList.contains("ytp-error") || !!element.querySelector("ytp-error");
+ }
+ return false;
+ }
+ };
+
// pages/duckplayer/app/features/iframe.js
var IframeFeature = class {
/**
@@ -3015,6 +3217,12 @@
*/
mouseCapture: () => {
return new MouseCapture();
+ },
+ /**
+ * @return {IframeFeature}
+ */
+ errorDetection: () => {
+ return new ErrorDetection(settings.customError);
}
};
}
@@ -3083,7 +3291,8 @@
features.pip(),
features.clickCapture(),
features.titleCapture(),
- features.mouseCapture()
+ features.mouseCapture(),
+ features.errorDetection()
];
const cleanups = [];
const loadHandler = () => {
@@ -3110,6 +3319,48 @@
return { ref, didLoad: () => didLoad.current = true };
}
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ var import_classnames10 = __toESM(require_classnames(), 1);
+
+ // pages/duckplayer/app/components/YouTubeError.module.css
+ var YouTubeError_default = {
+ error: "YouTubeError_error",
+ desktop: "YouTubeError_desktop",
+ mobile: "YouTubeError_mobile",
+ container: "YouTubeError_container",
+ content: "YouTubeError_content",
+ icon: "YouTubeError_icon",
+ heading: "YouTubeError_heading",
+ messages: "YouTubeError_messages"
+ };
+
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ function useErrorStrings(kind) {
+ const { t: t3 } = useTypedTranslation();
+ switch (kind) {
+ case "sign-in-required":
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("signInRequiredErrorMessage1"), t3("signInRequiredErrorMessage2")],
+ variant: "paragraphs"
+ };
+ default:
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("blockedVideoErrorMessage1"), t3("blockedVideoErrorMessage2")],
+ variant: "paragraphs"
+ };
+ }
+ }
+ function YouTubeError({ kind, layout }) {
+ const { heading, messages, variant } = useErrorStrings(kind);
+ const classes = (0, import_classnames10.default)(YouTubeError_default.error, {
+ [YouTubeError_default.desktop]: layout === "desktop",
+ [YouTubeError_default.mobile]: layout === "mobile"
+ });
+ return /* @__PURE__ */ g("div", { className: classes }, /* @__PURE__ */ g("div", { className: YouTubeError_default.container }, /* @__PURE__ */ g("span", { className: YouTubeError_default.icon }), /* @__PURE__ */ g("div", { className: YouTubeError_default.content }, /* @__PURE__ */ g("h1", { className: YouTubeError_default.heading }, heading), messages && variant === "inline" && /* @__PURE__ */ g("p", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("span", { key: item }, item))), messages && variant === "paragraphs" && /* @__PURE__ */ g("div", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("p", { key: item }, item))), messages && variant === "list" && /* @__PURE__ */ g("ul", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("li", { key: item }, item))))));
+ }
+
// pages/duckplayer/app/components/Components.jsx
function Components() {
const settings = new Settings({
@@ -3118,11 +3369,11 @@
let embed = EmbedSettings.fromHref("https://localhost?videoID=123");
let url = embed?.toEmbedUrl();
if (!url) throw new Error("unreachable");
- return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
+ return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "sign-in-required" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "no-embed" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "sign-in-required" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "no-embed" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
}
// pages/duckplayer/app/components/MobileApp.jsx
- var import_classnames10 = __toESM(require_classnames(), 1);
+ var import_classnames11 = __toESM(require_classnames(), 1);
// pages/duckplayer/app/components/MobileApp.module.css
var MobileApp_default = {
@@ -3133,7 +3384,8 @@
switch: "MobileApp_switch",
embed: "MobileApp_embed",
logo: "MobileApp_logo",
- buttons: "MobileApp_buttons"
+ buttons: "MobileApp_buttons",
+ detachedControls: "MobileApp_detachedControls"
};
// pages/duckplayer/app/features/app.js
@@ -3231,11 +3483,13 @@
function MobileApp({ embed }) {
const settings = useSettings();
const telemetry2 = useTelemetry();
+ const youtubeError = useYouTubeError();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g(
+ return /* @__PURE__ */ g(k, null, !youtubeError && features.focusMode(), /* @__PURE__ */ g(
OrientationProvider,
{
onChange: (orientation) => {
+ if (youtubeError) return;
if (orientation === "portrait") {
return FocusMode.enable();
}
@@ -3251,7 +3505,10 @@
}
function MobileLayout({ embed }) {
const platformName = usePlatformName();
- return /* @__PURE__ */ g("main", { class: MobileApp_default.main }, /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("main", { class: MobileApp_default.main, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
}
// pages/duckplayer/app/components/DesktopApp.module.css
@@ -3272,10 +3529,14 @@
function DesktopApp({ embed }) {
const settings = useSettings();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app }, /* @__PURE__ */ g(DesktopLayout, { embed })));
+ const youtubeError = useYouTubeError();
+ return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g(DesktopLayout, { embed })));
}
function DesktopLayout({ embed }) {
- return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
}
// pages/duckplayer/app/index.js
@@ -3291,7 +3552,11 @@
console.log("locale:", environment.locale);
document.body.dataset.display = environment.display;
const strings = environment.locale === "en" ? duckplayer_default : await getTranslationsFromStringOrLoadDynamically(init2.localeStrings, environment.locale) || duckplayer_default;
- const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode"));
+ const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withFeatureState("customError", init2.settings.customError).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode")).withCustomError(baseEnvironment2.urlParams.get("customError"));
+ const initialYouTubeError = (
+ /** @type {YouTubeError} */
+ baseEnvironment2.urlParams.get("youtubeError")
+ );
console.log(settings);
const embed = createEmbedSettings(window.location.href, settings);
const didCatch = (error) => {
@@ -3303,7 +3568,7 @@
if (!root) throw new Error("could not render, root element missing");
if (environment.display === "app") {
D(
- /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
+ /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(YouTubeErrorProvider, { initial: initialYouTubeError }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
TranslationProvider,
{
translationObject: duckplayer_default,
@@ -3319,7 +3584,7 @@
textLength: environment.textLength
},
/* @__PURE__ */ g(MobileApp, { embed })
- ), /* @__PURE__ */ g(WillThrow, null))))))),
+ ), /* @__PURE__ */ g(WillThrow, null)))))))),
root
);
} else if (environment.display === "components") {
@@ -3467,6 +3732,13 @@
onUserValuesChanged(cb) {
return this.messaging.subscribe("onUserValuesChanged", cb);
}
+ /**
+ * This will be sent if the application fails to load.
+ * @param {{error: import('../types/duckplayer.ts').YouTubeError}} params
+ */
+ reportYouTubeError(params) {
+ this.messaging.notify("reportYouTubeError", params);
+ }
/**
* This will be sent if the application has loaded, but a client-side error
* has occurred that cannot be recovered from
diff --git a/build/integration/pages/duckplayer/locales/bg/duckplayer.json b/build/integration/pages/duckplayer/locales/bg/duckplayer.json
index d7f3d20ef..c807a2f78 100644
--- a/build/integration/pages/duckplayer/locales/bg/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/bg/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ГРЕШКА: невалиден идентификатор на видеоклипа",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube няма да позволи на Duck Player да зареди това видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволява това видео да бъде гледано извън YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Все пак можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокира зареждането на това видео. Ако използвате VPN, опитайте да го изключите и презаредете тази страница.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ако това не свърши работа, можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player осигурява чисто изживяване без персонализирани реклами в YouTube и предотвратява влиянието на вече гледаните видеоклипове върху препоръките на YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/cs/duckplayer.json b/build/integration/pages/duckplayer/locales/cs/duckplayer.json
index 8d24c5726..690569a5d 100644
--- a/build/integration/pages/duckplayer/locales/cs/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/cs/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatné ID videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovoluje přehrávači Duck Player načíst tohle video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nedovoluje spuštění videa mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Na tohle video se můžeš pořád podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítání tohohle videa. Pokud používáš VPN, zkus ji vypnout a stránku znovu načíst.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Pokud to nefunguje, můžeš se na video podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Přehrávač Duck Player nabízí sledování v minimalistickém prostředí bez personalizovaných reklam a brání tomu, aby sledovaná videa ovlivňovala tvoje doporučení na YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/da/duckplayer.json b/build/integration/pages/duckplayer/locales/da/duckplayer.json
index f99ab298e..aae6004c2 100644
--- a/build/integration/pages/duckplayer/locales/da/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/da/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEJL: Ugyldigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube vil ikke lade Duck Player indlæse denne video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillader ikke, at denne video vises uden for YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokerer for, at denne video kan indlæses. Hvis du bruger en VPN, så prøv at slå den fra og genindlæse denne side.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis dette ikke virker, kan du stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player giver en ren seeroplevelse uden målrettede annoncer og forhindrer, at visningsaktivitet påvirker dine YouTube-anbefalinger."
}
diff --git a/build/integration/pages/duckplayer/locales/de/duckplayer.json b/build/integration/pages/duckplayer/locales/de/duckplayer.json
index 0ddca103a..abd163119 100644
--- a/build/integration/pages/duckplayer/locales/de/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/de/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEHLER: Ungültige Video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lässt den Duck Player dieses Video nicht laden",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube erlaubt nicht, dass dieses Video außerhalb von YouTube angesehen wird.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kannst dieses Video auf YouTube ansehen, aber ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockiert das Laden dieses Videos. Falls du ein VPN benutzt, deaktiviere es und lade diese Seite neu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Falls das nicht funktioniert, kannst du das Video dennoch auf YouTube ansehen, jedoch ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Mit Duck Player kannst du dir ungestört und ohne personalisierte Werbung Inhalte ansehen. Er verhindert, dass das, was du dir ansiehst, deine YouTube-Empfehlungen beeinflussen."
}
diff --git a/build/integration/pages/duckplayer/locales/el/duckplayer.json b/build/integration/pages/duckplayer/locales/el/duckplayer.json
index d00195f5e..2d8fa43c5 100644
--- a/build/integration/pages/duckplayer/locales/el/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/el/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ΣΦΑΛΜΑ: Μη έγκυρο αναγνωριστικό βίντεο",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "Το YouTube δεν θα αφήσει το Duck Player να φορτώσει το βίντεο αυτό",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Το YouTube δεν επιτρέπει την προβολή αυτού του βίντεο εκτός YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Μπορείτε ακόμα να παρακολουθήσετε αυτό το βίντεο στο YouTube, αλλά χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "Το YouTube μπλοκάρει τη φόρτωση αυτού του βίντεο. Εάν χρησιμοποιείτε VPN, δοκιμάστε να το απενεργοποιήσετε και να φορτώσετε εκ νέου αυτήν τη σελίδα.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Εάν δεν λειτουργήσει αυτό, μπορείτε να παρακολουθήσετε αυτό το βίντεο στο YouTube, ωστόσο χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Το Duck Player παρέχει μια καθαρή εμπειρία προβολής χωρίς εξατομικευμένες διαφημίσεις, ενώ εμποδίζει τη δραστηριότητα προβολής να επηρεάσει τις συστάσεις που θα λαμβάνετε στο YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/en/duckplayer.json b/build/integration/pages/duckplayer/locales/en/duckplayer.json
index c2b5683b9..fe94917c4 100644
--- a/build/integration/pages/duckplayer/locales/en/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/en/duckplayer.json
@@ -33,6 +33,26 @@
"title": "ERROR: Invalid video id",
"note": "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading": {
+ "title": "YouTube won’t let Duck Player load this video",
+ "note": "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1": {
+ "title": "YouTube doesn’t allow this video to be viewed outside of YouTube.",
+ "note": "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2": {
+ "title": "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1": {
+ "title": "YouTube is blocking this video from loading. If you’re using a VPN, try turning it off and reloading this page.",
+ "note": "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2": {
+ "title": "If this doesn’t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "More troubleshooting tips for this specific error"
+ },
"tooltipInfo": {
"title": "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
diff --git a/build/integration/pages/duckplayer/locales/es/duckplayer.json b/build/integration/pages/duckplayer/locales/es/duckplayer.json
index 1b5d8b958..f5af0d046 100644
--- a/build/integration/pages/duckplayer/locales/es/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/es/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERROR: ID de vídeo no válida",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube no permite que Duck Player cargue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube no permite que este vídeo se vea fuera de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional que ofrece Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube está bloqueando la carga de este vídeo. Si estás usando una VPN, intenta desactivarla y volver a cargar la página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si esto no funciona, sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ofrece una experiencia de visualización limpia sin anuncios personalizados e impide que la actividad de visualización influya en tus recomendaciones de YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/et/duckplayer.json b/build/integration/pages/duckplayer/locales/et/duckplayer.json
index c9863f4f5..70b30efee 100644
--- a/build/integration/pages/duckplayer/locales/et/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/et/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIGA: vale video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei luba Duck Playeril seda videot laadida",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei luba seda videot väljaspool YouTube'i vaadata.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Saate seda videot endiselt YouTube'is vaadata, kuid ilma Duck Player'i lisatud privaatsuseta.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokeerib selle video laadimise. Kui kasutate VPN-i, proovige see välja lülitada ning leht uuesti laadida.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Kui see ei aita, saate seda videot ikkagi YouTube'is vaadata, kuid ilma Duck Playeri lisatud privaatsuseta.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pakub isikupärastatud reklaamidest vaba vaatamiskogemust ja takistab, et vaatamisaktiivsus mõjutaks sinu YouTube'i soovitusi."
}
diff --git a/build/integration/pages/duckplayer/locales/fi/duckplayer.json b/build/integration/pages/duckplayer/locales/fi/duckplayer.json
index e73022b8f..4c830a739 100644
--- a/build/integration/pages/duckplayer/locales/fi/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/fi/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIRHE: virheellinen videotunnus",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei salli Duck Playerin ladata tätä videota.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei salli tämän videon katsomista YouTuben ulkopuolella.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Voit yhä katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube estää tämän videon latautumisen. Jos käytät VPN:ää, kytke se pois päältä ja lataa tämä sivu uudelleen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jos tämä ei toimi, voit silti katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tarjoaa puhtaan katselukokemuksen ilman kohdennettuja mainoksia ja estää katseluhistoriaa vaikuttamasta YouTube-suosituksiisi."
}
diff --git a/build/integration/pages/duckplayer/locales/fr/duckplayer.json b/build/integration/pages/duckplayer/locales/fr/duckplayer.json
index 716f0c071..12eb0ac92 100644
--- a/build/integration/pages/duckplayer/locales/fr/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/fr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERREUR : identifiant vidéo non valide",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne permet pas à Duck Player de charger cette vidéo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube n'autorise pas le visionnage de cette vidéo en dehors de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloque le chargement de cette vidéo. Si vous utilisez un VPN, essayez de le désactiver et de recharger cette page.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si cela ne fonctionne pas, vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre une expérience de visionnage épurée, sans publicités personnalisées, et empêche l'activité de visionnage d'influencer vos recommandations YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/hr/duckplayer.json b/build/integration/pages/duckplayer/locales/hr/duckplayer.json
index 3f0e8aeae..48fdbf997 100644
--- a/build/integration/pages/duckplayer/locales/hr/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/hr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "POGREŠKA: Nevažeći ID videozapisa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dopušta Duck Playeru da učita ovaj videozapis.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dopušta da se ovaj videozapis gleda izvan YouTubea.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Još uvijek možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokira učitavanje ovog videozapisa. Ako koristiš VPN, pokušaj ga isključiti i ponovno učitati ovu stranicu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ako to ne uspije, i dalje možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pruža čisti doživljaj gledanja bez personaliziranih oglasa i sprječava da aktivnosti gledanja utječu na tvoje preporuke na YouTubeu."
}
diff --git a/build/integration/pages/duckplayer/locales/hu/duckplayer.json b/build/integration/pages/duckplayer/locales/hu/duckplayer.json
index 3bbe06210..c8faf475f 100644
--- a/build/integration/pages/duckplayer/locales/hu/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/hu/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HIBA: Érvénytelen videoazonosító",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "A YouTube nem engedi, hogy a Duck Player betöltse ezt a videót",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "A YouTube nem engedi, hogy ezt a videót a YouTube-on kívül nézd meg.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Megnézheted a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "A YouTube blokkolja ennek a videónak a betöltését. Ha VPN-t használsz, próbáld meg, hogy kikapcsolod, majd újra betöltöd ezt az oldalt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ha ez nem működik, akkor is megnézheted ezt a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "A Duck Player személyre szabott hirdetések nélküli, letisztult megtekintési élményt nyújt, és megakadályozza, hogy a megtekintési tevékenységed befolyásolja a neked szóló YouTube-ajánlásokat."
}
diff --git a/build/integration/pages/duckplayer/locales/it/duckplayer.json b/build/integration/pages/duckplayer/locales/it/duckplayer.json
index 9ce216677..464303fca 100644
--- a/build/integration/pages/duckplayer/locales/it/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/it/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRORE: ID video non valido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube non consente a Duck Player di caricare questo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Questo video si può vedere solo su YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Puoi ancora guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube sta impedendo il caricamento di questo video. Se stai utilizzando una VPN, prova a disattivarla e a ricaricare questa pagina.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se il problema non si risolve, puoi comunque guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre un'esperienza di visualizzazione pulita, senza annunci personalizzati, e impedisce che l'attività di visualizzazione incida sulle raccomandazioni di YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/lt/duckplayer.json b/build/integration/pages/duckplayer/locales/lt/duckplayer.json
index 1b282dd2c..c8dd25e38 100644
--- a/build/integration/pages/duckplayer/locales/lt/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/lt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KLAIDA: netinkamas vaizdo įrašo ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "„YouTube“ neleidžia „Duck Player“ įkelti šio vaizdo įrašo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "„YouTube“ neleidžia šio vaizdo įrašo žiūrėti ne „YouTube“ platformoje.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šį vaizdo įrašą vis dar gali žiūrėti „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "„YouTube“ blokuoja šio vaizdo įrašo įkėlimą. Jei naudoji VPN, pabandyk jį išjungti ir iš naujo įkelti šį puslapį.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jei tai neveikia, vis tiek gali žiūrėti šį vaizdo įrašą „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "„Duck Player“ užtikrina nepriekaištingą žiūrėjimo patirtį be suasmenintų reklamų ir neleidžia žiūrėjimo veiklai daryti įtakos „YouTube“ rekomendacijoms."
}
diff --git a/build/integration/pages/duckplayer/locales/lv/duckplayer.json b/build/integration/pages/duckplayer/locales/lv/duckplayer.json
index 46d0bee8e..3ae376c31 100644
--- a/build/integration/pages/duckplayer/locales/lv/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/lv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KĻŪDA: Nederīgs video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube neļauj Duck Player ielādēt šo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube neļauj skatīties šo video ārpus YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šo videoklipu joprojām vari skatīties vietnē YouTube, taču bez papildu Duck Player konfidencialitātes.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloķē šī video ielādi. Ja tu izmanto VPN, mēģini to izslēgt un pārlādēt šo lapu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ja tas nedarbojas, joprojām vari skatīties šo video vietnē YouTube, taču bez papildu privātuma, ko nodrošina Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player nodrošina netraucētu skatīšanās pieredzi bez personalizētām reklāmām un neļauj skatīšanās darbībām ietekmēt tavus YouTube ieteikumus."
}
diff --git a/build/integration/pages/duckplayer/locales/nb/duckplayer.json b/build/integration/pages/duckplayer/locales/nb/duckplayer.json
index 4c1d826f6..fef475447 100644
--- a/build/integration/pages/duckplayer/locales/nb/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/nb/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEIL: Ugyldig video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lar ikke Duck Player laste denne videoen",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillater ikke visning av denne videoen utenfor YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fremdeles se videoen på YouTube, men uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkerer denne videoen fra å lastes. Hvis du bruker en VPN, kan du prøve å slå den av og laste denne siden på nytt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis ikke det virker, kan du fremdeles se videoen på YouTube, bare uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tilbyr en ren seeropplevelse uten tilpassede annonser og forhindrer at seeraktiviteten din påvirker YouTube-anbefalingene dine."
}
diff --git a/build/integration/pages/duckplayer/locales/nl/duckplayer.json b/build/integration/pages/duckplayer/locales/nl/duckplayer.json
index a1be8669e..51a0ece78 100644
--- a/build/integration/pages/duckplayer/locales/nl/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/nl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FOUT: ongeldige video-id",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube staat Duck Player niet toe om deze video te laden.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube staat niet toe dat je deze video buiten YouTube bekijkt.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Je kunt deze video nog steeds bekijken op YouTube, maar zonder de extra privacy van Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkeert het laden van deze video. Als je een VPN gebruikt, probeer deze dan uit te schakelen en deze pagina opnieuw te laden.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Als dit niet werkt, kun je deze video nog steeds op YouTube bekijken, maar dan zonder de extra privacy van Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player biedt puur kijkplezier zonder gepersonaliseerde advertenties en voorkomt dat de dingen die je bekijkt je YouTube-aanbevelingen beïnvloeden."
}
diff --git a/build/integration/pages/duckplayer/locales/pl/duckplayer.json b/build/integration/pages/duckplayer/locales/pl/duckplayer.json
index accd3fde5..8b633d415 100644
--- a/build/integration/pages/duckplayer/locales/pl/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/pl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "BŁĄD: nieprawidłowy identyfikator filmu",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nie zezwala na załadowanie tego filmu przez Duck Player",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nie zezwala na oglądanie tego filmu poza YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Nadal możesz oglądać ten film na YouTube, ale bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje ładowanie tego filmu. Jeśli korzystasz z sieci VPN, spróbuj ją wyłączyć i ponownie załadować tę stronę.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jeśli to nie pomoże, nadal możesz oglądać ten film na YouTube, jednak bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player zapewnia czyste środowisko oglądania bez spersonalizowanych reklam i sprawia, że aktywność związana z oglądaniem filmów nie wpływa na rekomendacje YouTube'a."
}
diff --git a/build/integration/pages/duckplayer/locales/pt/duckplayer.json b/build/integration/pages/duckplayer/locales/pt/duckplayer.json
index a5bfca188..2ff6fad9c 100644
--- a/build/integration/pages/duckplayer/locales/pt/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/pt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRO: ID de vídeo inválido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "O YouTube não permite que o Duck Player carregue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "O YouTube não permite que vejas este vídeo fora do YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "O YouTube está a bloquear o carregamento deste vídeo. Se estiveres a usar uma VPN, tenta desativá-la e recarregar esta página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se isto não funcionar, continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "O Duck Player oferece uma experiência de visualização limpa sem anúncios personalizados e evita que as atividades de visualização influenciem as recomendações do YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/ro/duckplayer.json b/build/integration/pages/duckplayer/locales/ro/duckplayer.json
index bfafec70e..25ffac535 100644
--- a/build/integration/pages/duckplayer/locales/ro/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/ro/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "EROARE: ID video incorect",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nu permite Duck Player să încarce acest videoclip",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nu permite ca acest videoclip să fie vizionat în afara YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blochează încărcarea acestui videoclip. Dacă folosești un VPN, încearcă să-l dezactivezi și să reîncarci această pagină.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Dacă acest lucru nu funcționează, poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player oferă o experiență de vizionare fără perturbări, fără reclame personalizate și împiedică activitatea de vizionare să îți influențeze recomandările YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/ru/duckplayer.json b/build/integration/pages/duckplayer/locales/ru/duckplayer.json
index 4bf5cc0c1..d1a3fc528 100644
--- a/build/integration/pages/duckplayer/locales/ru/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/ru/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ОШИБКА: Неверный идентификатор видео",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube не позволяет проигрывателю Duck Player загрузить это видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволяет смотреть это видео вне своей платформы.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Вы по-прежнему можете посмотреть этот ролик на YouTube, но уже без дополнительной защиты Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокирует загрузку этого видео. Если вы используете VPN, отключите ее и перезагрузите страницу.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Если это не даст результата, вы все равно сможете просмотреть это видео на YouTube, но без дополнительной защиты конфиденциальности, обеспечиваемой Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Проигрыватель Duck Player обеспечивает беспрепятственный просмотр без персонализированной рекламы и влияния просмотренных роликов на рекомендации в YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/sk/duckplayer.json b/build/integration/pages/duckplayer/locales/sk/duckplayer.json
index 88a2cc3a5..a68cdeb89 100644
--- a/build/integration/pages/duckplayer/locales/sk/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/sk/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatný identifikátor videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovolí Duck Playeru načítať toto video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nepovoľuje, aby sa toto video pozeralo mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Toto video si môžeš pozrieť aj na YouTube, ale bez dodatočného súkromia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítanie tohto videa. Ak používaš sieť VPN, skús ju vypnúť a znova načítať túto stránku.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ak to nefunguje, môžeš si toto video pozrieť aj na YouTube, ale bez dodatočnej ochrany súkromia, ktorú poskytuje Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player poskytuje čisté zobrazenie bez personalizovaných reklám a zabraňuje tomu, aby aktivita pri sledovaní ovplyvňovala vaše odporúčania v službe YouTube."
}
diff --git a/build/integration/pages/duckplayer/locales/sl/duckplayer.json b/build/integration/pages/duckplayer/locales/sl/duckplayer.json
index 7d4a89155..977830ef4 100644
--- a/build/integration/pages/duckplayer/locales/sl/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/sl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "NAPAKA: Neveljaven ID videoposnetka",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dovoli predvajalniku Duck Player naložiti tega videa",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dovoljuje, da si ta videoposnetek ogledate zunaj YouTuba.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Ta videoposnetek si lahko še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube preprečuje nalaganje tega videoposnetka. Če uporabljate omrežje VPN, ga poskusite izklopiti in znova naložite to stran.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Če to ne deluje, si lahko ta videoposnetek še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Predvajalnik Duck Player zagotavlja čisto izkušnjo gledanja brez prilagojenih oglasov in preprečuje, da bi dejavnost gledanja vplivala na vaša priporočila v YouTubu."
}
diff --git a/build/integration/pages/duckplayer/locales/sv/duckplayer.json b/build/integration/pages/duckplayer/locales/sv/duckplayer.json
index cb5b75e4c..444e91750 100644
--- a/build/integration/pages/duckplayer/locales/sv/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/sv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEL: Ogiltigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube låter inte Duck Player läsa in den här videon",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillåter inte att den här videon ses utanför YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockerar den här videon från att läsas in. Om du använder ett VPN kan du prova stänga av det och läsa in sidan igen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Om det inte fungerar kan du fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ger en störningsfri visningsupplevelse utan personliga annonser och förhindrar att din tittaraktivitet påverkar YouTube-rekommendationer."
}
diff --git a/build/integration/pages/duckplayer/locales/tr/duckplayer.json b/build/integration/pages/duckplayer/locales/tr/duckplayer.json
index c23cab2bb..e733fcaa6 100644
--- a/build/integration/pages/duckplayer/locales/tr/duckplayer.json
+++ b/build/integration/pages/duckplayer/locales/tr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HATA: Geçersiz video kimliği",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube, Duck Player'ın bu videoyu yüklemesine izin vermiyor",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube, bu videonun YouTube dışında izlenmesine izin vermiyor.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Bu videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bu videonun yüklenmesini engelliyor. VPN kullanıyorsanız, VPN'i kapatıp bu sayfayı yeniden yüklemeyi deneyin.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Bu işe yaramazsa, videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player, kişiselleştirilmiş reklamlar olmadan temiz bir görüntüleme deneyimi sağlar ve görüntüleme etkinliğinin YouTube önerilerinizi etkilemesini önler."
}
diff --git a/build/windows/pages/duckplayer/dist/index.css b/build/windows/pages/duckplayer/dist/index.css
index cff98b790..7e8fbf3a0 100644
--- a/build/windows/pages/duckplayer/dist/index.css
+++ b/build/windows/pages/duckplayer/dist/index.css
@@ -61,6 +61,7 @@ body[data-display=app] {
/* pages/duckplayer/app/components/Components.module.css */
.Components_main {
+ background-color: #000;
color: white;
max-width: 3840px;
margin: 0 auto;
@@ -112,7 +113,7 @@ body[data-display=app] {
text-decoration: none;
}
[data-layout=mobile] .Button_button {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
.Button_button:hover,
.Button_button:focus-visible {
@@ -188,7 +189,7 @@ body[data-display=app] {
.SwitchBarMobile_switchBar {
display: grid;
border-radius: 8px;
- background: #2f2f2f;
+ background: rgba(255, 255, 255, 0.12);
padding-inline: 16px;
height: 100%;
line-height: 1.1;
@@ -518,6 +519,9 @@ body[data-display=app] {
.Wordmark_mobile_logo {
height: 100px;
}
+ [data-youtube-error=true] .Wordmark_mobile_logo {
+ height: 44px;
+ }
}
.Wordmark_mobile_logoSvg img {
display: block;
@@ -589,6 +593,127 @@ body[data-display=app] {
}
}
+/* pages/duckplayer/app/components/YouTubeError.module.css */
+.YouTubeError_error {
+ align-items: center;
+ background: rgba(0, 0, 0, 0.6);
+ display: grid;
+ height: 100%;
+ justify-items: center;
+}
+.YouTubeError_error.YouTubeError_desktop {
+ height: var(--frame-height);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+.YouTubeError_error.YouTubeError_mobile {
+ border-radius: var(--inner-radius);
+ height: 100%;
+ overflow: auto;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_error.YouTubeError_mobile {
+ aspect-ratio: 16 / 9;
+ }
+}
+.YouTubeError_desktop {
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.YouTubeError_container {
+ column-gap: 24px;
+ display: flex;
+ flex-flow: row;
+ margin: 0;
+ max-width: 680px;
+ padding: 0 40px;
+ row-gap: 4px;
+}
+.YouTubeError_mobile .YouTubeError_container {
+ flex-flow: column;
+ padding: 0 24px;
+}
+@media screen and (min-height: 320px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 16px 0;
+ }
+}
+@media screen and (min-width: 375px) and (min-height: 400px) {
+ .YouTubeError_mobile .YouTubeError_container {
+ margin: 36px 0;
+ }
+}
+.YouTubeError_content {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin: 16px 0;
+}
+@media screen and (min-width: 600px) {
+ .YouTubeError_content {
+ margin: 24px 0;
+ }
+}
+.YouTubeError_icon {
+ align-self: center;
+ display: flex;
+ justify-content: center;
+}
+.YouTubeError_icon::before {
+ content: " ";
+ display: block;
+ background: url('data:image/svg+xml,%0A') no-repeat;
+ height: 48px;
+ width: 48px;
+}
+@media screen and (max-width: 320px) {
+ .YouTubeError_icon {
+ display: none;
+ }
+}
+@media screen and (min-width: 600px) and (min-height: 600px) {
+ .YouTubeError_icon {
+ justify-content: start;
+ }
+ .YouTubeError_icon::before {
+ background-image: url('data:image/svg+xml,%0A');
+ height: 96px;
+ width: 128px;
+ }
+}
+.YouTubeError_heading {
+ color: #fff;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: calc(24 / 20);
+ margin: 0;
+}
+.YouTubeError_messages {
+ color: #ccc;
+ font-size: 16px;
+ line-height: calc(24 / 16);
+}
+div.YouTubeError_messages {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+}
+div.YouTubeError_messages p {
+ margin: 0;
+}
+p.YouTubeError_messages {
+ margin: 0;
+}
+ul.YouTubeError_messages li {
+ list-style: disc;
+ margin-left: 24px;
+}
+
/* pages/duckplayer/app/components/MobileApp.module.css */
body[data-display=app] {
padding: 8px;
@@ -626,6 +751,7 @@ html[data-focus-mode=on] .MobileApp_hideInFocus {
--inner-radius: 12px;
--logo-width: 157px;
--inner-padding: 8px;
+ --mobile-buttons-padding: 8px;
position: relative;
max-width: 100vh;
margin: 0 auto;
@@ -681,6 +807,15 @@ html[data-focus-mode=on] .MobileApp_embed {
grid-area: switch;
height: 44px;
}
+.MobileApp_detachedControls {
+ grid-area: detached;
+ display: flex;
+ flex-flow: column;
+ gap: 8px;
+ padding: 8px;
+ background: #2f2f2f;
+ border-radius: 12px;
+}
@media screen and (min-width: 425px) and (max-height: 600px) {
.MobileApp_main {
grid-template-rows: max-content auto max-content max-content 12px max-content auto;
@@ -774,6 +909,71 @@ html[data-focus-mode=on] .MobileApp_embed {
justify-content: end;
}
}
+@media screen and (max-width: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ --bg-color: transparent;
+ --inner-padding: 4px;
+ grid-template-areas: "logo" "gap3" "embed" "gap4" "switch" "buttons";
+ grid-template-rows: max-content 16px auto 12px max-content max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ background: #2f2f2f;
+ border-radius: var(--outer-radius);
+ padding: 4px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_switch {
+ background: #2f2f2f;
+ padding: 8px 8px 0 8px;
+ height: 60px;
+ max-height: 60px;
+ border-top-left-radius: var(--outer-radius);
+ border-top-right-radius: var(--outer-radius);
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ background: #2f2f2f;
+ padding: 8px;
+ transition: all 0.3s;
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+ .MobileApp_main[data-youtube-error=true]:has([data-state=completed]) .MobileApp_switch {
+ background: transparent;
+ max-height: 0;
+ }
+}
+@media screen and (max-width: 599px) and (max-height: 599px) {
+ .MobileApp_main[data-youtube-error=true] {
+ max-width: unset;
+ grid-template-rows: 0 0 auto 12px 0 max-content;
+ }
+ .MobileApp_main[data-youtube-error=true] :is(.MobileApp_logo, .MobileApp_switch) {
+ display: none;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ }
+}
+@media screen and (min-width: 600px) and (max-height: 450px) {
+ .MobileApp_main[data-youtube-error=true] {
+ grid-template-areas: "embed" "buttons" "gap5";
+ grid-template-rows: auto max-content 8px;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ border-radius: var(--outer-radius);
+ display: block;
+ }
+}
+@media screen and (max-height: 320px) {
+ .MobileApp_main[data-youtube-error=true] .MobileApp_embed {
+ overflow-y: auto;
+ }
+ .MobileApp_main[data-youtube-error=true] .MobileApp_buttons {
+ bottom: 0;
+ position: sticky;
+ }
+}
/* pages/duckplayer/app/components/MobileButtons.module.css */
.MobileButtons_buttons {
diff --git a/build/windows/pages/duckplayer/dist/index.js b/build/windows/pages/duckplayer/dist/index.js
index 64928541f..430aabad4 100644
--- a/build/windows/pages/duckplayer/dist/index.js
+++ b/build/windows/pages/duckplayer/dist/index.js
@@ -1982,12 +1982,33 @@
title: "ERROR: Invalid video id",
note: "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ blockedVideoErrorHeading: {
+ title: "YouTube won\u2019t let Duck Player load this video",
+ note: "Message shown when YouTube has blocked playback of a video"
+ },
+ blockedVideoErrorMessage1: {
+ title: "YouTube doesn\u2019t allow this video to be viewed outside of YouTube.",
+ note: "Explanation on why the error is happening."
+ },
+ blockedVideoErrorMessage2: {
+ title: "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ signInRequiredErrorMessage1: {
+ title: "YouTube is blocking this video from loading. If you\u2019re using a VPN, try turning it off and reloading this page.",
+ note: "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ signInRequiredErrorMessage2: {
+ title: "If this doesn\u2019t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ note: "More troubleshooting tips for this specific error"
+ },
tooltipInfo: {
title: "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
};
// pages/duckplayer/app/settings.js
+ var DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
var Settings = class _Settings {
/**
* @param {object} params
@@ -1995,17 +2016,20 @@
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
+ * @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
*/
constructor({
platform = { name: "macos" },
pip = { state: "disabled" },
autoplay = { state: "enabled" },
- focusMode = { state: "enabled" }
+ focusMode = { state: "enabled" },
+ customError = { state: "disabled", signInRequiredSelector: "" }
}) {
this.platform = platform;
this.pip = pip;
this.autoplay = autoplay;
this.focusMode = focusMode;
+ this.customError = customError;
}
/**
* @param {keyof import("../types/duckplayer.js").DuckPlayerPageSettings} named
@@ -2014,7 +2038,7 @@
*/
withFeatureState(named, settings) {
if (!settings) return this;
- const valid = ["pip", "autoplay", "focusMode"];
+ const valid = ["pip", "autoplay", "focusMode", "customError"];
if (!valid.includes(named)) {
console.warn(`Excluding invalid feature key ${named}`);
return this;
@@ -2053,6 +2077,28 @@
}
return this;
}
+ /**
+ * @param {string|null|undefined} newState
+ * @return {Settings}
+ */
+ withCustomError(newState) {
+ if (newState === "disabled") {
+ return new _Settings({
+ ...this,
+ customError: { state: "disabled" }
+ });
+ }
+ if (newState === "enabled") {
+ return new _Settings({
+ ...this,
+ customError: {
+ state: "enabled",
+ signOnRequiredSelector: DEFAULT_SIGN_IN_REQURED_HREF
+ }
+ });
+ }
+ return this;
+ }
/**
* @return {string}
*/
@@ -2959,6 +3005,162 @@
}
};
+ // pages/duckplayer/app/providers/YouTubeErrorProvider.jsx
+ var YOUTUBE_ERROR_EVENT = "ddg-duckplayer-youtube-error";
+ var YOUTUBE_ERRORS = {
+ ageRestricted: "age-restricted",
+ signInRequired: "sign-in-required",
+ noEmbed: "no-embed",
+ unknown: "unknown"
+ };
+ var YOUTUBE_ERROR_IDS = Object.values(YOUTUBE_ERRORS);
+ var YouTubeErrorContext = J({
+ /** @type {YouTubeError|null} */
+ error: null
+ });
+ function YouTubeErrorProvider({ initial = null, children }) {
+ let initialError = null;
+ if (initial && YOUTUBE_ERROR_IDS.includes(initial)) {
+ initialError = initial;
+ }
+ const [error, setError] = h2(initialError);
+ const messaging2 = useMessaging();
+ const platformName = usePlatformName();
+ const setFocusMode = useSetFocusMode();
+ y2(() => {
+ const errorEventHandler = (event) => {
+ const eventError = event.detail?.error;
+ if (YOUTUBE_ERROR_IDS.includes(eventError) || eventError === null) {
+ if (eventError && eventError !== error) {
+ setFocusMode("paused");
+ if (platformName === "macos" || platformName === "ios") {
+ messaging2.reportYouTubeError({ error: eventError });
+ }
+ } else {
+ setFocusMode("enabled");
+ }
+ setError(eventError);
+ }
+ };
+ window.addEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ return () => window.removeEventListener(YOUTUBE_ERROR_EVENT, errorEventHandler);
+ }, []);
+ return /* @__PURE__ */ g(YouTubeErrorContext.Provider, { value: { error } }, children);
+ }
+ function useYouTubeError() {
+ return x2(YouTubeErrorContext).error;
+ }
+
+ // pages/duckplayer/app/features/error-detection.js
+ var ErrorDetection = class {
+ /** @type {HTMLIFrameElement} */
+ iframe;
+ /** @type {CustomErrorOptions} */
+ options;
+ /**
+ * @param {CustomErrorOptions} options
+ */
+ constructor(options) {
+ this.options = options;
+ }
+ /**
+ * @param {HTMLIFrameElement} iframe
+ */
+ iframeDidLoad(iframe) {
+ this.iframe = iframe;
+ if (!this.options || !this.options.signInRequiredSelector) {
+ console.log("Missing Custom Error options");
+ return null;
+ }
+ const documentBody = iframe.contentWindow?.document?.body;
+ if (documentBody) {
+ if (this.checkForError(documentBody)) {
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ return null;
+ }
+ const observer = new MutationObserver(this.handleMutation.bind(this));
+ observer.observe(documentBody, {
+ childList: true,
+ subtree: true
+ // Observe all descendants of the body
+ });
+ return () => {
+ observer.disconnect();
+ };
+ }
+ return null;
+ }
+ /**
+ * Mutation handler that checks new nodes for error states
+ *
+ * @type {MutationCallback}
+ */
+ handleMutation(mutationsList) {
+ for (const mutation of mutationsList) {
+ if (mutation.type === "childList") {
+ mutation.addedNodes.forEach((node) => {
+ if (this.checkForError(node)) {
+ console.log("A node with an error has been added to the document:", node);
+ const error = this.getErrorType();
+ window.dispatchEvent(new CustomEvent(YOUTUBE_ERROR_EVENT, { detail: { error } }));
+ }
+ });
+ }
+ }
+ }
+ /**
+ * Attempts to detect the type of error in the YouTube embed iframe
+ * @returns {YouTubeError}
+ */
+ getErrorType() {
+ const iframeWindow = (
+ /** @type {Window & { ytcfg: object }} */
+ this.iframe.contentWindow
+ );
+ let playerResponse;
+ try {
+ playerResponse = JSON.parse(iframeWindow.ytcfg?.get("PLAYER_VARS")?.embedded_player_response);
+ } catch (e3) {
+ console.log("Could not parse player response", e3);
+ }
+ if (typeof playerResponse === "object") {
+ const {
+ previewPlayabilityStatus: { desktopLegacyAgeGateReason, status }
+ } = playerResponse;
+ if (status === "UNPLAYABLE") {
+ if (desktopLegacyAgeGateReason === 1) {
+ return YOUTUBE_ERRORS.ageRestricted;
+ }
+ return YOUTUBE_ERRORS.noEmbed;
+ }
+ try {
+ if (this.options?.signInRequiredSelector && !!iframeWindow.document.querySelector(this.options.signInRequiredSelector)) {
+ return YOUTUBE_ERRORS.signInRequired;
+ }
+ } catch (e3) {
+ console.log("Sign-in required query failed", e3);
+ }
+ }
+ return YOUTUBE_ERRORS.unknown;
+ }
+ /**
+ * Analyses a node and its children to determine if it contains an error state
+ *
+ * @param {Node} [node]
+ */
+ checkForError(node) {
+ if (node?.nodeType === Node.ELEMENT_NODE) {
+ const element = (
+ /** @type {HTMLElement} */
+ node
+ );
+ return element.classList.contains("ytp-error") || !!element.querySelector("ytp-error");
+ }
+ return false;
+ }
+ };
+
// pages/duckplayer/app/features/iframe.js
var IframeFeature = class {
/**
@@ -3015,6 +3217,12 @@
*/
mouseCapture: () => {
return new MouseCapture();
+ },
+ /**
+ * @return {IframeFeature}
+ */
+ errorDetection: () => {
+ return new ErrorDetection(settings.customError);
}
};
}
@@ -3083,7 +3291,8 @@
features.pip(),
features.clickCapture(),
features.titleCapture(),
- features.mouseCapture()
+ features.mouseCapture(),
+ features.errorDetection()
];
const cleanups = [];
const loadHandler = () => {
@@ -3110,6 +3319,48 @@
return { ref, didLoad: () => didLoad.current = true };
}
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ var import_classnames10 = __toESM(require_classnames(), 1);
+
+ // pages/duckplayer/app/components/YouTubeError.module.css
+ var YouTubeError_default = {
+ error: "YouTubeError_error",
+ desktop: "YouTubeError_desktop",
+ mobile: "YouTubeError_mobile",
+ container: "YouTubeError_container",
+ content: "YouTubeError_content",
+ icon: "YouTubeError_icon",
+ heading: "YouTubeError_heading",
+ messages: "YouTubeError_messages"
+ };
+
+ // pages/duckplayer/app/components/YouTubeError.jsx
+ function useErrorStrings(kind) {
+ const { t: t3 } = useTypedTranslation();
+ switch (kind) {
+ case "sign-in-required":
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("signInRequiredErrorMessage1"), t3("signInRequiredErrorMessage2")],
+ variant: "paragraphs"
+ };
+ default:
+ return {
+ heading: t3("blockedVideoErrorHeading"),
+ messages: [t3("blockedVideoErrorMessage1"), t3("blockedVideoErrorMessage2")],
+ variant: "paragraphs"
+ };
+ }
+ }
+ function YouTubeError({ kind, layout }) {
+ const { heading, messages, variant } = useErrorStrings(kind);
+ const classes = (0, import_classnames10.default)(YouTubeError_default.error, {
+ [YouTubeError_default.desktop]: layout === "desktop",
+ [YouTubeError_default.mobile]: layout === "mobile"
+ });
+ return /* @__PURE__ */ g("div", { className: classes }, /* @__PURE__ */ g("div", { className: YouTubeError_default.container }, /* @__PURE__ */ g("span", { className: YouTubeError_default.icon }), /* @__PURE__ */ g("div", { className: YouTubeError_default.content }, /* @__PURE__ */ g("h1", { className: YouTubeError_default.heading }, heading), messages && variant === "inline" && /* @__PURE__ */ g("p", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("span", { key: item }, item))), messages && variant === "paragraphs" && /* @__PURE__ */ g("div", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("p", { key: item }, item))), messages && variant === "list" && /* @__PURE__ */ g("ul", { className: YouTubeError_default.messages }, messages.map((item) => /* @__PURE__ */ g("li", { key: item }, item))))));
+ }
+
// pages/duckplayer/app/components/Components.jsx
function Components() {
const settings = new Settings({
@@ -3118,11 +3369,11 @@
let embed = EmbedSettings.fromHref("https://localhost?videoID=123");
let url = embed?.toEmbedUrl();
if (!url) throw new Error("unreachable");
- return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
+ return /* @__PURE__ */ g(k, null, /* @__PURE__ */ g("main", { class: Components_default.main }, /* @__PURE__ */ g("div", { class: Components_default.tube }, /* @__PURE__ */ g(Wordmark, null), /* @__PURE__ */ g("h2", null, "Floating Bar"), /* @__PURE__ */ g("div", { style: "position: relative; padding-left: 10em; min-height: 150px;" }, /* @__PURE__ */ g(InfoIcon, { debugStyles: true })), /* @__PURE__ */ g("h2", null, "Info Tooltip"), /* @__PURE__ */ g(FloatingBar, null, /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: info_data_default })), /* @__PURE__ */ g(Button, { icon: true }, /* @__PURE__ */ g(Icon, { src: cog_data_default })), /* @__PURE__ */ g(Button, { fill: true }, "Open in YouTube")), /* @__PURE__ */ g("h2", null, "Info Bar"), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(InfoBar, { embed }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (ios)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" })), /* @__PURE__ */ g("h2", null, "Mobile Switch Bar (android)"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName: "android" })), /* @__PURE__ */ g("h2", null, "Desktop Switch bar"), /* @__PURE__ */ g("h3", null, "idle"), /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarDesktop, null))), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=false (desktop)")), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(Player, { src: url, layout: "desktop" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "sign-in-required" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(PlayerContainer, null, /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: "no-embed" }), /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g("h2", null, /* @__PURE__ */ g("code", null, "inset=true (mobile)")), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "sign-in-required" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null), /* @__PURE__ */ g(PlayerContainer, { inset: true }, /* @__PURE__ */ g(PlayerInternal, { inset: true }, /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: "no-embed" }), /* @__PURE__ */ g(SwitchBarMobile, { platformName: "ios" }))), /* @__PURE__ */ g("br", null)));
}
// pages/duckplayer/app/components/MobileApp.jsx
- var import_classnames10 = __toESM(require_classnames(), 1);
+ var import_classnames11 = __toESM(require_classnames(), 1);
// pages/duckplayer/app/components/MobileApp.module.css
var MobileApp_default = {
@@ -3133,7 +3384,8 @@
switch: "MobileApp_switch",
embed: "MobileApp_embed",
logo: "MobileApp_logo",
- buttons: "MobileApp_buttons"
+ buttons: "MobileApp_buttons",
+ detachedControls: "MobileApp_detachedControls"
};
// pages/duckplayer/app/features/app.js
@@ -3231,11 +3483,13 @@
function MobileApp({ embed }) {
const settings = useSettings();
const telemetry2 = useTelemetry();
+ const youtubeError = useYouTubeError();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g(
+ return /* @__PURE__ */ g(k, null, !youtubeError && features.focusMode(), /* @__PURE__ */ g(
OrientationProvider,
{
onChange: (orientation) => {
+ if (youtubeError) return;
if (orientation === "portrait") {
return FocusMode.enable();
}
@@ -3251,7 +3505,10 @@
}
function MobileLayout({ embed }) {
const platformName = usePlatformName();
- return /* @__PURE__ */ g("main", { class: MobileApp_default.main }, /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames10.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("main", { class: MobileApp_default.main, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.filler, MobileApp_default.hideInFocus) }), /* @__PURE__ */ g("div", { class: MobileApp_default.embed }, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "mobile", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "mobile", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "mobile" })), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.logo, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileWordmark, null)), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.switch, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(SwitchProvider, null, /* @__PURE__ */ g(SwitchBarMobile, { platformName }))), /* @__PURE__ */ g("div", { class: (0, import_classnames11.default)(MobileApp_default.buttons, MobileApp_default.hideInFocus) }, /* @__PURE__ */ g(MobileButtons, { embed })));
}
// pages/duckplayer/app/components/DesktopApp.module.css
@@ -3272,10 +3529,14 @@
function DesktopApp({ embed }) {
const settings = useSettings();
const features = createAppFeaturesFrom(settings);
- return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app }, /* @__PURE__ */ g(DesktopLayout, { embed })));
+ const youtubeError = useYouTubeError();
+ return /* @__PURE__ */ g(k, null, features.focusMode(), /* @__PURE__ */ g("main", { class: DesktopApp_default.app, "data-youtube-error": !!youtubeError }, /* @__PURE__ */ g(DesktopLayout, { embed })));
}
function DesktopLayout({ embed }) {
- return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
+ const youtubeError = useYouTubeError();
+ const settings = useSettings();
+ const showCustomError = youtubeError && settings.customError?.state === "enabled";
+ return /* @__PURE__ */ g("div", { class: DesktopApp_default.desktop }, /* @__PURE__ */ g(PlayerContainer, null, embed === null && /* @__PURE__ */ g(PlayerError, { layout: "desktop", kind: "invalid-id" }), embed !== null && showCustomError && /* @__PURE__ */ g(YouTubeError, { layout: "desktop", kind: youtubeError }), embed !== null && !showCustomError && /* @__PURE__ */ g(Player, { src: embed.toEmbedUrl(), layout: "desktop" }), /* @__PURE__ */ g(HideInFocusMode, { style: "slide" }, /* @__PURE__ */ g(InfoBarContainer, null, /* @__PURE__ */ g(InfoBar, { embed })))));
}
// pages/duckplayer/app/index.js
@@ -3291,7 +3552,11 @@
console.log("locale:", environment.locale);
document.body.dataset.display = environment.display;
const strings = environment.locale === "en" ? duckplayer_default : await getTranslationsFromStringOrLoadDynamically(init2.localeStrings, environment.locale) || duckplayer_default;
- const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode"));
+ const settings = new Settings({}).withPlatformName(baseEnvironment2.injectName).withPlatformName(init2.platform?.name).withPlatformName(baseEnvironment2.urlParams.get("platform")).withFeatureState("pip", init2.settings.pip).withFeatureState("autoplay", init2.settings.autoplay).withFeatureState("focusMode", init2.settings.focusMode).withFeatureState("customError", init2.settings.customError).withDisabledFocusMode(baseEnvironment2.urlParams.get("focusMode")).withCustomError(baseEnvironment2.urlParams.get("customError"));
+ const initialYouTubeError = (
+ /** @type {YouTubeError} */
+ baseEnvironment2.urlParams.get("youtubeError")
+ );
console.log(settings);
const embed = createEmbedSettings(window.location.href, settings);
const didCatch = (error) => {
@@ -3303,7 +3568,7 @@
if (!root) throw new Error("could not render, root element missing");
if (environment.display === "app") {
D(
- /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
+ /* @__PURE__ */ g(EnvironmentProvider, { debugState: environment.debugState, injectName: environment.injectName, willThrow: environment.willThrow }, /* @__PURE__ */ g(ErrorBoundary, { didCatch, fallback: /* @__PURE__ */ g(Fallback, { showDetails: environment.env === "development" }) }, /* @__PURE__ */ g(UpdateEnvironment, { search: window.location.search }), /* @__PURE__ */ g(TelemetryContext.Provider, { value: telemetry2 }, /* @__PURE__ */ g(MessagingContext2.Provider, { value: messaging2 }, /* @__PURE__ */ g(SettingsProvider, { settings }, /* @__PURE__ */ g(YouTubeErrorProvider, { initial: initialYouTubeError }, /* @__PURE__ */ g(UserValuesProvider, { initial: init2.userValues }, settings.layout === "desktop" && /* @__PURE__ */ g(
TranslationProvider,
{
translationObject: duckplayer_default,
@@ -3319,7 +3584,7 @@
textLength: environment.textLength
},
/* @__PURE__ */ g(MobileApp, { embed })
- ), /* @__PURE__ */ g(WillThrow, null))))))),
+ ), /* @__PURE__ */ g(WillThrow, null)))))))),
root
);
} else if (environment.display === "components") {
@@ -3467,6 +3732,13 @@
onUserValuesChanged(cb) {
return this.messaging.subscribe("onUserValuesChanged", cb);
}
+ /**
+ * This will be sent if the application fails to load.
+ * @param {{error: import('../types/duckplayer.ts').YouTubeError}} params
+ */
+ reportYouTubeError(params) {
+ this.messaging.notify("reportYouTubeError", params);
+ }
/**
* This will be sent if the application has loaded, but a client-side error
* has occurred that cannot be recovered from
diff --git a/build/windows/pages/duckplayer/locales/bg/duckplayer.json b/build/windows/pages/duckplayer/locales/bg/duckplayer.json
index d7f3d20ef..c807a2f78 100644
--- a/build/windows/pages/duckplayer/locales/bg/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/bg/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ГРЕШКА: невалиден идентификатор на видеоклипа",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube няма да позволи на Duck Player да зареди това видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволява това видео да бъде гледано извън YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Все пак можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокира зареждането на това видео. Ако използвате VPN, опитайте да го изключите и презаредете тази страница.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ако това не свърши работа, можете да гледате това видео в YouTube, но без допълнителната поверителност на Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player осигурява чисто изживяване без персонализирани реклами в YouTube и предотвратява влиянието на вече гледаните видеоклипове върху препоръките на YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/cs/duckplayer.json b/build/windows/pages/duckplayer/locales/cs/duckplayer.json
index 8d24c5726..690569a5d 100644
--- a/build/windows/pages/duckplayer/locales/cs/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/cs/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatné ID videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovoluje přehrávači Duck Player načíst tohle video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nedovoluje spuštění videa mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Na tohle video se můžeš pořád podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítání tohohle videa. Pokud používáš VPN, zkus ji vypnout a stránku znovu načíst.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Pokud to nefunguje, můžeš se na video podívat na YouTube, ale bez ochrany soukromí, jakou nabízí Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Přehrávač Duck Player nabízí sledování v minimalistickém prostředí bez personalizovaných reklam a brání tomu, aby sledovaná videa ovlivňovala tvoje doporučení na YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/da/duckplayer.json b/build/windows/pages/duckplayer/locales/da/duckplayer.json
index f99ab298e..aae6004c2 100644
--- a/build/windows/pages/duckplayer/locales/da/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/da/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEJL: Ugyldigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube vil ikke lade Duck Player indlæse denne video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillader ikke, at denne video vises uden for YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokerer for, at denne video kan indlæses. Hvis du bruger en VPN, så prøv at slå den fra og genindlæse denne side.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis dette ikke virker, kan du stadig se denne video på YouTube, men uden den ekstra fortrolighed, som Duck Player giver.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player giver en ren seeroplevelse uden målrettede annoncer og forhindrer, at visningsaktivitet påvirker dine YouTube-anbefalinger."
}
diff --git a/build/windows/pages/duckplayer/locales/de/duckplayer.json b/build/windows/pages/duckplayer/locales/de/duckplayer.json
index 0ddca103a..abd163119 100644
--- a/build/windows/pages/duckplayer/locales/de/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/de/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEHLER: Ungültige Video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lässt den Duck Player dieses Video nicht laden",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube erlaubt nicht, dass dieses Video außerhalb von YouTube angesehen wird.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kannst dieses Video auf YouTube ansehen, aber ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockiert das Laden dieses Videos. Falls du ein VPN benutzt, deaktiviere es und lade diese Seite neu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Falls das nicht funktioniert, kannst du das Video dennoch auf YouTube ansehen, jedoch ohne die zusätzliche Privatsphäre des Duck Players.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Mit Duck Player kannst du dir ungestört und ohne personalisierte Werbung Inhalte ansehen. Er verhindert, dass das, was du dir ansiehst, deine YouTube-Empfehlungen beeinflussen."
}
diff --git a/build/windows/pages/duckplayer/locales/el/duckplayer.json b/build/windows/pages/duckplayer/locales/el/duckplayer.json
index d00195f5e..2d8fa43c5 100644
--- a/build/windows/pages/duckplayer/locales/el/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/el/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ΣΦΑΛΜΑ: Μη έγκυρο αναγνωριστικό βίντεο",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "Το YouTube δεν θα αφήσει το Duck Player να φορτώσει το βίντεο αυτό",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Το YouTube δεν επιτρέπει την προβολή αυτού του βίντεο εκτός YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Μπορείτε ακόμα να παρακολουθήσετε αυτό το βίντεο στο YouTube, αλλά χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "Το YouTube μπλοκάρει τη φόρτωση αυτού του βίντεο. Εάν χρησιμοποιείτε VPN, δοκιμάστε να το απενεργοποιήσετε και να φορτώσετε εκ νέου αυτήν τη σελίδα.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Εάν δεν λειτουργήσει αυτό, μπορείτε να παρακολουθήσετε αυτό το βίντεο στο YouTube, ωστόσο χωρίς την πρόσθετη ιδιωτικότητα του Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Το Duck Player παρέχει μια καθαρή εμπειρία προβολής χωρίς εξατομικευμένες διαφημίσεις, ενώ εμποδίζει τη δραστηριότητα προβολής να επηρεάσει τις συστάσεις που θα λαμβάνετε στο YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/en/duckplayer.json b/build/windows/pages/duckplayer/locales/en/duckplayer.json
index c2b5683b9..fe94917c4 100644
--- a/build/windows/pages/duckplayer/locales/en/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/en/duckplayer.json
@@ -33,6 +33,26 @@
"title": "ERROR: Invalid video id",
"note": "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading": {
+ "title": "YouTube won’t let Duck Player load this video",
+ "note": "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1": {
+ "title": "YouTube doesn’t allow this video to be viewed outside of YouTube.",
+ "note": "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2": {
+ "title": "You can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1": {
+ "title": "YouTube is blocking this video from loading. If you’re using a VPN, try turning it off and reloading this page.",
+ "note": "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2": {
+ "title": "If this doesn’t work, you can still watch this video on YouTube, but without the added privacy of Duck Player.",
+ "note": "More troubleshooting tips for this specific error"
+ },
"tooltipInfo": {
"title": "Duck Player provides a clean viewing experience without personalized ads and prevents viewing activity from influencing your YouTube recommendations."
}
diff --git a/build/windows/pages/duckplayer/locales/es/duckplayer.json b/build/windows/pages/duckplayer/locales/es/duckplayer.json
index 1b5d8b958..f5af0d046 100644
--- a/build/windows/pages/duckplayer/locales/es/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/es/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERROR: ID de vídeo no válida",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube no permite que Duck Player cargue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube no permite que este vídeo se vea fuera de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional que ofrece Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube está bloqueando la carga de este vídeo. Si estás usando una VPN, intenta desactivarla y volver a cargar la página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si esto no funciona, sigues pudiendo ver este vídeo en YouTube, pero sin la privacidad adicional de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ofrece una experiencia de visualización limpia sin anuncios personalizados e impide que la actividad de visualización influya en tus recomendaciones de YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/et/duckplayer.json b/build/windows/pages/duckplayer/locales/et/duckplayer.json
index c9863f4f5..70b30efee 100644
--- a/build/windows/pages/duckplayer/locales/et/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/et/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIGA: vale video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei luba Duck Playeril seda videot laadida",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei luba seda videot väljaspool YouTube'i vaadata.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Saate seda videot endiselt YouTube'is vaadata, kuid ilma Duck Player'i lisatud privaatsuseta.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokeerib selle video laadimise. Kui kasutate VPN-i, proovige see välja lülitada ning leht uuesti laadida.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Kui see ei aita, saate seda videot ikkagi YouTube'is vaadata, kuid ilma Duck Playeri lisatud privaatsuseta.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pakub isikupärastatud reklaamidest vaba vaatamiskogemust ja takistab, et vaatamisaktiivsus mõjutaks sinu YouTube'i soovitusi."
}
diff --git a/build/windows/pages/duckplayer/locales/fi/duckplayer.json b/build/windows/pages/duckplayer/locales/fi/duckplayer.json
index e73022b8f..4c830a739 100644
--- a/build/windows/pages/duckplayer/locales/fi/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/fi/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "VIRHE: virheellinen videotunnus",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ei salli Duck Playerin ladata tätä videota.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ei salli tämän videon katsomista YouTuben ulkopuolella.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Voit yhä katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube estää tämän videon latautumisen. Jos käytät VPN:ää, kytke se pois päältä ja lataa tämä sivu uudelleen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jos tämä ei toimi, voit silti katsoa tämän videon YouTubessa, mutta ilman Duck Playerin tarjoamaa ylimääräistä tietosuojaa.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tarjoaa puhtaan katselukokemuksen ilman kohdennettuja mainoksia ja estää katseluhistoriaa vaikuttamasta YouTube-suosituksiisi."
}
diff --git a/build/windows/pages/duckplayer/locales/fr/duckplayer.json b/build/windows/pages/duckplayer/locales/fr/duckplayer.json
index 716f0c071..12eb0ac92 100644
--- a/build/windows/pages/duckplayer/locales/fr/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/fr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERREUR : identifiant vidéo non valide",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne permet pas à Duck Player de charger cette vidéo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube n'autorise pas le visionnage de cette vidéo en dehors de YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloque le chargement de cette vidéo. Si vous utilisez un VPN, essayez de le désactiver et de recharger cette page.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Si cela ne fonctionne pas, vous pouvez toujours regarder cette vidéo sur YouTube, mais sans la confidentialité renforcée de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre une expérience de visionnage épurée, sans publicités personnalisées, et empêche l'activité de visionnage d'influencer vos recommandations YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/hr/duckplayer.json b/build/windows/pages/duckplayer/locales/hr/duckplayer.json
index 3f0e8aeae..48fdbf997 100644
--- a/build/windows/pages/duckplayer/locales/hr/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/hr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "POGREŠKA: Nevažeći ID videozapisa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dopušta Duck Playeru da učita ovaj videozapis.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dopušta da se ovaj videozapis gleda izvan YouTubea.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Još uvijek možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokira učitavanje ovog videozapisa. Ako koristiš VPN, pokušaj ga isključiti i ponovno učitati ovu stranicu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ako to ne uspije, i dalje možeš gledati ovaj videozapis na YouTubeu, ali bez dodatne privatnosti Duck Playera.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player pruža čisti doživljaj gledanja bez personaliziranih oglasa i sprječava da aktivnosti gledanja utječu na tvoje preporuke na YouTubeu."
}
diff --git a/build/windows/pages/duckplayer/locales/hu/duckplayer.json b/build/windows/pages/duckplayer/locales/hu/duckplayer.json
index 3bbe06210..c8faf475f 100644
--- a/build/windows/pages/duckplayer/locales/hu/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/hu/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HIBA: Érvénytelen videoazonosító",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "A YouTube nem engedi, hogy a Duck Player betöltse ezt a videót",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "A YouTube nem engedi, hogy ezt a videót a YouTube-on kívül nézd meg.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Megnézheted a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "A YouTube blokkolja ennek a videónak a betöltését. Ha VPN-t használsz, próbáld meg, hogy kikapcsolod, majd újra betöltöd ezt az oldalt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ha ez nem működik, akkor is megnézheted ezt a videót a YouTube-on, de a Duck Player által nyújtott extra adatvédelem nélkül.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "A Duck Player személyre szabott hirdetések nélküli, letisztult megtekintési élményt nyújt, és megakadályozza, hogy a megtekintési tevékenységed befolyásolja a neked szóló YouTube-ajánlásokat."
}
diff --git a/build/windows/pages/duckplayer/locales/it/duckplayer.json b/build/windows/pages/duckplayer/locales/it/duckplayer.json
index 9ce216677..464303fca 100644
--- a/build/windows/pages/duckplayer/locales/it/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/it/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRORE: ID video non valido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube non consente a Duck Player di caricare questo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "Questo video si può vedere solo su YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Puoi ancora guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube sta impedendo il caricamento di questo video. Se stai utilizzando una VPN, prova a disattivarla e a ricaricare questa pagina.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se il problema non si risolve, puoi comunque guardare questo video su YouTube, ma senza la privacy aggiuntiva offerta da Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player offre un'esperienza di visualizzazione pulita, senza annunci personalizzati, e impedisce che l'attività di visualizzazione incida sulle raccomandazioni di YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/lt/duckplayer.json b/build/windows/pages/duckplayer/locales/lt/duckplayer.json
index 1b282dd2c..c8dd25e38 100644
--- a/build/windows/pages/duckplayer/locales/lt/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/lt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KLAIDA: netinkamas vaizdo įrašo ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "„YouTube“ neleidžia „Duck Player“ įkelti šio vaizdo įrašo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "„YouTube“ neleidžia šio vaizdo įrašo žiūrėti ne „YouTube“ platformoje.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šį vaizdo įrašą vis dar gali žiūrėti „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "„YouTube“ blokuoja šio vaizdo įrašo įkėlimą. Jei naudoji VPN, pabandyk jį išjungti ir iš naujo įkelti šį puslapį.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jei tai neveikia, vis tiek gali žiūrėti šį vaizdo įrašą „YouTube“, bet be papildomo „Duck Player“ privatumo.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "„Duck Player“ užtikrina nepriekaištingą žiūrėjimo patirtį be suasmenintų reklamų ir neleidžia žiūrėjimo veiklai daryti įtakos „YouTube“ rekomendacijoms."
}
diff --git a/build/windows/pages/duckplayer/locales/lv/duckplayer.json b/build/windows/pages/duckplayer/locales/lv/duckplayer.json
index 46d0bee8e..3ae376c31 100644
--- a/build/windows/pages/duckplayer/locales/lv/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/lv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "KĻŪDA: Nederīgs video ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube neļauj Duck Player ielādēt šo video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube neļauj skatīties šo video ārpus YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Šo videoklipu joprojām vari skatīties vietnē YouTube, taču bez papildu Duck Player konfidencialitātes.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bloķē šī video ielādi. Ja tu izmanto VPN, mēģini to izslēgt un pārlādēt šo lapu.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ja tas nedarbojas, joprojām vari skatīties šo video vietnē YouTube, taču bez papildu privātuma, ko nodrošina Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player nodrošina netraucētu skatīšanās pieredzi bez personalizētām reklāmām un neļauj skatīšanās darbībām ietekmēt tavus YouTube ieteikumus."
}
diff --git a/build/windows/pages/duckplayer/locales/nb/duckplayer.json b/build/windows/pages/duckplayer/locales/nb/duckplayer.json
index 4c1d826f6..fef475447 100644
--- a/build/windows/pages/duckplayer/locales/nb/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/nb/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEIL: Ugyldig video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube lar ikke Duck Player laste denne videoen",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillater ikke visning av denne videoen utenfor YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fremdeles se videoen på YouTube, men uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkerer denne videoen fra å lastes. Hvis du bruker en VPN, kan du prøve å slå den av og laste denne siden på nytt.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Hvis ikke det virker, kan du fremdeles se videoen på YouTube, bare uten det ekstra personvernet som Duck Player tilbyr.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player tilbyr en ren seeropplevelse uten tilpassede annonser og forhindrer at seeraktiviteten din påvirker YouTube-anbefalingene dine."
}
diff --git a/build/windows/pages/duckplayer/locales/nl/duckplayer.json b/build/windows/pages/duckplayer/locales/nl/duckplayer.json
index a1be8669e..51a0ece78 100644
--- a/build/windows/pages/duckplayer/locales/nl/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/nl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FOUT: ongeldige video-id",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube staat Duck Player niet toe om deze video te laden.",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube staat niet toe dat je deze video buiten YouTube bekijkt.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Je kunt deze video nog steeds bekijken op YouTube, maar zonder de extra privacy van Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokkeert het laden van deze video. Als je een VPN gebruikt, probeer deze dan uit te schakelen en deze pagina opnieuw te laden.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Als dit niet werkt, kun je deze video nog steeds op YouTube bekijken, maar dan zonder de extra privacy van Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player biedt puur kijkplezier zonder gepersonaliseerde advertenties en voorkomt dat de dingen die je bekijkt je YouTube-aanbevelingen beïnvloeden."
}
diff --git a/build/windows/pages/duckplayer/locales/pl/duckplayer.json b/build/windows/pages/duckplayer/locales/pl/duckplayer.json
index accd3fde5..8b633d415 100644
--- a/build/windows/pages/duckplayer/locales/pl/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/pl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "BŁĄD: nieprawidłowy identyfikator filmu",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nie zezwala na załadowanie tego filmu przez Duck Player",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nie zezwala na oglądanie tego filmu poza YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Nadal możesz oglądać ten film na YouTube, ale bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje ładowanie tego filmu. Jeśli korzystasz z sieci VPN, spróbuj ją wyłączyć i ponownie załadować tę stronę.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Jeśli to nie pomoże, nadal możesz oglądać ten film na YouTube, jednak bez dodatkowej prywatności, jaką zapewnia Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player zapewnia czyste środowisko oglądania bez spersonalizowanych reklam i sprawia, że aktywność związana z oglądaniem filmów nie wpływa na rekomendacje YouTube'a."
}
diff --git a/build/windows/pages/duckplayer/locales/pt/duckplayer.json b/build/windows/pages/duckplayer/locales/pt/duckplayer.json
index a5bfca188..2ff6fad9c 100644
--- a/build/windows/pages/duckplayer/locales/pt/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/pt/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ERRO: ID de vídeo inválido",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "O YouTube não permite que o Duck Player carregue este vídeo",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "O YouTube não permite que vejas este vídeo fora do YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "O YouTube está a bloquear o carregamento deste vídeo. Se estiveres a usar uma VPN, tenta desativá-la e recarregar esta página.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Se isto não funcionar, continuas a poder ver este vídeo no YouTube, mas sem a privacidade adicional do Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "O Duck Player oferece uma experiência de visualização limpa sem anúncios personalizados e evita que as atividades de visualização influenciem as recomendações do YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/ro/duckplayer.json b/build/windows/pages/duckplayer/locales/ro/duckplayer.json
index bfafec70e..25ffac535 100644
--- a/build/windows/pages/duckplayer/locales/ro/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/ro/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "EROARE: ID video incorect",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nu permite Duck Player să încarce acest videoclip",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nu permite ca acest videoclip să fie vizionat în afara YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blochează încărcarea acestui videoclip. Dacă folosești un VPN, încearcă să-l dezactivezi și să reîncarci această pagină.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Dacă acest lucru nu funcționează, poți viziona în continuare acest videoclip pe YouTube, dar fără confidențialitatea suplimentară oferită de Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player oferă o experiență de vizionare fără perturbări, fără reclame personalizate și împiedică activitatea de vizionare să îți influențeze recomandările YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/ru/duckplayer.json b/build/windows/pages/duckplayer/locales/ru/duckplayer.json
index 4bf5cc0c1..d1a3fc528 100644
--- a/build/windows/pages/duckplayer/locales/ru/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/ru/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "ОШИБКА: Неверный идентификатор видео",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube не позволяет проигрывателю Duck Player загрузить это видео",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube не позволяет смотреть это видео вне своей платформы.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Вы по-прежнему можете посмотреть этот ролик на YouTube, но уже без дополнительной защиты Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube блокирует загрузку этого видео. Если вы используете VPN, отключите ее и перезагрузите страницу.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Если это не даст результата, вы все равно сможете просмотреть это видео на YouTube, но без дополнительной защиты конфиденциальности, обеспечиваемой Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Проигрыватель Duck Player обеспечивает беспрепятственный просмотр без персонализированной рекламы и влияния просмотренных роликов на рекомендации в YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/sk/duckplayer.json b/build/windows/pages/duckplayer/locales/sk/duckplayer.json
index 88a2cc3a5..a68cdeb89 100644
--- a/build/windows/pages/duckplayer/locales/sk/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/sk/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "CHYBA: Neplatný identifikátor videa",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube nedovolí Duck Playeru načítať toto video",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube nepovoľuje, aby sa toto video pozeralo mimo YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Toto video si môžeš pozrieť aj na YouTube, ale bez dodatočného súkromia Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blokuje načítanie tohto videa. Ak používaš sieť VPN, skús ju vypnúť a znova načítať túto stránku.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Ak to nefunguje, môžeš si toto video pozrieť aj na YouTube, ale bez dodatočnej ochrany súkromia, ktorú poskytuje Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player poskytuje čisté zobrazenie bez personalizovaných reklám a zabraňuje tomu, aby aktivita pri sledovaní ovplyvňovala vaše odporúčania v službe YouTube."
}
diff --git a/build/windows/pages/duckplayer/locales/sl/duckplayer.json b/build/windows/pages/duckplayer/locales/sl/duckplayer.json
index 7d4a89155..977830ef4 100644
--- a/build/windows/pages/duckplayer/locales/sl/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/sl/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "NAPAKA: Neveljaven ID videoposnetka",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube ne dovoli predvajalniku Duck Player naložiti tega videa",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube ne dovoljuje, da si ta videoposnetek ogledate zunaj YouTuba.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Ta videoposnetek si lahko še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube preprečuje nalaganje tega videoposnetka. Če uporabljate omrežje VPN, ga poskusite izklopiti in znova naložite to stran.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Če to ne deluje, si lahko ta videoposnetek še vedno ogledate na YouTubu, vendar brez dodane zasebnosti predvajalnika Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Predvajalnik Duck Player zagotavlja čisto izkušnjo gledanja brez prilagojenih oglasov in preprečuje, da bi dejavnost gledanja vplivala na vaša priporočila v YouTubu."
}
diff --git a/build/windows/pages/duckplayer/locales/sv/duckplayer.json b/build/windows/pages/duckplayer/locales/sv/duckplayer.json
index cb5b75e4c..444e91750 100644
--- a/build/windows/pages/duckplayer/locales/sv/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/sv/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "FEL: Ogiltigt video-ID",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube låter inte Duck Player läsa in den här videon",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube tillåter inte att den här videon ses utanför YouTube.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Du kan fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube blockerar den här videon från att läsas in. Om du använder ett VPN kan du prova stänga av det och läsa in sidan igen.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Om det inte fungerar kan du fortfarande se den här videon på YouTube, men utan den extra integriteten från Duck Player.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player ger en störningsfri visningsupplevelse utan personliga annonser och förhindrar att din tittaraktivitet påverkar YouTube-rekommendationer."
}
diff --git a/build/windows/pages/duckplayer/locales/tr/duckplayer.json b/build/windows/pages/duckplayer/locales/tr/duckplayer.json
index c23cab2bb..e733fcaa6 100644
--- a/build/windows/pages/duckplayer/locales/tr/duckplayer.json
+++ b/build/windows/pages/duckplayer/locales/tr/duckplayer.json
@@ -32,6 +32,26 @@
"title" : "HATA: Geçersiz video kimliği",
"note" : "Shown when the page URL doesn't match a known video ID. Note for translators: The tag makes the word 'ERROR:' bold. Depending on the grammar of the target language, you might need to move it so that the correct word is emphasized."
},
+ "blockedVideoErrorHeading" : {
+ "title" : "YouTube, Duck Player'ın bu videoyu yüklemesine izin vermiyor",
+ "note" : "Message shown when YouTube has blocked playback of a video"
+ },
+ "blockedVideoErrorMessage1" : {
+ "title" : "YouTube, bu videonun YouTube dışında izlenmesine izin vermiyor.",
+ "note" : "Explanation on why the error is happening."
+ },
+ "blockedVideoErrorMessage2" : {
+ "title" : "Bu videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "A message explaining that the blocked video can be watched directly on YouTube."
+ },
+ "signInRequiredErrorMessage1" : {
+ "title" : "YouTube bu videonun yüklenmesini engelliyor. VPN kullanıyorsanız, VPN'i kapatıp bu sayfayı yeniden yüklemeyi deneyin.",
+ "note" : "Explanation on why the error is happening and a suggestions on how to solve it."
+ },
+ "signInRequiredErrorMessage2" : {
+ "title" : "Bu işe yaramazsa, videoyu Duck Player'ın sunduğu ek gizlilik olmadan YouTube'da izleyebilirsiniz.",
+ "note" : "More troubleshooting tips for this specific error"
+ },
"tooltipInfo" : {
"title" : "Duck Player, kişiselleştirilmiş reklamlar olmadan temiz bir görüntüleme deneyimi sağlar ve görüntüleme etkinliğinin YouTube önerilerinizi etkilemesini önler."
}
diff --git a/special-pages/pages/duckplayer/app/components/Button.module.css b/special-pages/pages/duckplayer/app/components/Button.module.css
index 19cc263cd..5d6845ee2 100644
--- a/special-pages/pages/duckplayer/app/components/Button.module.css
+++ b/special-pages/pages/duckplayer/app/components/Button.module.css
@@ -15,7 +15,7 @@
text-decoration: none;
[data-layout="mobile"] & {
- background-color: #2f2f2f;
+ background-color: rgba(255, 255, 255, 0.12);
}
}
diff --git a/special-pages/pages/duckplayer/app/components/Components.jsx b/special-pages/pages/duckplayer/app/components/Components.jsx
index 479650844..b866a9170 100644
--- a/special-pages/pages/duckplayer/app/components/Components.jsx
+++ b/special-pages/pages/duckplayer/app/components/Components.jsx
@@ -14,6 +14,7 @@ import { Settings } from '../settings.js';
import { EmbedSettings } from '../embed-settings.js';
import { SwitchBarDesktop } from './SwitchBarDesktop.jsx';
import { SwitchProvider } from '../providers/SwitchProvider.jsx';
+import { YouTubeError } from './YouTubeError';
export function Components() {
const settings = new Settings({
@@ -81,6 +82,26 @@ export function Components() {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+