+
+
+
@@ -26,5 +28,4 @@
-
\ No newline at end of file
diff --git a/examples/browser/web/package.json b/examples/browser/web/package.json
index 284684c0..54593919 100644
--- a/examples/browser/web/package.json
+++ b/examples/browser/web/package.json
@@ -13,6 +13,6 @@
"vite": "^3.0.0"
},
"dependencies": {
- "dashjs": "^4.4.0"
+ "shaka-player": "^4.3.8"
}
}
\ No newline at end of file
diff --git a/examples/browser/web/src/assets/player.css b/examples/browser/web/src/assets/player.css
new file mode 100644
index 00000000..ce4a420d
--- /dev/null
+++ b/examples/browser/web/src/assets/player.css
@@ -0,0 +1,423 @@
+@import url(https://fonts.googleapis.com/css?family=Material+Icons+Sharp);
+
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url(https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Me5Q.ttf) format('truetype');
+}
+
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url(https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmEU9vAw.ttf) format('truetype');
+}
+
+.shaka-container {
+ font-family: 'Roboto', sans-serif;
+}
+
+.shaka-container .shaka-bottom-controls {
+ width: 100%;
+ padding: 0;
+ padding-bottom: 0;
+ z-index: 1;
+}
+
+.shaka-container .shaka-bottom-controls {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -ms-flex-direction: column;
+ flex-direction: column;
+}
+
+.shaka-container .shaka-ad-controls {
+ -webkit-box-ordinal-group: 2;
+ -ms-flex-order: 1;
+ order: 1;
+}
+
+.shaka-container .shaka-spinner .shaka-spinner-path {
+ stroke: #ffffff;
+}
+
+.shaka-container .shaka-scrim-container {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ flex-shrink: 1;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ opacity: 0;
+ transition: opacity cubic-bezier(.4, 0, .6, 1) .6s;
+ background: linear-gradient(to top, hsla(0, 0%, 0%, 0.61), transparent 15%);
+}
+
+.shaka-container .shaka-play-button {
+ width: 100px;
+ height: 100px;
+ border-radius: 0;
+ background-color: transparent;
+ filter: invert();
+ box-shadow: none;
+ -webkit-box-ordinal-group: -3;
+ -ms-flex-order: -4;
+ order: -4;
+}
+
+.shaka-container .shaka-small-play-button {
+ -webkit-box-ordinal-group: -2;
+ -ms-flex-order: -3;
+ order: -3;
+}
+
+.shaka-container .shaka-mute-button {
+ -webkit-box-ordinal-group: -1;
+ -ms-flex-order: -2;
+ order: -2;
+}
+
+.shaka-container .shaka-controls-button-panel>* {
+ margin: 0;
+ padding: 3px 8px;
+ color: #EEE;
+ height: 40px;
+}
+
+.shaka-container .shaka-controls-button-panel>*:hover {
+ color: #FFF;
+}
+
+.shaka-container .shaka-controls-button-panel .shaka-volume-bar-container {
+ position: relative;
+ z-index: 10;
+ left: -1px;
+ -webkit-box-ordinal-group: 0;
+ -ms-flex-order: -1;
+ order: -1;
+ opacity: 0;
+ width: 0px;
+ -webkit-transition: width 0.2s cubic-bezier(0.4, 0, 1, 1);
+ height: 3px;
+ transition: width 0.2s cubic-bezier(0.4, 0, 1, 1);
+ padding: 0;
+}
+
+.shaka-container .shaka-controls-button-panel .shaka-volume-bar-container:hover,
+.shaka-container .shaka-controls-button-panel .shaka-volume-bar-container:focus {
+ display: block;
+ width: 50px;
+ opacity: 1;
+ padding: 0 6px;
+}
+
+.shaka-container .shaka-mute-button:hover+div {
+ opacity: 1;
+ width: 50px;
+ padding: 0 6px;
+}
+
+.shaka-container .shaka-current-time {
+ padding: 0 10px;
+ font-size: 12px;
+}
+
+.shaka-container .shaka-seek-bar-container {
+ height: 3px;
+ position: relative;
+ top: -1px;
+ border-radius: 0;
+ margin-bottom: 0;
+}
+
+.shaka-container .shaka-seek-bar-container .shaka-range-element {
+ opacity: 0;
+ transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1);
+}
+
+.shaka-container .shaka-seek-bar-container:hover {
+ height: 5px;
+ top: 0;
+ cursor: pointer;
+}
+
+.shaka-container .shaka-seek-bar-container:hover .shaka-range-element {
+ opacity: 1;
+ cursor: pointer;
+}
+
+.shaka-container .shaka-seek-bar-container input[type=range]::-webkit-slider-thumb {
+ background: #FF0000;
+ cursor: pointer;
+}
+
+.shaka-container .shaka-seek-bar-container input[type=range]::-moz-range-thumb {
+ background: #FF0000;
+ cursor: pointer;
+}
+
+.shaka-container .shaka-seek-bar-container input[type=range]::-ms-thumb {
+ background: #FF0000;
+ cursor: pointer;
+}
+
+.shaka-container .shaka-video-container * {
+ font-family: 'Roboto', sans-serif;
+}
+
+.shaka-container .shaka-video-container .material-icons-round {
+ font-family: 'Material Icons Sharp';
+}
+
+.shaka-container .shaka-overflow-menu,
+.shaka-container .shaka-settings-menu {
+ border-radius: 2px;
+ background: rgba(37, 37, 37, 0.9);
+ text-shadow: 0 0 2px rgb(0 0 0%);
+ -webkit-transition: opacity 0.1s cubic-bezier(0, 0, 0.2, 1);
+ transition: opacity 0.1s cubic-bezier(0, 0, 0.2, 1);
+ -moz-user-select: none;
+ -ms-user-select: none;
+ animation: fade 0.3s;
+ -webkit-user-select: none;
+ right: 10px;
+ bottom: 50px;
+ padding: 0;
+ min-width: 200px;
+}
+
+@keyframes fade {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+.shaka-container .shaka-settings-menu {
+ padding: 0 0 8px;
+}
+
+.shaka-container .shaka-settings-menu button {
+ font-size: 12px;
+}
+
+.shaka-container .shaka-settings-menu button span {
+ margin-left: 33px;
+ font-size: 13px;
+}
+
+.shaka-container .shaka-settings-menu button[aria-selected="true"] {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+.shaka-container .shaka-settings-menu button[aria-selected="true"] span {
+ -webkit-box-ordinal-group: 3;
+ -ms-flex-order: 2;
+ order: 2;
+ margin-left: 0;
+}
+
+.shaka-container .shaka-settings-menu button[aria-selected="true"] i {
+ -webkit-box-ordinal-group: 2;
+ -ms-flex-order: 1;
+ order: 1;
+ font-size: 18px;
+ padding-left: 5px;
+}
+
+.shaka-container .shaka-overflow-menu button {
+ padding: 0;
+}
+
+.shaka-container .shaka-overflow-menu button i {
+ display: none;
+}
+
+.shaka-container .shaka-overflow-menu button .shaka-overflow-button-label {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: justify;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ -webkit-box-orient: horizontal;
+ -webkit-box-direction: normal;
+ -ms-flex-direction: row;
+ flex-direction: row;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ cursor: default;
+ outline: none;
+ height: 40px;
+ -webkit-box-flex: 0;
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+}
+
+.shaka-container .shaka-overflow-menu button .shaka-overflow-button-label span {
+ -ms-flex-negative: initial;
+ flex-shrink: initial;
+ padding-left: 15px;
+ font-size: 13px;
+ font-weight: 500;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.shaka-container .shaka-overflow-menu span+span {
+ color: #FFF;
+ font-weight: 400 !important;
+ font-size: 12px !important;
+ padding-right: 8px;
+ padding-left: 0 !important;
+}
+
+.shaka-container .shaka-overflow-menu span+span:after {
+ content: "navigate_next";
+ font-family: 'Material Icons Sharp';
+ font-size: 20px;
+}
+
+.shaka-container .shaka-overflow-menu .shaka-pip-button span+span {
+ padding-right: 15px !important;
+}
+
+.shaka-container .shaka-overflow-menu .shaka-pip-button span+span:after {
+ content: "";
+}
+
+.shaka-container .shaka-back-to-overflow-button {
+ padding: 8px 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ font-size: 12px;
+ color: #eee;
+ height: 40px;
+}
+
+.shaka-container .shaka-back-to-overflow-button .material-icons-round {
+ font-size: 15px;
+ padding-right: 10px;
+}
+
+.shaka-container .shaka-back-to-overflow-button span {
+ margin-left: 3px !important;
+}
+
+.shaka-container .shaka-overflow-menu button:hover,
+.shaka-container .shaka-settings-menu button:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ cursor: pointer;
+}
+
+.shaka-container .shaka-overflow-menu button:hover label,
+.shaka-container .shaka-settings-menu button:hover label {
+ cursor: pointer;
+}
+
+.shaka-container .shaka-overflow-menu button,
+.shaka-container .shaka-settings-menu button {
+ color: #EEE;
+}
+
+.shaka-container .shaka-captions-off {
+ color: #BFBFBF;
+}
+
+.shaka-container .shaka-overflow-menu-button {
+ font-size: 18px;
+ margin-right: 5px;
+}
+
+.shaka-container .shaka-fullscreen-button:hover {
+ font-size: 25px;
+ -webkit-transition: font-size 0.1s cubic-bezier(0, 0, 0.2, 1);
+ transition: font-size 0.1s cubic-bezier(0, 0, 0.2, 1);
+}
+
+.shaka-container .shaka-overflow-menu,
+.shaka-container .shaka-settings-menu {
+ border-radius: 10px;
+}
+
+@media (prefers-color-scheme: light) {
+
+ .shaka-container .shaka-overflow-menu,
+ .shaka-container .shaka-settings-menu {
+ background: rgba(255, 255, 255, 0.9);
+ }
+
+ .shaka-container .shaka-overflow-menu span+span,
+ .shaka-container .shaka-overflow-menu button,
+ .shaka-container .shaka-settings-menu button {
+ color: #000000;
+ }
+}
+
+@media (min-width: 800px) {
+ .shaka-container .shaka-controls-button-panel {
+ -webkit-box-ordinal-group: 3;
+ -ms-flex-order: 2;
+ order: 2;
+ height: 40px;
+ padding: 0 10px;
+ }
+}
+
+@media (max-width: 800px) {
+ .shaka-container .shaka-scrim-container {
+ background: rgba(0, 0, 0, 0.5);
+ }
+
+ .shaka-container .shaka-range-container {
+ margin: 0;
+ top: 0;
+ }
+
+ .shaka-container .shaka-mute-button {
+ display: none;
+ }
+
+ .shaka-container .shaka-overflow-menu,
+ .shaka-container .shaka-settings-menu {
+ bottom: 0;
+ top: 0;
+ left: 0;
+ right: 0;
+ width: 80%;
+ margin: 10px;
+ border-radius: 10px;
+ }
+
+ .shaka-container .shaka-overflow-menu button,
+ .shaka-container .shaka-settings-menu button {
+ width: 100%;
+ height: 40px;
+ padding: 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+ }
+
+ .shaka-container .shaka-overflow-menu button span,
+ .shaka-container .shaka-settings-menu button span {
+ margin-left: 0;
+ padding-left: 15px;
+ }
+}
\ No newline at end of file
diff --git a/examples/browser/web/src/assets/style.css b/examples/browser/web/src/assets/style.css
new file mode 100644
index 00000000..62f455a7
--- /dev/null
+++ b/examples/browser/web/src/assets/style.css
@@ -0,0 +1,135 @@
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: #202020;
+ color: rgb(255, 255, 255);
+ line-height: 1.6;
+ font-family: Roboto, Arial, sans-serif;
+ font-size: 15px;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-tap-highlight-color: transparent;
+}
+
+hr {
+ width: 100%;
+ border: 1px solid transparent;
+ background-color: rgb(68, 68, 68);
+}
+
+form {
+ margin: 0.5rem 0;
+ display: none;
+ border-radius: 0.3rem;
+ background-color: rgb(68, 68, 68);
+}
+
+form input {
+ padding: 0.5rem;
+ border: none;
+ color: rgb(255, 255, 255);
+}
+
+form input[type="text"] {
+ background: transparent;
+}
+
+form input[type="text"]:focus {
+ outline: none;
+}
+
+form input[type="submit"] {
+ color: rgb(255, 255, 255);
+ background-color: rgba(0, 0, 0, 0.244);
+ cursor: pointer;
+}
+
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: #ffffff;
+ transition: background-color 5000s ease-in-out 0s;
+}
+
+#loader {
+ display: block;
+ border: 10px solid rgb(68, 68, 68);
+ border-top: 10px solid rgb(255, 255, 255);
+ border-radius: 50%;
+ width: 50px;
+ height: 50px;
+ align-self: center;
+ animation: spin 1s linear infinite;
+ margin: 0.5rem 0;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+#shaka-container {
+ height: 40vw;
+}
+
+#video-container {
+ display: none;
+ flex-direction: column;
+ position: relative;
+ width: 70vw !important;
+ margin: 0.5rem 0;
+}
+
+#metadata {
+ display: flex;
+ flex-direction: row;
+ align-self: left;
+ margin: 0.5rem 0;
+}
+
+#metadata>#metadata-item {
+ margin: 0 0.3rem;
+ background-color: #ffffff;
+ color: rgba(0, 0, 0, 0.757);
+ font-weight: 600;
+ padding: 0.2rem 0.5rem;
+ border-radius: 0.3rem;
+}
+
+#video-container>#description {
+ align-self: left;
+ margin-left: 0.5rem;
+ font-size: medium;
+}
+
+video {
+ width: 100%;
+ height: 100%;
+}
+
+footer {
+ margin: 0.5rem 0;
+}
+
+@media screen and (max-width: 768px) {
+ video {
+ height: auto;
+ }
+
+ #shaka-container {
+ height: auto;
+ }
+
+ #video-container {
+ width: 100% !important;
+ }
+}
\ No newline at end of file
diff --git a/examples/browser/web/src/main.ts b/examples/browser/web/src/main.ts
index cc185f45..7d1ce24e 100644
--- a/examples/browser/web/src/main.ts
+++ b/examples/browser/web/src/main.ts
@@ -1,27 +1,27 @@
-import './style.css';
import { Innertube, UniversalCache } from '../../../../bundle/browser';
-import dashjs from 'dashjs';
-const description = document.getElementById('description') as HTMLDivElement;
-const form = document.querySelector('form') as HTMLFormElement;
+// @ts-ignore - Shaka's TS support is not the best.
+import shaka from 'shaka-player/dist/shaka-player.ui.js';
+
+import "shaka-player/dist/controls.css";
+
const title = document.getElementById('title') as HTMLHeadingElement;
+const description = document.getElementById('description') as HTMLDivElement;
const metadata = document.getElementById('metadata') as HTMLDivElement;
const loader = document.getElementById('loader') as HTMLDivElement;
-const video = document.getElementById('video') as HTMLVideoElement;
-const video_container = document.getElementById('video_container') as HTMLDivElement;
+const form = document.querySelector('form') as HTMLFormElement;
async function main() {
const yt = await Innertube.create({
generate_session_locally: true,
fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
- // url
const url = typeof input === 'string'
? new URL(input)
: input instanceof URL
? input
: new URL(input.url);
- // transform the url for use with our proxy
+ // Transform the url for use with our proxy.
url.searchParams.set('__host', url.host);
url.host = 'localhost:8080';
url.protocol = 'http';
@@ -32,13 +32,15 @@ async function main() {
? input.headers
: new Headers();
- // now serialize the headers
+ // Now serialize the headers.
url.searchParams.set('__headers', JSON.stringify([...headers]));
- // @ts-ignore
- input.duplex = 'half';
+ if (input instanceof Request) {
+ // @ts-ignore
+ input.duplex = 'half';
+ }
- // copy over the request
+ // Copy over the request.
const request = new Request(
url,
input instanceof Request ? input : undefined,
@@ -46,7 +48,6 @@ async function main() {
headers.delete('user-agent');
- // fetch the url
return fetch(request, init ? {
...init,
headers
@@ -60,45 +61,46 @@ async function main() {
form.animate({ opacity: [0, 1] }, { duration: 300, easing: 'ease-in-out' });
form.style.display = 'block';
- showUI(false);
+ showUI({ hidePlayer: true });
- let player: dashjs.MediaPlayerClass | undefined;
+ let player: shaka.Player | undefined;
+ let ui: shaka.ui.Overlay | undefined;
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (player) {
- player.reset();
+ player.destroy();
}
hideUI();
- let video_id;
+ let videoId;
- const video_id_or_url = document.querySelector
('input[type=text]')?.value;
+ const videoIdOrURL = document.querySelector('input[type=text]')?.value;
- if (!video_id_or_url) {
+ if (!videoIdOrURL) {
title.textContent = 'No video id or URL provided';
- showUI(false);
+ showUI({ hidePlayer: true });
return;
}
try {
- if (video_id_or_url.match(/(http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/)) {
- const endpoint = await yt.resolveURL(video_id_or_url);
+ if (videoIdOrURL.match(/(http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/)) {
+ const endpoint = await yt.resolveURL(videoIdOrURL);
if (!endpoint.payload.videoId) {
title.textContent = 'Could not resolve URL';
- showUI(false);
+ showUI({ hidePlayer: true });
return;
}
- video_id = endpoint.payload.videoId;
+ videoId = endpoint.payload.videoId;
} else {
- video_id = video_id_or_url;
+ videoId = videoIdOrURL;
}
- const info = await yt.getInfo(video_id);
+ const info = await yt.getInfo(videoId);
title.textContent = info.basic_info.title || null;
description.innerHTML = info.secondary_info?.description.toHTML() || '';
@@ -106,12 +108,12 @@ async function main() {
document.title = info.basic_info.title || '';
- metadata!.innerHTML = '';
- metadata!.innerHTML += `${info.primary_info?.published.toHTML()}
`;
- metadata!.innerHTML += `${info.primary_info?.view_count.toHTML()}
`;
- metadata!.innerHTML += `${info.basic_info.like_count} likes
`;
+ metadata.innerHTML = '';
+ metadata.innerHTML += `${info.primary_info?.published.toHTML()}
`;
+ metadata.innerHTML += `${info.primary_info?.view_count.toHTML()}
`;
+ metadata.innerHTML += `${info.basic_info.like_count} likes
`;
- showUI(true);
+ showUI({ hidePlayer: false });
const dash = await info.toDash((url) => {
url.searchParams.set('__host', url.host);
@@ -122,35 +124,155 @@ async function main() {
const uri = 'data:application/dash+xml;charset=utf-8;base64,' + btoa(dash);
- // create and append video element
- const video_element = document.querySelector('video') as HTMLVideoElement;
- video_element.setAttribute('controls', 'true');
- video_element.poster = info.basic_info.thumbnail![0].url;
-
- // use dash.js to parse the manifest
if (player) {
- player.destroy();
+ await player.destroy();
+ player = undefined;
}
- player = dashjs.MediaPlayer().create();
- player.initialize(video_element, uri, true);
- player.setInitialMediaSettingsFor('audio', { lang: 'en-US' });
+ if (ui) {
+ ui.destroy();
+ ui = undefined;
+ }
+
+ const videoEl = document.getElementById('videoel') as HTMLVideoElement;
+ const shakaContainer = document.getElementById('shaka-container') as HTMLDivElement;
+
+ shakaContainer
+ .querySelectorAll("div")
+ .forEach(node => node.remove());
+
+ shaka.polyfill.installAll();
+
+ if (shaka.Player.isBrowserSupported()) {
+ videoEl.poster = info.basic_info.thumbnail![0].url;
+
+ player = new shaka.Player(videoEl);
+ ui = new shaka.ui.Overlay(player, shakaContainer, videoEl);
+
+ const config = {
+ seekBarColors: {
+ base: 'rgba(255,255,255,.2)',
+ buffered: 'rgba(255,255,255,.4)',
+ played: 'rgb(255,0,0)',
+ },
+ fadeDelay: 0,
+ };
+
+ ui.configure(config);
+
+ const overflowMenuButton = document.querySelector('.shaka-overflow-menu-button');
+ if (overflowMenuButton) {
+ overflowMenuButton.innerHTML = 'settings';
+ }
+
+ const backToOverflowButton = document.querySelector('.shaka-back-to-overflow-button .material-icons-round');
+ if (backToOverflowButton) {
+ backToOverflowButton.innerHTML = 'arrow_back_ios_new';
+ }
+
+ player.configure({
+ streaming: {
+ bufferingGoal: 180,
+ rebufferingGoal: 0.02,
+ bufferBehind: 300
+ }
+ });
+
+ player.getNetworkingEngine()?.registerRequestFilter((_type: any, request: any) => {
+ const uri = request.uris[0];
+ const url = new URL(uri);
+ const headers = request.headers;
+
+ request.method = 'POST';
+
+ // protobuf - { 15: 0 }
+ request.body = new Uint8Array([120, 0]);
+
+ if (url.pathname === "/videoplayback") {
+ if (headers.Range) {
+ request.headers = {};
+ url.searchParams.set("range", headers.Range.split("=")[1]);
+ url.searchParams.set("alr", "yes");
+ }
+ }
+
+ request.uris[0] = url.toString();
+ });
+
+ // The UTF-8 characters "h", "t", "t", and "p".
+ const HTTP_IN_HEX = 0x68747470;
+
+ const RequestType = shaka.net.NetworkingEngine.RequestType;
+
+ player.getNetworkingEngine()?.registerResponseFilter(async (type: any, response: any) => {
+ const dataView = new DataView(response.data);
+
+ if (response.data.byteLength < 4 ||
+ dataView.getUint32(0) != HTTP_IN_HEX) {
+ // This doesn't start with "http", so it is not an ALR.
+ return;
+ }
+
+ // Interpret the response data as a URL string.
+ const response_as_string = shaka.util.StringUtils.fromUTF8(response.data);
+
+ let retry_parameters;
+
+ if (type == RequestType.MANIFEST) {
+ retry_parameters = player!.getConfiguration().manifest.retryParameters;
+ } else if (type == RequestType.SEGMENT) {
+ retry_parameters = player!.getConfiguration().streaming.retryParameters;
+ } else if (type == RequestType.LICENSE) {
+ retry_parameters = player!.getConfiguration().drm.retryParameters;
+ } else {
+ retry_parameters = shaka.net.NetworkingEngine.defaultRetryParameters();
+ }
+
+ // Make another request for the redirect URL.
+ const uris = [response_as_string];
+ const redirect_request = shaka.net.NetworkingEngine.makeRequest(uris, retry_parameters);
+ const request_operation = player!.getNetworkingEngine()!.request(type, redirect_request);
+ const redirect_response = await request_operation.promise;
+
+ // Modify the original response to contain the results of the redirect
+ // response.
+ response.data = redirect_response.data;
+ response.headers = redirect_response.headers;
+ response.uri = redirect_response.uri;
+ });
+
+ try {
+ await player.load(uri);
+ } catch (e) {
+ console.error('Could not load manifest', e);
+ }
+ } else {
+ console.error('Browser not supported!');
+ }
} catch (error) {
title.textContent = 'An error occurred (see console)';
- showUI(false);
+ showUI({ hidePlayer: true });
console.error(error);
}
});
}
-function showUI(with_video = true) {
- loader.style.display = 'none';
- video.style.display = with_video ? 'block' : 'none';
+function showUI(args: { hidePlayer?: boolean } = {
+ hidePlayer: true,
+}) {
+ const ytplayer = document.getElementById('shaka-container') as HTMLDivElement;
+
+ ytplayer.style.display = args.hidePlayer ? 'none' : 'block';
+
+ const video_container = document.getElementById('video-container') as HTMLDivElement;
video_container.animate({ opacity: [0, 1] }, { duration: 300, easing: 'ease-in-out' });
video_container.style.display = 'block';
+
+ loader.style.display = 'none';
}
function hideUI() {
+ const video_container = document.getElementById('video-container') as HTMLDivElement;
video_container.style.display = 'none';
loader.style.display = 'block';
}
diff --git a/examples/browser/web/src/style.css b/examples/browser/web/src/style.css
deleted file mode 100644
index 7acb2fd3..00000000
--- a/examples/browser/web/src/style.css
+++ /dev/null
@@ -1,90 +0,0 @@
-body {
- display: flex;
- flex-direction: column;
- align-items: center;
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- background-color: rgb(32, 32, 32);
- color: rgb(255, 255, 255);
-}
-
-hr {
- height: 1px;
- width: 100%;
- border: 1px solid transparent;
- background-color: rgb(68, 68, 68);
-}
-
-form {
- margin: 0.5rem 0;
- display: none;
-}
-
-#loader {
- display: block;
- border: 10px solid rgb(68, 68, 68);
- border-top: 10px solid rgb(255, 255, 255);
- border-radius: 50%;
- width: 50px;
- height: 50px;
- align-self: center;
- animation: spin 1s linear infinite;
- margin: 0.5rem 0;
-}
-
-@keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
-
- 100% {
- transform: rotate(360deg);
- }
-}
-
-#video_container {
- display: none;
- flex-direction: column;
- position: relative;
- width: 70vw !important;
- margin: 0.5rem 0;
-}
-
-#metadata {
- display: flex;
- flex-direction: row;
- align-self: left;
- margin: 0.5rem 0;
-}
-
-#metadata > .metadata_item {
- margin: 0 0.3rem;
- background-color: beige;
- color: black;
- font: 1em bold;
- padding: 0.2rem 0.5rem;
- border-radius: 0.3rem;
-}
-
-#video_container > #description {
- align-self: left;
- margin-left: 0.5rem;
- font-size: medium;
-}
-
-video {
- width: 100%;
- height: 40vw;
-}
-
-footer {
- margin: 0.5rem 0;
-}
-
-@media screen and (max-width: 768px) {
- video {
- height: auto;
- }
- #video_container {
- width: 100% !important;
- }
-}
\ No newline at end of file
diff --git a/src/parser/youtube/HashtagFeed.ts b/src/parser/youtube/HashtagFeed.ts
index 81113830..7a6d278b 100644
--- a/src/parser/youtube/HashtagFeed.ts
+++ b/src/parser/youtube/HashtagFeed.ts
@@ -8,9 +8,10 @@ import type Actions from '../../core/Actions.js';
import type { ApiResponse } from '../../core/Actions.js';
import type ChipCloudChip from '../classes/ChipCloudChip.js';
import type { IBrowseResponse } from '../index.js';
+import { PageHeader } from '../nodes.js';
export default class HashtagFeed extends FilterableFeed {
- header?: HashtagHeader;
+ header?: HashtagHeader | PageHeader;
contents: RichGrid;
constructor(actions: Actions, response: IBrowseResponse | ApiResponse) {
@@ -25,7 +26,7 @@ export default class HashtagFeed extends FilterableFeed {
throw new InnertubeError('Content tab has no content', tab);
if (this.page.header) {
- this.header = this.page.header.item().as(HashtagHeader);
+ this.header = this.page.header.item().as(HashtagHeader, PageHeader);
}
this.contents = tab.content.as(RichGrid);