This commit is contained in:
Ashley
2022-08-05 22:33:38 +03:00
committed by GitHub
parent f431111611
commit 72143fede3
100 changed files with 12438 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
:root {
--text-primary: #fff;
--text-secondary: #808080;
--text-link: #3ea6ff;
--app-background: #181818;
--context-menu-background: #333;
--border-color: #444;
--item-hover-background: #373737;
--item-active-background: #383838;
--top-bar-background: #202020;
--guide-background: #212121;
--thumbnail-background: #252525;
--channel-info-background: #181818;
--channel-contents-background: #0f0f0f;
}

View File

@@ -0,0 +1,19 @@
:root {
--text-primary: #000;
--text-secondary: #606060;
--text-link: #3ea6ff;
--app-background: #f9f9f9;
--context-menu-background: #f2f2f2;
--border-color: #c5c5c5;
--item-hover-background: #f2f2f2;
--item-active-background: #E5E5E5;;
--top-bar-background: #FFF;
--guide-background: #FFF;
--thumbnail-background: #CCC;
--channel-info-background: #fff;
--channel-contents-background: #f9f9f9;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
* {
font-family: sans-serif;
}
.player {
background-color: #000 !important;
display: grid;
grid-template-columns: 1fr min-content;
grid-template-rows: max-content 1fr max-content max-content max-content;
gap: 0 0;
width: 100%;
height: 100%;
}
.player * {
color: #fff;
box-sizing: content-box;
}
.player.embed, video.embed {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.player * {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.player > video {
position: relative;
width: 100%;
height: 100%;
z-index: 0;
grid-area: 1 / 1 / 6 / 3;
}
.player.hide-controls > .player-title,
.player.hide-controls > .player-controls,
.player.hide-controls > .player-playback-bar-container,
.player.hide-controls > .player-menu {
display: none !important;
}
.player-title {
grid-area: 1 / 1 / 2 / 3;
color: white;
z-index: 2;
font-size: 27px;
background-image: linear-gradient(180deg, #0007 0%, #0000 100%);
padding: 8px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}
.player-controls {
padding-top: 4px;
color: #ddd !important;
width: 100%;
height: min-content;
position: relative;
bottom: 0;
z-index: 2;
background-image: linear-gradient(0deg, #0007 0%, #0007 80%, #0000 100%);
grid-area: 5 / 1 / 6 / 3;
}
.player-controls {
display: flex;
}
.player-controls > span {
line-height: 48px;
height: 48px;
font-size: 109%;
}
.player-controls-padding {
width: 12px;
}
.player-button {
cursor: pointer;
display: flex;
flex-direction: row;
transition: width ease-in 250ms;
width: 48px;
height: 48px;
font-size: 36px;
text-align: center;
line-height: 48px;
}
.player-button, .player-button * {
color: #dddddd !important;
text-decoration: none;
}
.player-button > i {
min-width: 48px;
}
.player-button:hover, .player-button:hover * {
color: #fff !important;
}
.player-volume {
overflow-x: hidden;
}
.player-volume:hover {
width: 200px;
}
.player-button-divider {
flex-grow: 1;
}
.player-button-menu {
flex-direction: column-reverse;
}
.player-menu {
grid-area: 3 / 2 / 4 / 3;
z-index: 3;
position: relative;
background-color: #000a !important;
width: 200px;
}
.player-menu > div {
overflow-y: scroll;
max-height: 300px;
}
.player-menu-item {
padding: 4px 8px;
height: 2rem;
line-height: 2rem;
color: white;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
}
.player-menu-item > .bi {
width: 16px;
height: 16px;
margin-right: 8px;
}
.player-menu-item > .bi-::before {
width: 16px;
height: 16px;
content: ""
}
.player-menu-item:hover {
background-color: #fff3 !important;
}
.player-playback-bar {
transition: width linear 100ms;
}
.player-playback-bar-container {
grid-area: 4 / 1 / 5 / 3;
height: 4px;
transition: height linear 100ms;
width: 100%;
z-index: 2;
}
.player-playback-bar-bg {
background-color: #fff3 !important;
width: calc(100% - 24px);
margin: auto;
height: 100%;
z-index: 2;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.player-playback-bar-bg > * {
grid-area: 1 / 1 / 2 / 2;
}
.player-playback-bar-container:hover {
height: 8px;
}
.player-playback-bar-buffer {
background-color: #fffa !important;
height: 100%;
width: 0;
z-index: 3;
}
.player-playback-bar-fg {
background-color: #f00 !important;
height: 100%;
width: 0;
z-index: 4;
}
.player-playback-bar-hover {
width: min-content !important;
padding: 4px;
position: fixed;
color: white;
display: none;
text-align: center;
}
.player-playback-bar-hover > span {
background-color: #000 !important;
padding: 4px;
}
.player-storyboard-image-container {
background-repeat: no-repeat;
display: inline-block;
width: 144px;
height: 81px;
}
.player-storyboard-image {
background-repeat: no-repeat;
display: inline-block;
width: 48px;
height: 27px;
background-position-x: 0;
background-position-y: 0;
transform: scale(3);
position: relative;
box-sizing: content-box;
border: 1px solid white;
top: 10px;
}
.player-buffering {
grid-area: 1 / 1 / 6 / 3;
background-color: #000A;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
.player-buffering-spinner {
width: 80px;
height: 80px;
}

View File

@@ -0,0 +1,153 @@
body, html {
margin: 0;
padding: 0;
}
* {
font-family: sans-serif;
}
.player {
background-color: #000 !important;
display: grid;
grid-template-columns: 1fr min-content;
grid-template-rows: max-content 1fr max-content max-content max-content;
gap: 0 0;
width: 100%;
/*
height: 100%;
*/
aspect-ratio: 16 / 9;
}
.player * {
color: #fff;
}
.player.embed, video.embed {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.player * {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.player > video {
position: relative;
width: 100%;
height: 100%;
z-index: 0;
grid-area: 1 / 1 / 6 / 3;
}
.player.hide-controls > .player-title,
.player.hide-controls > .player-controls,
.player.hide-controls > .player-playback-bar-container,
.player.hide-controls > .player-menu {
display: none !important;
}
.player-controls {
background-color: #0007;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
grid-area: 1 / 1 / 6 / 3;
}
.player-button {
width: 96px;
height: 96px;
font-size: 90px;
text-align: center;
line-height: 48px;
}
.player-tiny-button {
width: 40px;
font-size: 20px;
text-align: center;
}
.player-tiny-button > i {
color: #ddd;
}
.player-button, .player-button * {
color: #dddddd !important;
text-decoration: none;
}
.player-button > i {
min-width: 48px;
}
.player-button:hover, .player-button:hover * {
color: #fff !important;
}
.player-playback-bar {
transition: width linear 100ms;
}
.player-playback-bar-container {
grid-area: 4 / 1 / 5 / 3;
display: flex;
column-gap: 8px;
justify-content: center;
align-items: center;
height: 8px;
transition: height linear 100ms;
width: 100%;
z-index: 2;
margin-bottom: 10px;
}
.player-playback-bar-bg {
background-color: #fff3 !important;
width: 100%;
height: 100%;
z-index: 2;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.player-playback-bar-bg > * {
grid-area: 1 / 1 / 2 / 2;
}
.player-playback-bar-buffer {
background-color: #fffa !important;
height: 100%;
width: 0;
z-index: 3;
}
.player-playback-bar-fg {
background-color: #f00 !important;
height: 100%;
width: 0;
z-index: 4;
}
.player-buffering {
grid-area: 1 / 1 / 6 / 3;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
.player-buffering-spinner {
width: 80px;
height: 80px;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 3)"><path d="m.5 12.5v-10c0-1.1045695.8954305-2 2-2h10c1.1045695 0 2 .8954305 2 2v10c0 1.1045695-.8954305 2-2 2h-10c-1.1045695 0-2-.8954305-2-2z"/><path d="m2.5 12.5v-10c0-1.1045695.8954305-2 2-2h-2c-1 0-2 .8954305-2 2v10c0 1.1045695 1 2 2 2h2c-1.1045695 0-2-.8954305-2-2z" fill="currentColor"/><path d="m7.5 10.5-3-3 3-3"/><path d="m12.5 7.5h-8"/></g></svg>

After

Width:  |  Height:  |  Size: 568 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(2 2)"><circle cx="8.5" cy="8.5" r="8"/><path d="m10.5 9.5-4 3v-5l4-3z"/></g></svg>

After

Width:  |  Height:  |  Size: 290 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="matrix(1 0 0 -1 2 18)"><path d="m11.6427217 13.7567397-3.14377399-1.2567396h-4v-7.00000002h2l2.80105246-5.5c.57989907 0 1.07487363.2050252 1.48492373.61507546.4100508.41005058.6150761.90502516.6150755 1.48492425l-.8999994 2.40000029 4.0310597 1.34368655c.9979872.33266243 1.5591794 1.37584131 1.3086286 2.37964122l-.0684258.21997226-1.5536355 4.14302809c-.3878403 1.0342407-1.5406646 1.5582517-2.5749053 1.1704115z"/><path d="m1.5 4.5h2c.55228475 0 1 .44771525 1 1v8c0 .5522847-.44771525 1-1 1h-2c-.55228475 0-1-.4477153-1-1v-8c0-.55228475.44771525-1 1-1z"/></g></svg>

After

Width:  |  Height:  |  Size: 766 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)"><path d="m.5 9.5 9-9 9 9"/><path d="m2.5 7.5v8c0 .5522847.44771525 1 1 1h3c.55228475 0 1-.4477153 1-1v-4c0-.5522847.44771525-1 1-1h2c.5522847 0 1 .4477153 1 1v4c0 .5522847.4477153 1 1 1h3c.5522847 0 1-.4477153 1-1v-8"/></g></svg>

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -0,0 +1,16 @@
https://systemuicons.com/
=========================
home:
https://systemuicons.com/images/icons/home_door.svg
browse:
https://systemuicons.com/images/icons/compass.svg
subscriptions:
-
profile:
https://systemuicons.com/images/icons/user_male_circle.svg
search:
https://systemuicons.com/images/icons/search.svg
like:
https://systemuicons.com/images/icons/thumbs_up.svg
dislike:
https://systemuicons.com/images/icons/thumbs_down.svg

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(2 3)"><path d="m11.6427217 13.7567397-3.14377399-1.2567396h-4v-7.00000002h2l2.80105246-5.5c.57989907 0 1.07487363.2050252 1.48492373.61507546.4100508.41005058.6150761.90502516.6150755 1.48492425l-.8999994 2.40000029 4.0310597 1.34368655c.9979872.33266243 1.5591794 1.37584131 1.3086286 2.37964122l-.0684258.21997226-1.5536355 4.14302809c-.3878403 1.0342407-1.5406646 1.5582517-2.5749053 1.1704115z"/><path d="m1.5 4.5h2c.55228475 0 1 .44771525 1 1v8c0 .5522847-.44771525 1-1 1h-2c-.55228475 0-1-.4477153-1-1v-8c0-.55228475.44771525-1 1-1z"/></g></svg>

After

Width:  |  Height:  |  Size: 759 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(2 2)"><circle cx="8.5" cy="8.5" r="8"/><path d="m14.5 13.5c-.6615287-2.2735217-3.1995581-3.0251263-6-3.0251263-2.72749327 0-5.27073171.8688092-6 3.0251263"/><path d="m8.5 2.5c1.6568542 0 3 1.34314575 3 3v2c0 1.65685425-1.3431458 3-3 3-1.65685425 0-3-1.34314575-3-3v-2c0-1.65685425 1.34314575-3 3-3z"/></g></svg>

After

Width:  |  Height:  |  Size: 519 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="8.5" cy="8.5" r="5"/><path d="m17.571 17.5-5.571-5.5"/></g></svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 3)"><path d="m7.5.5c.35132769 0 .69661025.02588228 1.03404495.07584411l.50785434 1.53911115c.44544792.12730646.86820077.30839026 1.26078721.53578009l1.4600028-.70360861c.5166435.39719686.9762801.86487779 1.3645249 1.388658l-.7293289 1.44720284c.2201691.39604534.3936959.82158734.5131582 1.2692035l1.5298263.5338186c.0390082.29913986.0591302.60421522.0591302.91399032 0 .35132769-.0258823.69661025-.0758441 1.03404495l-1.5391112.50785434c-.1273064.44544792-.3083902.86820077-.5357801 1.26078721l.7036087 1.4600028c-.3971969.5166435-.8648778.9762801-1.388658 1.3645249l-1.4472029-.7293289c-.39604532.2201691-.82158732.3936959-1.26920348.5131582l-.5338186 1.5298263c-.29913986.0390082-.60421522.0591302-.91399032.0591302-.35132769 0-.69661025-.0258823-1.03404495-.0758441l-.50785434-1.5391112c-.44544792-.1273064-.86820077-.3083902-1.26078723-.5357801l-1.46000277.7036087c-.51664349-.3971969-.97628006-.8648778-1.36452491-1.388658l.72932886-1.4472029c-.2203328-.39633993-.39395403-.82222042-.51342462-1.27020241l-1.52968981-.53381682c-.03892294-.29882066-.05900023-.60356226-.05900023-.91299317 0-.35132769.02588228-.69661025.07584411-1.03404495l1.53911115-.50785434c.12730646-.44544792.30839026-.86820077.53578009-1.26078723l-.70360861-1.46000277c.39719686-.51664349.86487779-.97628006 1.388658-1.36452491l1.44720284.72932886c.39633995-.2203328.82222044-.39395403 1.27020243-.51342462l.53381682-1.52968981c.29882066-.03892294.60356226-.05900023.91299317-.05900023z" stroke-width=".933"/><circle cx="7.5" cy="7.5" r="3"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(2.5 4.5)"><path d="m3.65939616 0h8.68120764c.4000282 0 .7615663.23839685.9191451.6060807l2.7402511 6.3939193v4c0 1.1045695-.8954305 2-2 2h-12c-1.1045695 0-2-.8954305-2-2v-4l2.74025113-6.3939193c.15757879-.36768385.51911692-.6060807.91914503-.6060807z"/><path d="m0 7h4c.55228475 0 1 .44771525 1 1v1c0 .55228475.44771525 1 1 1h4c.5522847 0 1-.44771525 1-1v-1c0-.55228475.4477153-1 1-1h4"/></g></svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@@ -0,0 +1 @@
<svg height="21" viewBox="0 0 21 21" width="21" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" transform="translate(3 3)"><path d="m.5 12.5v-10c0-1.1045695.8954305-2 2-2h10c1.1045695 0 2 .8954305 2 2v10c0 1.1045695-.8954305 2-2 2h-10c-1.1045695 0-2-.8954305-2-2z"/><path d="m12.5 12.5v-10c0-1.1045695-.8954305-2-2-2h2c1 0 2 .8954305 2 2v10c0 1.1045695-1 2-2 2h-2c1.1045695 0 2-.8954305 2-2z" fill="currentColor"/><path d="m7.5 10.5 3-3-3-3"/><path d="m10.5 7.5h-8"/></g></svg>

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,735 @@
class Player {
constructor(query, info, sources, externalPlayer, externalPlayerType) {
// vars
this.externalPlayerType = externalPlayerType ?? "html5";
this.muted = false;
this.info = info;
this.sources = sources;
this.__videoElement = document.querySelector(query);
this.__videoElement.removeAttribute("controls");
this.__externalPlayer = externalPlayer;
// container
const container = document.createElement("div");
container.classList.add("player");
this.__videoElement.parentElement.appendChild(container);
container.appendChild(this.__videoElement);
this.container = container;
if (info.embed) {
this.container.classList.add("embed");
this.__videoElement.classList.remove("embed");
}
// default source
switch (this.externalPlayerType) {
case "html5":
for (let source of sources) {
if (source.height <= 720) {
this.__videoElement.src = source.src;
break;
}
}
break;
case "hls.js":
for (let level = this.__externalPlayer.levels.length - 1; level >= 0; level--) {
if (this.__externalPlayer.levels[level].height <= 720) {
this.__externalPlayer.currentLevel = level;
break;
}
}
break;
case "shaka":
let variants = this.__externalPlayer.getVariantTracks();
for (let variant = variants.length - 1; variant >= 0; variant--) {
let v = variants[variant];
if (v.height <= 720) {
this.__externalPlayer.selectVariantTrack(v, true);
break;
}
}
break;
}
// controls
const createButton = (tag, icon) => {
const b = document.createElement(tag);
b.classList.add("player-button");
if (icon !== "")
b.innerHTML = `<i class="bi bi-${icon}"></i>`;
return b;
}
this.controls = {
play: createButton("div", "play-fill"),
pause: createButton("div", "pause-fill"),
volume: createButton("div", "volume-up-fill"),
time: document.createElement("span"),
skipToLive: createButton("div", "skip-forward-fill"),
div: document.createElement("div"),
settings: createButton("div", "gear-fill"),
embed: createButton("a", ""),
pip: createButton("div", "pip"),
fullscreen: createButton("div", "fullscreen")
}
if (!info.live) this.controls.skipToLive.style.display = "none"
const controlHolder = document.createElement("div");
controlHolder.classList.add("player-controls");
this.controls.embed.innerHTML = "<span style='text-align: center; width: 100%'>l<b>t</b></span>";
this.controls.embed.setAttribute("target", "_blank");
this.controls.embed.setAttribute("href", "/watch?v=" + info.id);
if (!info.embed) this.controls.embed.style.display = "none";
const els = [
document.createElement("div"),
document.createElement("div"),
]
for (const padding of els)
padding.classList.add("player-controls-padding");
controlHolder.appendChild(els[0]);
for (const control of Object.values(this.controls)) {
controlHolder.appendChild(control);
}
controlHolder.appendChild(els[1]);
container.appendChild(controlHolder);
this.controls.play.onclick = () => this.togglePlayPause();
this.controls.pause.onclick = () => this.togglePlayPause();
this.controls.volume.onclick = e => this.mute(e);
this.controls.volume.classList.add("player-volume");
this.controls.fullscreen.onclick = () => this.fullscreen();
this.controls.skipToLive.onclick = () => this.skipToLive();
if (document.pictureInPictureEnabled === true)
this.controls.pip.onclick = () => this.pip();
else
this.controls.pip.style.display = "none";
let vol = null;
if (localStorage !== undefined)
vol = localStorage?.getItem("ltvideo.volume");
let volumeRange = document.createElement("input");
volumeRange.oninput = e => this.setVolume(e);
volumeRange.setAttribute("min", "0");
volumeRange.setAttribute("max", "1");
volumeRange.setAttribute("step", "0.01");
volumeRange.setAttribute("value", vol ?? "1");
volumeRange.setAttribute("type", "range");
if (vol != null)
this.setVolume({target: {value: Number(vol)}});
this.controls.volume.appendChild(volumeRange);
this.controls.div.classList.add("player-button-divider")
// playback bar
this.playbackBar = {
bg: document.createElement("div"),
played: document.createElement("div"),
buffered: document.createElement("div"),
hover: document.createElement("div"),
sb: document.createElement("div"),
sbC: document.createElement("div"),
hoverText: document.createElement("span")
}
this.playbackBar.bg.classList.add("player-playback-bar");
this.playbackBar.bg.classList.add("player-playback-bar-bg");
this.playbackBar.played.classList.add("player-playback-bar");
this.playbackBar.played.classList.add("player-playback-bar-fg");
this.playbackBar.buffered.classList.add("player-playback-bar");
this.playbackBar.buffered.classList.add("player-playback-bar-buffer");
this.playbackBar.bg.appendChild(this.playbackBar.buffered);
this.playbackBar.bg.appendChild(this.playbackBar.played);
this.playbackBar.hover.classList.add("player-playback-bar-hover");
if (!this.info.live) {
this.playbackBar.sb.classList.add("player-storyboard-image");
this.playbackBar.sbC.classList.add("player-storyboard-image-container");
this.playbackBar.sb.style.backgroundImage = `url("/proxy/storyboard/${info.id}")`;
this.playbackBar.sbC.appendChild(this.playbackBar.sb);
} else {
this.playbackBar.sb.remove();
}
let playbackBarContainer = document.createElement("div");
playbackBarContainer.classList.add("player-playback-bar-container")
this.playbackBar.bg.onclick = e => {
this.playbackBarSeek(e)
}
this.playbackBar.bg.ondragover = e => {
this.playbackBarSeek(e)
}
this.playbackBar.bg.onmouseenter = () => {
this.playbackBar.hover.style.display = "block";
}
this.playbackBar.bg.onmouseleave = () => {
this.playbackBar.hover.style.display = "none";
}
this.playbackBar.bg.onmousemove = e => {
this.moveHover(e)
}
playbackBarContainer.appendChild(this.playbackBar.bg);
this.playbackBar.hover.appendChild(this.playbackBar.sbC)
this.playbackBar.hover.appendChild(this.playbackBar.hoverText)
playbackBarContainer.appendChild(this.playbackBar.hover);
container.appendChild(playbackBarContainer);
// title
this.titleElement = document.createElement("div");
this.titleElement.classList.add("player-title");
this.titleElement.innerText = info.title;
container.appendChild(this.titleElement);
if (!info.embed)
this.titleElement.style.display = "none";
// events
container.onfullscreenchange = () => {
if (!document.fullscreenElement) {
this.controls.fullscreen.querySelector("i").setAttribute("class", "bi bi-fullscreen");
if (!info.embed)
this.titleElement.style.display = "none";
} else {
this.titleElement.style.display = "block";
this.controls.fullscreen.querySelector("i").setAttribute("class", "bi bi-fullscreen-exit");
}
}
const updatePlayButtons = () => {
if (this.__videoElement.paused) {
this.controls.pause.style.display = "none";
this.controls.play.style.display = "block";
} else {
this.controls.pause.style.display = "block";
this.controls.play.style.display = "none";
}
}
this.__videoElement.onplay = () => updatePlayButtons();
this.__videoElement.onpause = () => updatePlayButtons();
updatePlayButtons();
this.__videoElement.onclick = () => this.togglePlayPause();
this.__videoElement.ondblclick = () => this.fullscreen();
this.container.onkeydown = e => this.keyboardHandler(e);
this.container.onmousemove = () => {
let d = new Date();
d.setSeconds(d.getSeconds() + 3);
this.controlsDisappearTimeout = d.getTime();
}
switch (this.externalPlayerType) {
case "shaka":
externalPlayer.addEventListener("variantchanged", () => {
this.updateMenu();
});
externalPlayer.addEventListener('error', this.fallbackFromShaka);
break;
case "hls.js":
// uhhhhhh...
break;
}
// menu
this.controls.settings.onclick = e => this.menuButtonClick(e);
this.controls.settings.setAttribute("data-action", "toggle");
this.controls.settings.querySelector("i").setAttribute("data-action", "toggle");
this.updateMenu(sources);
// buffering
this.bufferingScreen = document.createElement("div");
this.bufferingScreen.classList.add("player-buffering");
this.container.appendChild(this.bufferingScreen);
let bufferingSpinner = document.createElement("img");
bufferingSpinner.classList.add("player-buffering-spinner");
bufferingSpinner.src = "/img/spinner.gif";
this.bufferingScreen.appendChild(bufferingSpinner);
setInterval(() => this.update(), 100);
}
togglePlayPause() {
if (this.__videoElement.paused)
this.__videoElement.play();
else
this.__videoElement.pause();
}
updateMenu() {
const makeButton = (label, action, icon) => {
const b = document.createElement("div");
//todo: yes fix this
b.innerHTML = `<i class="bi bi-${icon}"></i>${label}`;
b.onclick = e => this.menuButtonClick(e);
b.setAttribute("data-action", action)
b.classList.add("player-menu-item")
return b;
}
const makeMenu = (id, buttons) => {
const menu = document.createElement("div");
menu.id = id;
for (const button of buttons) {
menu.appendChild(makeButton(button.label, button.action, button.icon));
}
return menu;
}
if (this.menuElement) {
this.menuElement.remove();
this.menuElement = undefined;
}
this.menuElement = document.createElement("div");
this.menuElement.classList.add("player-menu");
this.menuElement.appendChild(makeMenu("menu-main", [
{
icon: "sliders",
label: "Quality",
action: "menu res"
},
{
icon: "badge-cc",
label: "Subtitles",
action: "menu sub"
},
{
icon: "speedometer2",
label: "Speed",
action: "menu speed"
}
]))
const resButtons = [
{
icon: "arrow-left",
label: "Back",
action: "menu main"
}
]
switch (this.externalPlayerType) {
case "html5":
for (const index in this.sources) {
resButtons.push({
icon: this.sources[index].src === this.__videoElement.src ? "check2" : "",
label: this.sources[index].label,
action: "videosrc " + index
});
}
break;
case "shaka":
resButtons.pop();
let tracks = this.__externalPlayer.getVariantTracks();
for (const index in tracks) {
if (tracks[index].audioId === 2)
resButtons.unshift({
icon: tracks[index].active ? "check2" : "",
label: tracks[index].height + "p",
action: "shakavariant " + index
});
}
resButtons.unshift({
icon: this.__externalPlayer.getConfiguration().abr.enabled ? "check2" : "",
label: "Auto",
action: "shakavariant -1"
});
resButtons.unshift(
{
icon: "arrow-left",
label: "Back",
action: "menu main"
});
break;
case "hls.js":
resButtons.pop();
for (const level in this.__externalPlayer.levels) {
resButtons.unshift({
icon: level === this.__externalPlayer.currentLevel ? "check2" : "",
label: this.__externalPlayer.levels[level].height + "p",
action: "hlslevel " + level
});
}
resButtons.unshift(
{
icon: -1 === this.__externalPlayer.currentLevel ? "check2" : "",
label: "Auto",
action: "hlslevel -1"
});
resButtons.unshift(
{
icon: "arrow-left",
label: "Back",
action: "menu main"
});
break;
}
this.menuElement.appendChild(makeMenu("menu-res", resButtons));
const subButtons = [
{
icon: "arrow-left",
label: "Back",
action: "menu main"
}
]
for (let index = 0; index < this.__videoElement.textTracks.length; index++) {
if (this.__videoElement.textTracks[index].label.includes("Shaka Player")) continue;
subButtons.push({
icon: this.__videoElement.textTracks[index].mode === "showing" ? "check2" : "",
label: this.__videoElement.textTracks[index].label,
action: "texttrack " + index
});
}
this.menuElement.appendChild(makeMenu("menu-sub", subButtons));
this.menuElement.appendChild(makeMenu("menu-speed", [
{
icon: "arrow-left",
label: "Back",
action: "menu main"
},
{
icon: this.__videoElement.playbackRate === 0.25 ? "check2" : "",
label: "0.25",
action: "speed 0.25"
},
{
icon: this.__videoElement.playbackRate === 0.50 ? "check2" : "",
label: "0.50",
action: "speed 0.5"
},
{
icon: this.__videoElement.playbackRate === 0.75 ? "check2" : "",
label: "0.75",
action: "speed 0.75"
},
{
icon: this.__videoElement.playbackRate === 1 ? "check2" : "",
label: "Normal",
action: "speed 1"
},
{
icon: this.__videoElement.playbackRate === 1.25 ? "check2" : "",
label: "1.25",
action: "speed 1.25"
},
{
icon: this.__videoElement.playbackRate === 1.50 ? "check2" : "",
label: "1.50",
action: "speed 1.5"
},
{
icon: this.__videoElement.playbackRate === 1.75 ? "check2" : "",
label: "1.75",
action: "speed 1.75"
},
{
icon: this.__videoElement.playbackRate === 2 ? "check2" : "",
label: "2",
action: "speed 2"
},
]))
this.container.appendChild(this.menuElement);
for (const child of this.menuElement.children) {
if (child.tagName === "DIV")
child.style.display = "none";
}
}
openMenu(id) {
for (const child of this.menuElement.children) {
if (child.tagName === "DIV")
child.style.display = "none";
}
try {
this.menuElement.querySelector("#menu-" + id).style.display = "block";
} catch {
// intended
}
}
menuButtonClick(e) {
let args = (e.target.getAttribute("data-action") ?? e.target.parentElement.getAttribute("data-action")).split(" ");
let command = args.shift();
let closeMenu = true;
switch (command) {
case "toggle":
closeMenu = this.menuElement.clientHeight !== 0;
if (!closeMenu)
this.openMenu("main");
break;
case "menu":
this.openMenu(args[0]);
closeMenu = false;
break;
case "speed":
this.__videoElement.playbackRate = Number.parseFloat(args[0]);
this.updateMenu();
break;
case "texttrack":
let i = Number.parseFloat(args[0]);
for (let index = 0; index < this.__videoElement.textTracks.length; index++) {
this.__videoElement.textTracks[index].mode = "hidden";
}
this.__videoElement.textTracks[i].mode = "showing";
this.updateMenu();
break;
case "videosrc":
let time = this.__videoElement.currentTime;
let shouldPlay = !this.__videoElement.paused;
this.__videoElement.src = this.sources[Number.parseFloat(args[0])].src;
this.__videoElement.currentTime = time;
if (shouldPlay)
this.__videoElement.play();
this.updateMenu();
break;
case "shakavariant":
if (args[0] !== "-1")
this.__externalPlayer.selectVariantTrack(this.__externalPlayer.getVariantTracks()[Number.parseFloat(args[0])], true, 2)
this.__externalPlayer.configure({abr: {enabled: args[0] === "-1"}})
break;
case "hlslevel":
this.__externalPlayer.nextLevel = Number.parseInt(args[0]);
break;
}
if (closeMenu)
this.openMenu();
};
mute(e) {
if (e.target.tagName === "INPUT") return;
this.muted = !this.muted;
if (this.muted) {
this.controls.volume.querySelector("i").setAttribute("class", "bi bi-volume-mute-fill");
this.__videoElement.volume = 0;
} else {
this.controls.volume.querySelector("i").setAttribute("class", "bi bi-volume-up-fill");
this.__videoElement.volume = this.controls.volume.querySelector("input").value;
}
}
fullscreen() {
if (!document.fullscreenElement) {
this.container.requestFullscreen();
} else {
document.exitFullscreen();
}
}
pip() {
this.__videoElement.requestPictureInPicture();
}
timeUpdate() {
if (this.info.live) {
let timeBack = this.__videoElement.duration - this.__videoElement.currentTime;
this.controls.time.innerHTML = timeBack > 10 ? this.getTimeString(timeBack) : "LIVE";
} else
this.controls.time.innerHTML = this.getTimeString(this.__videoElement.currentTime) + " / " + this.getTimeString(this.__videoElement.duration);
this.playbackBar.played.style.width = ((this.__videoElement.currentTime / this.__videoElement.duration) * 100) + "%";
this.playbackBar.buffered.style.width = ((this.getLoadEnd() / this.__videoElement.duration) * 100) + "%";
if (this.controlsDisappearTimeout - Date.now() < 0 && !this.container.classList.contains("hide-controls") && !this.__videoElement.paused)
this.container.classList.add("hide-controls");
if (this.controlsDisappearTimeout - Date.now() > 0 && this.container.classList.contains("hide-controls"))
this.container.classList.remove("hide-controls");
if (this.__videoElement.paused && this.container.classList.contains("hide-controls"))
this.container.classList.add("hide-controls");
}
setVolume(e) {
this.__videoElement.volume = e.target.value;
localStorage.setItem("ltvideo.volume", e.target.value);
}
getLoadEnd() {
let longest = -1;
for (let i = 0; i < this.__videoElement.buffered.length; i++) {
const end = this.__videoElement.buffered.end(i);
if (end > longest) longest = end;
}
return longest;
}
playbackBarSeek(e) {
let percentage = (e.offsetX / (this.playbackBar.bg.clientLeft + this.playbackBar.bg.clientWidth));
this.playbackBar.played.style.width = (percentage * 100) + "%";
this.__videoElement.currentTime = this.__videoElement.duration * percentage;
}
moveHover(e) {
let percentage = (e.offsetX / (this.playbackBar.bg.clientLeft + this.playbackBar.bg.clientWidth));
let rPercent = Math.round(percentage * 100);
if (!this.info.live) {
this.playbackBar.sb.style.backgroundPositionX = `-${rPercent % 10 * 48}px`;
this.playbackBar.sb.style.backgroundPositionY = `-${Math.floor(rPercent / 10) * 27}px`;
}
this.playbackBar.hover.style.top = (this.playbackBar.bg.getBoundingClientRect().y - 4 - this.playbackBar.hover.clientHeight) + 'px';
this.playbackBar.hover.style.left = (e.clientX - this.playbackBar.hover.clientWidth / 2) + 'px';
this.playbackBar.hoverText.innerText = this.getTimeString(this.__videoElement.duration * percentage);
}
skipToLive() {
this.__videoElement.currentTime = this.__videoElement.duration;
}
keyboardHandler(e) {
let pd = true;
if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) return;
switch (e.code) {
case "Space":
this.togglePlayPause();
break;
case "Digit1":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.1;
break;
case "Digit2":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.2;
break;
case "Digit3":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.3;
break;
case "Digit4":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.4;
break;
case "Digit5":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.5;
break;
case "Digit6":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.6;
break;
case "Digit7":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.7;
break;
case "Digit8":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.8;
break;
case "Digit9":
if (!this.info.live)
this.__videoElement.currentTime = this.__videoElement.duration * 0.9;
break;
case "Digit0":
if (!this.info.live)
this.__videoElement.currentTime = 0;
break;
case "ArrowLeft":
if (!this.info.live)
this.__videoElement.currentTime -= 5;
break;
case "ArrowRight":
if (!this.info.live)
this.__videoElement.currentTime += 5;
break;
case "ArrowUp":
if (!this.info.live)
this.__videoElement.volume += 0.1;
break;
case "ArrowDown":
if (!this.info.live)
this.__videoElement.volume -= 0.1;
break;
case "KeyF":
this.fullscreen();
break;
case "KeyM":
this.mute({target: {tagName: ""}});
break;
default:
pd = false;
break;
}
if (pd) e.preventDefault();
}
getTimeString(s) {
let res = s < 3600 ? new Date(s * 1000).toISOString().substr(14, 5) : new Date(s * 1000).toISOString().substr(11, 8);
if (res.startsWith("0"))
res = res.substr(1);
return res;
}
update() {
this.timeUpdate();
if (this.info.live) {
let timeBack = Math.abs(this.__videoElement.currentTime - this.__videoElement.buffered.end(this.__videoElement.buffered.length - 1));
this.bufferingScreen.style.display = timeBack < .1 ? "flex" : "none";
} else {
switch (this.__videoElement.readyState) {
case 1:
this.bufferingScreen.style.display = "flex";
break;
default:
this.bufferingScreen.style.display = "none";
break;
}
}
}
async fallbackFromShaka() {
if (this.externalPlayerType !== "shaka") return;
this.externalPlayerType = "html5";
console.log("Shaka player crashed, falling back");
let cTime = this.__videoElement.currentTime;
await this.__externalPlayer.detach();
await this.__externalPlayer.destroy();
this.__videoElement.src = this.sources[0].src;
this.__externalPlayer = undefined;
this.__videoElement.currentTime = cTime;
this.updateMenu();
console.log("Fallback complete!");
}
}
const loadPlayerWithShaka = async (query, info, sources, manifestUri) => {
let player;
if (manifestUri !== undefined) {
shaka.polyfill.installAll();
let shakaUsable = shaka.Player.isBrowserSupported();
if (shakaUsable) {
const video = document.querySelector(query);
player = new shaka.Player(video);
try {
await player.load(manifestUri);
} catch (e) {
await player.destroy();
return new Player(query, info, sources, undefined, "html5");
}
}
}
return new Player(query, info, sources, await player, "shaka");
}
const loadPlayerWithHls = (query, info, manifestUri) => {
return new Promise((res, rej) => {
let hls;
const video = document.querySelector(query);
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(manifestUri);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
res(new Player(query, info, [], hls, "hls.js"));
});
} else
rej("You can't watch livestreams / premieres because hls.js is not supported in your browser.")
})
}

View File

@@ -0,0 +1,292 @@
class Player {
constructor(query, info, sources, externalPlayer, externalPlayerType) {
// vars
this.externalPlayerType = externalPlayerType ?? "html5";
this.muted = false;
this.info = info;
this.sources = sources;
this.__videoElement = document.querySelector(query);
this.__videoElement.removeAttribute("controls");
this.__externalPlayer = externalPlayer;
// container
const container = document.createElement("div");
container.classList.add("player");
this.__videoElement.parentElement.appendChild(container);
container.appendChild(this.__videoElement);
this.container = container;
if (info.embed) {
this.container.classList.add("embed");
this.__videoElement.classList.remove("embed");
}
// default source
switch (this.externalPlayerType) {
case "html5":
for (let source of sources) {
if (source.height <= 720) {
this.__videoElement.src = source.src;
break;
}
}
break;
case "hls.js":
for (let level = this.__externalPlayer.levels.length - 1; level >= 0; level--) {
if (this.__externalPlayer.levels[level].height <= 720) {
this.__externalPlayer.currentLevel = level;
break;
}
}
break;
case "shaka":
this.__externalPlayer.configure({abr: {enabled: false}})
let variants = this.__externalPlayer.getVariantTracks();
for (let variant = variants.length - 1; variant >= 0; variant--) {
let v = variants[variant];
if (v.height <= 720) {
this.__externalPlayer.selectVariantTrack(v, true);
break;
}
}
break;
}
// controls
const createButton = (tag, icon) => {
const b = document.createElement(tag);
b.classList.add("player-button");
if (icon !== "")
b.innerHTML = `<i class="bi bi-${icon}"></i>`;
return b;
}
this.controls = {
container: document.createElement("div"),
play: createButton("div", "play-fill"),
fullscreen: createButton("div", "fullscreen"),
time: document.createElement("span"),
duration: document.createElement("span"),
}
this.controls.container.classList.add("player-controls");
this.controls.container.appendChild(this.controls.play);
this.controls.fullscreen.classList.replace("player-button", "player-tiny-button")
container.appendChild(this.controls.container);
this.controls.play.onclick = () => this.togglePlayPause();
this.controls.fullscreen.onclick = () => this.fullscreen();
this.setVolume({target: {value: 1}});
// playback bar
this.playbackBar = {
bg: document.createElement("div"),
played: document.createElement("div"),
buffered: document.createElement("div")
}
this.playbackBar.bg.classList.add("player-playback-bar");
this.playbackBar.bg.classList.add("player-playback-bar-bg");
this.playbackBar.played.classList.add("player-playback-bar");
this.playbackBar.played.classList.add("player-playback-bar-fg");
this.playbackBar.buffered.classList.add("player-playback-bar");
this.playbackBar.buffered.classList.add("player-playback-bar-buffer");
this.playbackBar.bg.appendChild(this.playbackBar.buffered);
this.playbackBar.bg.appendChild(this.playbackBar.played);
let playbackBarContainer = document.createElement("div");
playbackBarContainer.classList.add("player-playback-bar-container")
this.playbackBar.bg.onclick = e => {
this.playbackBarSeek(e)
}
playbackBarContainer.appendChild(this.controls.time);
playbackBarContainer.appendChild(this.playbackBar.bg);
playbackBarContainer.appendChild(this.controls.duration);
playbackBarContainer.appendChild(this.controls.fullscreen);
container.appendChild(playbackBarContainer);
// events
container.onfullscreenchange = () => {
if (!document.fullscreenElement) {
this.controls.fullscreen.querySelector("i").setAttribute("class", "bi bi-fullscreen");
} else {
this.controls.fullscreen.querySelector("i").setAttribute("class", "bi bi-fullscreen-exit");
}
}
const updatePlayButtons = () => {
if (this.__videoElement.paused) {
this.controls.play.querySelector("i").classList.replace("bi-pause-fill", "bi-play-fill");
} else {
this.controls.play.querySelector("i").classList.replace("bi-play-fill", "bi-pause-fill");
}
}
this.__videoElement.onplay = () => updatePlayButtons();
this.__videoElement.onpause = () => updatePlayButtons();
updatePlayButtons();
this.__videoElement.onclick = e => this.toggleControls(e);
this.controls.container.onclick = e => this.toggleControls(e);
this.__videoElement.onclick = e => this.toggleControls(e);
switch (this.externalPlayerType) {
case "shaka":
externalPlayer.addEventListener("variantchanged", () => {
this.updateMenu();
});
externalPlayer.addEventListener('error', this.fallbackFromShaka);
break;
case "hls.js":
// uhhhhhh...
break;
}
// buffering
this.bufferingScreen = document.createElement("div");
this.bufferingScreen.classList.add("player-buffering");
this.container.appendChild(this.bufferingScreen);
let bufferingSpinner = document.createElement("img");
bufferingSpinner.classList.add("player-buffering-spinner");
bufferingSpinner.src = "/img/spinner.gif";
this.bufferingScreen.appendChild(bufferingSpinner);
setInterval(() => this.update(), 100);
}
togglePlayPause(e) {
if (this.__videoElement.paused)
this.__videoElement.play();
else
this.__videoElement.pause();
}
updateMenu() {
// todo: mobile resolution switching
}
fullscreen() {
if (!document.fullscreenElement) {
this.container.requestFullscreen();
} else {
document.exitFullscreen();
}
}
timeUpdate() {
if (this.info.live) {
let timeBack = this.__videoElement.duration - this.__videoElement.currentTime;
this.controls.time.innerHTML = timeBack > 10 ? this.getTimeString(timeBack) : "LIVE";
} else {
this.controls.time.innerHTML = this.getTimeString(this.__videoElement.currentTime);
this.controls.duration.innerHTML = this.getTimeString(this.__videoElement.duration);
}
this.playbackBar.played.style.width = ((this.__videoElement.currentTime / this.__videoElement.duration) * 100) + "%";
this.playbackBar.buffered.style.width = ((this.getLoadEnd() / this.__videoElement.duration) * 100) + "%";
}
setVolume(e) {
this.__videoElement.volume = 1;
localStorage.setItem("ltvideo.volume", 1);
}
getLoadEnd() {
let longest = -1;
for (let i = 0; i < this.__videoElement.buffered.length; i++) {
const end = this.__videoElement.buffered.end(i);
if (end > longest) longest = end;
}
return longest;
}
playbackBarSeek(e) {
let percentage = (e.offsetX / (this.playbackBar.bg.clientLeft + this.playbackBar.bg.clientWidth));
this.playbackBar.played.style.width = (percentage * 100) + "%";
this.__videoElement.currentTime = this.__videoElement.duration * percentage;
}
getTimeString(s) {
let res = s < 3600 ? new Date(s * 1000).toISOString().substr(14, 5) : new Date(s * 1000).toISOString().substr(11, 8);
if (res.startsWith("0"))
res = res.substr(1);
return res;
}
update() {
this.timeUpdate();
if (this.info.live) {
let timeBack = Math.abs(this.__videoElement.currentTime - this.__videoElement.buffered.end(this.__videoElement.buffered.length - 1));
this.bufferingScreen.style.display = timeBack < .1 ? "flex" : "none";
} else {
switch (this.__videoElement.readyState) {
case 1:
this.bufferingScreen.style.display = "flex";
break;
default:
this.bufferingScreen.style.display = "none";
break;
}
}
}
async fallbackFromShaka() {
if (this.externalPlayerType !== "shaka") return;
this.externalPlayerType = "html5";
console.log("Shaka player crashed, falling back");
let cTime = this.__videoElement.currentTime;
await this.__externalPlayer.detach();
await this.__externalPlayer.destroy();
this.__videoElement.src = this.sources[0].src;
this.__externalPlayer = undefined;
this.__videoElement.currentTime = cTime;
this.updateMenu();
console.log("Fallback complete!");
}
toggleControls(e) {
if (["DIV", "VIDEO"].includes(e.target.tagName))
if (this.container.classList.contains("hide-controls")) {
this.container.classList.remove("hide-controls")
} else {
this.container.classList.add("hide-controls")
}
}
}
const loadPlayerWithShaka = async (query, info, sources, manifestUri) => {
let player;
if (manifestUri !== undefined) {
shaka.polyfill.installAll();
let shakaUsable = shaka.Player.isBrowserSupported();
if (shakaUsable) {
const video = document.querySelector(query);
player = new shaka.Player(video);
try {
await player.load(manifestUri);
} catch (e) {
await player.destroy();
return new Player(query, info, sources, undefined, "html5");
}
}
}
return new Player(query, info, sources, await player, "shaka");
}
const loadPlayerWithHls = (query, info, manifestUri) => {
return new Promise((res, rej) => {
let hls;
const video = document.querySelector(query);
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(manifestUri);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
res(new Player(query, info, [], hls, "hls.js"));
});
} else
rej("You can't watch livestreams / premieres because hls.js is not supported in your browser.")
})
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,43 @@
const subscribeButtons = document.querySelectorAll("button.subscribe-button");
const subscribeToChannel = (e) => {
const channelId = e.target.attributes["data-cid"].value;
e.target.disabled = true;
let xhr = new XMLHttpRequest();
xhr.open("GET", "/Account/Subscribe?channel=" + channelId, false)
xhr.send()
e.target.disabled = false;
if (xhr.status !== 200)
alert("You need to login to subscribe to a channel")
if (xhr.responseText === "true") {
e.target.innerText = "Subscribed";
e.target.classList.add("subscribed")
} else {
e.target.innerText = "Subscribe";
e.target.classList.remove("subscribed")
}
}
if (subscribeButtons.length > 0) {
let xhr = new XMLHttpRequest();
xhr.open("GET", "/Account/SubscriptionsJson", false)
xhr.send()
let subscribedChannels = JSON.parse(xhr.responseText);
for (let i = 0; i < subscribeButtons.length; i++) {
let button = subscribeButtons[i];
if (subscribedChannels.includes(button.attributes["data-cid"].value)) {
button.innerText = "Subscribed";
button.classList.add("subscribed")
} else {
button.innerText = "Subscribe";
button.classList.remove("subscribed")
}
button.onclick = subscribeToChannel;
button.style.display = ""
}
}

View File

@@ -0,0 +1,41 @@
// ==UserScript==
// @name LightTube Redirect Button
// @namespace http://youtube.com
// @version 0.1
// @description Adds a redirect button to the YouTube watch page to redirect to LightTube
// @match https://www.youtube.com/*
// @require http://code.jquery.com/jquery-latest.js
// ==/UserScript==
(function () {
"use strict";
const createLtButton = () => {
let ltButton = document.createElement("button");
ltButton.onclick = () => {
ltButton.innerHTML = "Loading proxy, please wait...";
ltButton.disabled = true;
window.location = "https://lighttube.herokuapp.com/watch" + window.location.search;
};
ltButton.innerHTML = "Proxy (lighttube)";
ltButton.id = "lighttube-button";
return ltButton;
};
let ltButton = createLtButton();
// Add button whenever you can
setInterval(() => {
if (window.location.pathname === "/watch" && !document.getElementById("lighttube-button") && document.getElementById("sponsor-button")) {
console.log("Inserted button!");
document.getElementById("sponsor-button").parentElement.insertBefore(ltButton, document.getElementById("sponsor-button"));
}
}, 1000);
// Ping lighttube so it stays awake
setInterval(() => {
fetch("https://lighttube.herokuapp.com/").then(() => {})
}, 30000);
console.log("Pog!");
})();