Added preview bar to mobile

This commit is contained in:
Ajay Ramachandran
2020-02-17 15:10:50 -05:00
parent 933babb4e6
commit 219a7ba8c3
2 changed files with 111 additions and 37 deletions

View File

@@ -30,6 +30,7 @@ var sponsorSkipped = [];
var video: HTMLVideoElement; var video: HTMLVideoElement;
var onInvidious; var onInvidious;
var onMobileYouTube;
//the video id of the last preview bar update //the video id of the last preview bar update
var lastPreviewBarUpdate; var lastPreviewBarUpdate;
@@ -47,7 +48,7 @@ var title;
var channelWhitelisted = false; var channelWhitelisted = false;
// create preview bar // create preview bar
var previewBar = null; var previewBar: PreviewBar = null;
// When not null, a sponsor is currently being previewed and auto skip should be enabled. // When not null, a sponsor is currently being previewed and auto skip should be enabled.
// This is set to a timeout function when that happens that will reset it after 3 seconds. // This is set to a timeout function when that happens that will reset it after 3 seconds.
@@ -255,7 +256,7 @@ async function videoIDChange(id) {
sponsorVideoID = id; sponsorVideoID = id;
resetValues(); resetValues();
//id is not valid //id is not valid
if (!id) return; if (!id) return;
@@ -278,26 +279,19 @@ async function videoIDChange(id) {
channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true); channelIDPromise.then(() => channelIDPromise.isFulfilled = true).catch(() => channelIDPromise.isRejected = true);
//setup the preview bar //setup the preview bar
if (previewBar == null) { if (previewBar === null) {
//create it if (onMobileYouTube) {
utils.wait(getControls).then(result => { // Mobile YouTube workaround
const progressElementSelectors = [ const observer = new MutationObserver(handleMobileControlsMutations);
// For YouTube
"ytp-progress-bar-container",
"no-model cue-range-markers",
// For Invidious/VideoJS
"vjs-progress-holder"
];
for (const selector of progressElementSelectors) { observer.observe(document.getElementById("player-control-container"), {
const el = document.getElementsByClassName(selector); attributes: true,
childList: true,
if (el && el.length && el[0]) { subtree: true
previewBar = new PreviewBar(el[0]); });
break; } else {
} utils.wait(getControls).then(createPreviewBar);
} }
});
} }
//warn them if they had unsubmitted times //warn them if they had unsubmitted times
@@ -361,6 +355,56 @@ async function videoIDChange(id) {
} }
} }
function handleMobileControlsMutations(): void {
let mobileYouTubeSelector = ".progress-bar-background";
if (previewBar !== null) {
if (document.body.contains(previewBar.container)) {
updatePreviewBarPositionMobile(document.getElementsByClassName(mobileYouTubeSelector)[0]);
return;
} else {
// The container does not exist anymore, remove that old preview bar
previewBar.remove();
previewBar = null;
}
}
// Create the preview bar if needed (the function hasn't returned yet)
createPreviewBar();
}
/**
* Creates a preview bar on the video
*/
function createPreviewBar(): void {
if (previewBar !== null) return;
const progressElementSelectors = [
// For mobile YouTube
".progress-bar-background",
// For YouTube
".ytp-progress-bar-container",
".no-model.cue-range-markers",
// For Invidious/VideoJS
".vjs-progress-holder"
];
for (const selector of progressElementSelectors) {
const el = document.querySelectorAll(selector);
if (el && el.length && el[0]) {
console.log(selector)
previewBar = new PreviewBar(el[0], onMobileYouTube);
updatePreviewBar();
break;
}
}
}
function sponsorsLookup(id: string, channelIDPromise?) { function sponsorsLookup(id: string, channelIDPromise?) {
video = document.querySelector('video') // Youtube video player video = document.querySelector('video') // Youtube video player
@@ -499,6 +543,8 @@ function getYouTubeVideoID(url: string) {
// Check if valid hostname // Check if valid hostname
if (Config.config && Config.config.invidiousInstances.includes(urlObject.host)) { if (Config.config && Config.config.invidiousInstances.includes(urlObject.host)) {
onInvidious = true; onInvidious = true;
} else if (urlObject.host === "m.youtube.com") {
onMobileYouTube = true;
} else if (!["m.youtube.com", "www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) { } else if (!["m.youtube.com", "www.youtube.com", "www.youtube-nocookie.com"].includes(urlObject.host)) {
if (!Config.config) { if (!Config.config) {
// Call this later, in case this is an Invidious tab // Call this later, in case this is an Invidious tab
@@ -572,6 +618,15 @@ function getChannelID() {
channelWhitelisted = false; channelWhitelisted = false;
} }
/**
* This function is required on mobile YouTube and will keep getting called whenever the preview bar disapears
*/
async function updatePreviewBarPositionMobile(parent: Element) {
if (document.getElementById("previewbar") === null) {
previewBar.updatePosition(parent);
}
}
function updatePreviewBar() { function updatePreviewBar() {
let localSponsorTimes = sponsorTimes; let localSponsorTimes = sponsorTimes;
if (localSponsorTimes == null) localSponsorTimes = []; if (localSponsorTimes == null) localSponsorTimes = [];
@@ -750,16 +805,25 @@ function createButton(baseID, title, callback, imageName, isDraggable=false) {
controls.prepend(newButton); controls.prepend(newButton);
} }
function getControls() { function getControls(): HTMLElement | boolean {
let controls = document.getElementsByClassName("ytp-right-controls"); let controlsSelectors = [
// YouTube
".ytp-right-controls",
// Mobile YouTube
"#player-control-overlay",
// Invidious/videojs video element's controls element
".vjs-control-bar"
]
if (!controls || controls.length === 0) { for (const controlsSelector of controlsSelectors) {
// The invidious video element's controls element let controls = document.querySelectorAll(controlsSelector);
controls = document.getElementsByClassName("vjs-control-bar");
return (!controls || controls.length === 0) ? false : controls[controls.length - 1]; if (controls && controls.length > 0) {
} else { return <HTMLElement> controls[controls.length - 1];
return controls[controls.length - 1]; }
} }
return false;
}; };
//adds all the player controls buttons //adds all the player controls buttons
@@ -782,7 +846,7 @@ async function updateVisibilityOfPlayerControlsButton() {
await createButtons(); await createButtons();
if (Config.config.hideVideoPlayerControls || onInvidious) { if (Config.config.hideVideoPlayerControls || onInvidious || onMobileYouTube) {
document.getElementById("startSponsorButton").style.display = "none"; document.getElementById("startSponsorButton").style.display = "none";
document.getElementById("submitButton").style.display = "none"; document.getElementById("submitButton").style.display = "none";
} else { } else {
@@ -790,13 +854,13 @@ async function updateVisibilityOfPlayerControlsButton() {
} }
//don't show the info button on embeds //don't show the info button on embeds
if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious) { if (Config.config.hideInfoButtonPlayerControls || document.URL.includes("/embed/") || onInvidious || onMobileYouTube) {
document.getElementById("infoButton").style.display = "none"; document.getElementById("infoButton").style.display = "none";
} else { } else {
document.getElementById("infoButton").style.removeProperty("display"); document.getElementById("infoButton").style.removeProperty("display");
} }
if (Config.config.hideDeleteButtonPlayerControls || onInvidious) { if (Config.config.hideDeleteButtonPlayerControls || onInvidious || onMobileYouTube) {
document.getElementById("deleteButton").style.display = "none"; document.getElementById("deleteButton").style.display = "none";
} }
} }
@@ -848,7 +912,7 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
await utils.wait(isSubmitButtonLoaded); await utils.wait(isSubmitButtonLoaded);
//if it isn't visible, there is no data //if it isn't visible, there is no data
let shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none" let shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious || onMobileYouTube)) ? "unset" : "none"
document.getElementById("deleteButton").style.display = shouldHide; document.getElementById("deleteButton").style.display = shouldHide;
if (showStartSponsor) { if (showStartSponsor) {

View File

@@ -23,18 +23,28 @@ let barTypes = {
class PreviewBar { class PreviewBar {
container: HTMLUListElement; container: HTMLUListElement;
parent: any; parent: any;
onMobileYouTube: boolean;
constructor(parent) { constructor(parent, onMobileYouTube) {
this.container = document.createElement('ul'); this.container = document.createElement('ul');
this.container.id = 'previewbar'; this.container.id = 'previewbar';
this.parent = parent; this.parent = parent;
this.updatePosition(); this.onMobileYouTube = onMobileYouTube;
this.updatePosition(parent);
} }
updatePosition() { updatePosition(parent) {
//below the seek bar //below the seek bar
// this.parent.insertAdjacentElement("afterEnd", this.container); // this.parent.insertAdjacentElement("afterEnd", this.container);
this.parent = parent;
if (this.onMobileYouTube) {
parent.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
parent.style.opacity = "1";
}
//on the seek bar //on the seek bar
this.parent.insertAdjacentElement("afterBegin", this.container); this.parent.insertAdjacentElement("afterBegin", this.container);
@@ -70,7 +80,7 @@ class PreviewBar {
bar.setAttribute('data-vs-segment-type', types[i]); bar.setAttribute('data-vs-segment-type', types[i]);
bar.style.backgroundColor = barTypes[types[i]].color; bar.style.backgroundColor = barTypes[types[i]].color;
bar.style.opacity = barTypes[types[i]].opacity; if (!this.onMobileYouTube) bar.style.opacity = barTypes[types[i]].opacity;
bar.style.width = width + '%'; bar.style.width = width + '%';
bar.style.left = (timestamps[i][0] / duration * 100) + "%"; bar.style.left = (timestamps[i][0] / duration * 100) + "%";
bar.style.position = "absolute" bar.style.position = "absolute"