Skip to content

Commit

Permalink
feat(videohosttools.ts): add thumbnail support for youtube video
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenlx committed May 13, 2021
1 parent 861c9ce commit c927ede
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 38 deletions.
14 changes: 13 additions & 1 deletion src/main.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
iframe.external-video {
div.external-video {
min-height: 360px;
min-width: 480px;
}

iframe.external-video {
min-height: inherit;
min-width: inherit;
}

div.thumbnail {
background-repeat: no-repeat;
background-size: cover;
min-width: inherit;
min-height: inherit;
}

/* hide recommended video in youtube */
.plyr--youtube iframe {
transition: 0.2s filter linear;
Expand Down
130 changes: 93 additions & 37 deletions src/modules/videoHostTools.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { parseTF } from "./tfTools";
import { assertNever } from "assert-never";
import { parse } from "query-string";
import Plyr from "plyr";
import { getSetupTool, injectTimestamp, Plyr_TF } from "./playerSetup";
import { getSetupTool, Plyr_TF } from "./playerSetup";

enum Host {
YouTube,
Bilibili,
Vimeo,
}

const playButtonHtml = `<svg aria-hidden="true" focusable="false"> <svg id="plyr-play" viewBox="0 0 18 18"><path d="M15.562 8.1L3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8z"></path></svg></svg ><span class="plyr__sr-only">Play</span>`;

interface videoInfo {
host: Host;
id: string;
iframe: URL;
src: URL;
}

export function getVideoInfo(src: URL): videoInfo | null {
Expand All @@ -38,6 +39,7 @@ export function getVideoInfo(src: URL): videoInfo | null {
iframe: new URL(
`https://player.bilibili.com/player.html${queryStr}&high_quality=1&danmaku=0`,
),
src,
};
} else {
console.log("bilibili video url not supported or invalid");
Expand All @@ -53,6 +55,7 @@ export function getVideoInfo(src: URL): videoInfo | null {
host: Host.YouTube,
id: videoId,
iframe: new URL(`https://www.youtube.com/embed/${videoId}`),
src,
};
} else {
console.log(`invalid video id: ${src.toString()}`);
Expand All @@ -65,6 +68,7 @@ export function getVideoInfo(src: URL): videoInfo | null {
host: Host.YouTube,
id: videoId,
iframe: new URL(`https://www.youtube.com/embed/${videoId}`),
src,
};
} else {
console.log(`invalid video id: ${src.toString()}`);
Expand All @@ -84,6 +88,7 @@ export function getVideoInfo(src: URL): videoInfo | null {
host: Host.Vimeo,
id: videoId,
iframe: new URL(`https://player.vimeo.com/video/${videoId}`),
src,
};
} else {
console.log("vimeo video url not supported or invalid");
Expand All @@ -98,10 +103,70 @@ export function getVideoInfo(src: URL): videoInfo | null {
export function getPlayer(url: URL): HTMLDivElement | null {
let info = getVideoInfo(url);
if (!info) return null;
const container = createDiv({ cls: "external-video" });
switch (info.host) {
case Host.YouTube: {
setupThumbnail(container, info);
return container;
}
case Host.Vimeo: {
setupPlyr(container, info);
return container;
}
case Host.Bilibili: {
container.appendChild(getIFrame(info));
container.addClass("bili-embed");
return container;
}
default:
assertNever(info.host);
}
}

function setupThumbnail(container: HTMLDivElement, info: videoInfo): void {
const { id: videoId } = info;

const thumbnail = createDiv(
{
cls: ["thumbnail", "plyr plyr--full-ui plyr--video"],
},
(el) => {
el.style.backgroundImage = `url("https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg")`;
el.appendChild(
createEl(
"button",
{
cls: "plyr__control plyr__control--overlaid",
attr: {
type: "button",
"data-plyr": "play",
"aria-label": "Play",
},
},
(button) => {
button.innerHTML = playButtonHtml;
button.onClickEvent(fakePlayHandler);
},
),
);
},
);

const iframe = createEl("iframe", {
function fakePlayHandler() {
const player = setupPlyr(container, info);
player.once("ready", function (evt) {
this.play();
});
container.removeChild(thumbnail);
}

container.appendChild(thumbnail);
}

function getIFrame(info: videoInfo) {
return createEl("iframe", {
cls: "external-video",
attr: {
class: "external-video",
src: info.iframe.toString(),
scrolling: "no",
border: "0",
Expand All @@ -112,39 +177,30 @@ export function getPlayer(url: URL): HTMLDivElement | null {
"allow-forms allow-presentation allow-same-origin allow-scripts allow-modals",
},
});
}

const container = createDiv(undefined, (el) => el.appendChild(iframe));

switch (info.host) {
case Host.YouTube:
case Host.Vimeo: {
const { timeSpan, isLoop, setPlayer } = getSetupTool(url.toString());

let ytOpt;
if (info.host === Host.YouTube) {
ytOpt = {} as any;
// set start time
if (timeSpan && timeSpan.start !== 0) ytOpt.start = timeSpan.start;
ytOpt.loop = +isLoop;
}
function setupPlyr(container: HTMLDivElement, info: videoInfo): Plyr_TF {
const iframe = getIFrame(info);

container.addClass("plyr__video-embed");
// @ts-ignore
Plyr.timeSpan = null;
const player = new Plyr(container, {
fullscreen: { enabled: false },
loop: { active: isLoop },
invertTime: false,
youtube: ytOpt,
}) as Plyr_TF;
if (setPlayer) setPlayer(player);
return container;
}
case Host.Bilibili: {
container.addClass("bili-embed");
return container;
}
default:
assertNever(info.host);
container.appendChild(iframe);
const { isLoop, timeSpan, setPlayer } = getSetupTool(info.src);
container.addClass("plyr__video-embed");
let youtube: any;
if (info.host === Host.YouTube) {
youtube = {
// set start time
start: timeSpan && timeSpan.start !== 0 ? timeSpan.start : undefined,
loop: +isLoop,
};
}
// @ts-ignore
Plyr.timeSpan = null;
const player = new Plyr(container, {
fullscreen: { enabled: false },
loop: { active: isLoop },
invertTime: false,
youtube,
}) as Plyr_TF;
if (setPlayer) setPlayer(player);
return player;
}

0 comments on commit c927ede

Please sign in to comment.