Skip to content

Commit f135967

Browse files
committed
Thumbnail overlay
1 parent 3101b42 commit f135967

File tree

6 files changed

+189
-16
lines changed

6 files changed

+189
-16
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"unprotectedTemporary": [],
3+
"features": {
4+
"duckPlayerNative": {
5+
"state": "enabled"
6+
}
7+
}
8+
}

injected/integration-test/test-pages/duckplayer/pages/player-native.html

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,16 @@
154154
</template>
155155
<script>
156156
(async () => {
157-
settingsFiles = {
158-
default: '/duckplayer/config/overlays.json',
159-
drawer: '/duckplayer/config/overlays-drawer.json',
160-
}
161-
162157
if (!new URLSearchParams(location.search).has('preview')) {
163158
return;
164159
}
165160
const platformName = new URLSearchParams(window.location.search).get('platform') || 'macos';
166161
const locale = new URLSearchParams(window.location.search).get('locale') || 'en';
167-
const settingsId = new URLSearchParams(window.location.search).get('config') || 'default';
168162

169163
await import("/build/contentScope.js").catch(console.error)
170164

171-
const settingsFile = settingsFiles[settingsId] || settingsFiles.default;
172-
const overlays = await fetch(settingsFile).then(x => x.json())
165+
const settingsFile = '/duckplayer/config/native.json';
166+
const featureSettings = await fetch(settingsFile).then(x => x.json())
173167

174168
document.dispatchEvent(new CustomEvent('content-scope-init-args', {
175169
detail: {
@@ -186,9 +180,7 @@
186180
'duckPlayerNative'
187181
]
188182
},
189-
featureSettings: {
190-
duckPlayer: overlays.features.duckPlayer.settings,
191-
}
183+
featureSettings
192184
}
193185
}))
194186
})();

injected/src/features.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const otherFeatures = /** @type {const} */ ([
3333
/** @typedef {baseFeatures[number]|otherFeatures[number]} FeatureName */
3434
/** @type {Record<string, FeatureName[]>} */
3535
export const platformSupport = {
36-
apple: ['webCompat', ...baseFeatures],
36+
apple: ['webCompat', 'duckPlayerNative', ...baseFeatures],
3737
'apple-isolated': ['duckPlayerNative', 'brokerProtection', 'performanceMetrics', 'clickToLoad', 'messageBridge', 'favicon'],
3838
android: [...baseFeatures, 'webCompat', 'breakageReporting', 'duckPlayer', 'messageBridge'],
3939
'android-broker-protection': ['brokerProtection'],

injected/src/features/duckplayer-native/duckplayer-native.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { getCurrentTimestamp } from './get-current-timestamp.js';
2-
import { mediaControl } from './media-control.js';
2+
// import { mediaControl } from './media-control.js';
33
import { muteAudio } from './mute-audio.js';
44
import { serpNotify } from './serp-notify.js';
55
import { ErrorDetection } from './error-detection.js';
6+
import { appendThumbnailOverlay } from './overlays/thumbnail-overlay.js';
67

78
/**
89
*
@@ -32,10 +33,20 @@ export async function initDuckPlayerNative(messages) {
3233
// getCurrentTimestamp();
3334
// });
3435

35-
messages.onMediaControl(({ pause }) => {
36+
const onMediaControlHandler = ({ pause }) => {
3637
console.log('MEDIA CONTROL', pause);
37-
mediaControl(pause);
38-
});
38+
39+
// TODO: move to settings.selectors.videoElementContainer or something similar
40+
const targetElement = document.querySelector('#player .html5-video-player');
41+
if (targetElement) {
42+
const destroy = appendThumbnailOverlay(/** @type {HTMLElement} */(targetElement));
43+
sideEffects.push(destroy);
44+
}
45+
46+
// mediaControl(pause);
47+
}
48+
49+
messages.onMediaControl(onMediaControlHandler);
3950

4051
messages.onMuteAudio(({ mute }) => {
4152
console.log('MUTE AUDIO', mute);
@@ -62,6 +73,8 @@ export async function initDuckPlayerNative(messages) {
6273
clearInterval(timestampPolling);
6374
});
6475

76+
onMediaControlHandler({ pause: false });
77+
6578
// return async () => {
6679
// return await Promise.all(sideEffects.map((destroy) => destroy()));
6780
// };
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/* -- VIDEO PLAYER OVERLAY */
2+
:host {
3+
position: absolute;
4+
top: 0;
5+
left: 0;
6+
bottom: 0;
7+
right: 0;
8+
z-index: 10000;
9+
--title-size: 16px;
10+
--title-line-height: 20px;
11+
--title-gap: 16px;
12+
--button-gap: 6px;
13+
--logo-size: 32px;
14+
--logo-gap: 8px;
15+
--gutter: 16px;
16+
}
17+
/* iphone 15 */
18+
@media screen and (min-width: 390px) {
19+
:host {
20+
--title-size: 20px;
21+
--title-line-height: 25px;
22+
--button-gap: 16px;
23+
--logo-size: 40px;
24+
--logo-gap: 12px;
25+
--title-gap: 16px;
26+
}
27+
}
28+
/* iphone 15 Pro Max */
29+
@media screen and (min-width: 430px) {
30+
:host {
31+
--title-size: 22px;
32+
--title-gap: 24px;
33+
--button-gap: 20px;
34+
--logo-gap: 16px;
35+
}
36+
}
37+
/* small landscape */
38+
@media screen and (min-width: 568px) {
39+
}
40+
/* large landscape */
41+
@media screen and (min-width: 844px) {
42+
:host {
43+
--title-gap: 30px;
44+
--button-gap: 24px;
45+
--logo-size: 48px;
46+
}
47+
}
48+
49+
50+
:host * {
51+
font-family: system, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
52+
}
53+
54+
:root *, :root *:after, :root *:before {
55+
box-sizing: border-box;
56+
}
57+
58+
.ddg-video-player-overlay {
59+
width: 100%;
60+
height: 100%;
61+
padding-left: var(--gutter);
62+
padding-right: var(--gutter);
63+
64+
@media screen and (min-width: 568px) {
65+
padding: 0;
66+
}
67+
}
68+
69+
.bg {
70+
position: absolute;
71+
top: 0;
72+
left: 0;
73+
right: 0;
74+
bottom: 0;
75+
color: white;
76+
background: rgba(0, 0, 0, 0.6);
77+
background-position: center;
78+
text-align: center;
79+
}
80+
81+
.logo {
82+
content: " ";
83+
position: absolute;
84+
display: block;
85+
width: 100%;
86+
height: 100%;
87+
top: 0;
88+
left: 0;
89+
right: 0;
90+
bottom: 0;
91+
background-color: transparent;
92+
background-image: url('data:image/svg+xml,<svg width="90" height="64" viewBox="0 0 90 64" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M88.119 9.88293C87.0841 6.01134 84.0348 2.96133 80.1625 1.92639C73.1438 0.0461578 44.9996 0.0461578 44.9996 0.0461578C44.9996 0.0461578 16.8562 0.0461578 9.83751 1.92639C5.96518 2.96133 2.91592 6.01134 1.88097 9.88293C0 16.9023 0 31.5456 0 31.5456C0 31.5456 0 46.1896 1.88097 53.2083C2.91592 57.0799 5.96518 60.1306 9.83751 61.1648C16.8562 63.0458 44.9996 63.0458 44.9996 63.0458C44.9996 63.0458 73.1438 63.0458 80.1625 61.1648C84.0348 60.1306 87.0841 57.0799 88.119 53.2083C90 46.1896 90 31.5456 90 31.5456C90 31.5456 90 16.9023 88.119 9.88293Z" fill="%23FF0000"/><path fill-rule="evenodd" clip-rule="evenodd" d="M36.8184 45.3313L60.2688 31.792L36.8184 18.2512V45.3313Z" fill="%23FFFFFE"/></svg>');
93+
background-size: 90px 64px;
94+
background-position: center center;
95+
background-repeat: no-repeat;
96+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import mobilecss from './thumbnail-overlay.css';
2+
import { createPolicy, html } from '../../../dom-utils.js';
3+
import { customElementsDefine, customElementsGet } from '../../../captured-globals.js';
4+
5+
export function registerCustomElements() {
6+
if (!customElementsGet(DDGVideoThumbnailOverlay.CUSTOM_TAG_NAME)) {
7+
customElementsDefine(DDGVideoThumbnailOverlay.CUSTOM_TAG_NAME, DDGVideoThumbnailOverlay);
8+
}
9+
}
10+
11+
/**
12+
* The custom element that we use to present our UI elements
13+
* over the YouTube player
14+
*/
15+
export class DDGVideoThumbnailOverlay extends HTMLElement {
16+
static CUSTOM_TAG_NAME = 'ddg-video-thumbnail-overlay-mobile';
17+
18+
policy = createPolicy();
19+
/** @type {boolean} */
20+
testMode = false;
21+
22+
connectedCallback() {
23+
this.createMarkupAndStyles();
24+
}
25+
26+
createMarkupAndStyles() {
27+
const shadow = this.attachShadow({ mode: this.testMode ? 'open' : 'closed' });
28+
const style = document.createElement('style');
29+
style.innerText = mobilecss;
30+
const container = document.createElement('div');
31+
const content = this.render();
32+
container.innerHTML = this.policy.createHTML(content);
33+
shadow.append(style, container);
34+
this.container = container;
35+
}
36+
37+
/**
38+
* @returns {string}
39+
*/
40+
render() {
41+
return html`
42+
<div class="ddg-video-player-overlay">
43+
<div class="bg ddg-vpo-bg"></div>
44+
<div class="logo"></div>
45+
</div>
46+
`.toString();
47+
}
48+
}
49+
50+
/**
51+
*
52+
* @param {HTMLElement} targetElement
53+
*/
54+
export function appendThumbnailOverlay(targetElement) {
55+
registerCustomElements();
56+
const overlay = /** @type {DDGVideoThumbnailOverlay} */ (document.createElement(DDGVideoThumbnailOverlay.CUSTOM_TAG_NAME));
57+
console.log('Overlay', overlay, targetElement);
58+
overlay.testMode = true;
59+
targetElement.appendChild(overlay);
60+
61+
return () => {
62+
document.querySelector(DDGVideoThumbnailOverlay.CUSTOM_TAG_NAME)?.remove();
63+
};
64+
}

0 commit comments

Comments
 (0)