YouTube Thumbnails - Full Video Thumbnails for YouTube

Shows complete video thumbnails for YouTube videos. You can click a thumbnail image to jump to that point in the video.

< 腳本YouTube Thumbnails - Full Video Thumbnails for YouTube的回應

評論:正評 - 腳本一切正常

§
發表於:2024-01-29

I think it would be better to remove this big library. Here's the converted code with a focus on removing jQuery and using vanilla JavaScript (does not works):// Constants (use descriptive names)
const MATCHING_PAGE = /watch\?/;
const MAX_THUMBNAILS = 400;
const MIN_THUMBNAIL_WIDTH = 4;
const LOGGING_ENABLED = true;
const DISABLE_SPF = GM_getValue('ytDisableSPF', false);
const ADD_AND_BUTTS = GM_getValue('ytAndButts', false);
const THUMBNAIL_POSITION = true; // Before title
const TIMEOUT_DELAY = 50;
const MAX_TITLE_SEARCH_TRIES = 1;
const DIV_PADDING = 10;
const TD_PADDING = 2;

// Global variables (minimize usage)
let scriptStarted = false;
let scriptFinished = false;
let storyboardSpec = null;
let bestStoryboardSize = null;
let videoLengthSeconds = 0;
let currentVideoId = null;
let thumbnailDiv = null;
let titleDiv = null;

function log(...messages) {
LOGGING_ENABLED && console.log(...messages);
}

// Event listeners for script execution
window.addEventListener('spfdone', findStuff);
window.addEventListener('yt-navigate-finish', findStuff);
window.setTimeout(findStuff, 200);

function findStuff() {
log("Started...");
if (!MATCHING_PAGE.test(window.location.href) || (scriptStarted && !scriptFinished)) {
return;
}


scriptStarted = true;
scriptFinished = false;
storyboardSpec = null;
currentVideoId = new URL(window.location.href).searchParams.get('v');
// Remove any existing thumbnail divs
const existingThumbDivs = document.querySelectorAll('#thumbDiv');
existingThumbDivs.forEach(div => div.remove());

tryTitle();
}

// Replace jQuery selectors with vanilla JavaScript equivalents
var thumbDivYesThumbs = document.querySelector('#thumbDiv.yes-thumbs');
if (thumbDivYesThumbs) {
console.log('current_id', current_id, 'existing id', thumbDivYesThumbs.dataset.videoId);
if (thumbDivYesThumbs.dataset.videoId === current_id) {
scriptFinished = true;
return;
} else {
thumbDivYesThumbs.remove();
}
} else {
var thumbDiv = document.querySelector('#thumbDiv');
if (thumbDiv) {
thumbDiv.remove();
}
}

function getInfoFromXHR(args) {
console.log('args from xhr', args);
storyboard_spec = args.storyboard_spec;
len_seconds = parseInt(args.length_seconds);
video_id = args.video_id;
if (!storyboard_spec) {
var pr = JSON.parse(args.player_response);
storyboard_spec = pr.storyboards.playerStoryboardSpecRenderer.spec;
}
}

function tryTitle() {
console.log('tryTitle');

// Replace jQuery selectors with vanilla JavaScript equivalents
const possibleTitleSelectors = [
'div#info.ytd-watch-flexy',
'div#info.ytd-watch',
'#watch-header'
];

const titleDiv = possibleTitleSelectors.find(selector => {
return document.querySelector(selector);
});

if (!titleDiv || titleDiv.nodeName !== 'DIV') {
console.log("no title div");
if (tries++ < MAX_TRIES) {
console.log("trying again for title div");
setTimeout(tryTitle, TIMEOUT_DELAY);
} else {
console.log("done trying for title div");
scriptFinished = true;
}
} else {
console.log('found title div', titleDiv);
setUp();
}
}

function setUp() {
if (storyboard_spec) {
console.log('sb_spec', storyboard_spec);
storyboard = parseStoryboardSpec(storyboard_spec);
console.log('sb', storyboard);
best_size_idx = chooseBestStoryboardSize(storyboard);
best_size = storyboard.sizes[best_size_idx];
console.log(best_size);
if (!len_seconds > 0) {
var len_str = document.querySelector('div.ytp-progress-bar').getAttribute('aria-valuemax') || yp.config.args.length_seconds;
len_seconds = parseInt(len_str);
}
console.log("len: ", len_seconds);

// Create elements using vanilla JavaScript
const thumbDiv = document.createElement('div');
thumbDiv.id = 'thumbDiv';
thumbDiv.classList.add('style-scope', 'yt-card', 'yes-thumbs', 'thumb-inactive');
thumbDiv.dataset.videoId = video_id;

const thumbHeader = document.createElement('h1');
thumbHeader.width = '100%';
thumbHeader.classList.add('title', 'ytd-video-primary-info-renderer');
thumbHeader.textContent = 'Thumbnails';

thumbDiv.appendChild(thumbHeader);
thumbDiv.addEventListener('click', showThumbs);

// ... (rest of the code with jQuery parts replaced)
} else if (unsafeWindow.ytplayer) {
// ... similar replacements for element creation and event listeners
} else {
// ...
}

if (BEFORE_TITLE) {
titleDiv.before(thumbDiv);
} else {
titleDiv.after(thumbDiv);
}
console.log($titleDiv);

console.log("Script done");
console.log("finished.");
scriptFinished = true;
}


function showThumbs(event) {
var duration = best_size.duration / 1000;
var num_thumbs = 1;
if (duration > 0) {
num_thumbs = Math.ceil(len_seconds / duration / best_size.cols / best_size.rows);
} else {
duration = len_seconds / best_size.cols / best_size.rows;
}
console.log("Thumb header clicked. Loading " + num_thumbs + " images.");

// ... (rest of the code with jQuery parts replaced)

var badImage = false;
(function loadImage(imgNum) {
if (badImage === false && imgNum < num_thumbs && imgNum < MAX_IMAGES) {
// ... (link creation logic using vanilla string manipulation)

// Create table element and timestamps using vanilla JavaScript
var newTable = document.createElement('table');
newTable.classList.add('imgTable');
// ... (timestamp creation logic using textContent and createElement)

newTable.addEventListener('error', function() {
badImage = true;
this.remove();
console.log("Hid bad image");
});

// Use newTable.onload instead of .load()
newTable.onload = function() {
loadImage(imgNum + 1);
};

newTable.style.backgroundImage = 'url(' + link + ')';
newTable.style.width = best_size.width * best_size.cols + 'px';

thumbDiv.appendChild(newTable);

setTimeout(loadImage, 1, imgNum + 1);
}

// Use querySelectorAll and addEventListener for click events
document.querySelectorAll('.imgTable a').forEach(function(link) {
link.addEventListener('click', function(event) {
event.preventDefault();
var seekTime = parseInt(link.dataset.seekTo);
console.log('seek time', seekTime);
document.querySelector('video.html5-main-video').currentTime = seekTime;
window.scrollTo(0, 0);
event.stopImmediatePropagation();
});
});
})(0);

console.log("Done making thumb div");
thumbDiv.removeEventListener('click', showThumbs);
thumbDiv.addEventListener('click', hideThumbs);
}


function hideThumbs(event) {
if (event.target === thumbDiv || event.target.closest('#thumbDiv h1')) {
thumbDiv.querySelectorAll('table').forEach(function(table) {
table.style.display = 'none';
});
thumbDiv.removeEventListener('click', showThumbsAgain);
thumbDiv.addEventListener('click', hideThumbs);
thumbDiv.classList.remove('thumb-active');
thumbDiv.classList.add('thumb-inactive');
} else {
window.scrollTo(0, 0);
}
}

function showThumbsAgain(event) {
if (event.target === thumbDiv || event.target.closest('#thumbDiv h1')) {
thumbDiv.querySelectorAll('table').forEach(function(table) {
table.style.display = 'block';
});
thumbDiv.removeEventListener('click', hideThumbs);
thumbDiv.addEventListener('click', showThumbsAgain);
thumbDiv.classList.remove('thumb-inactive');
thumbDiv.classList.add('thumb-active');
}
}

function parseStoryboardSpec(spec) {
// EX: https:\/\/i.ytimg.com\/sb\/Pk2oW4SDDxY\/storyboard3_L$L\/$N.jpg|48#27#100#10#10#0#default#vpw4l5h3xmm2AkCT6nMZbvFIyJw|80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o|160#90#90#5#5#2000#M$M#ys1MKEnwYXA1QAcFiugAA_cZ81Q
var sections = spec.split('|');
console.log(sections);
var sb = {
sizes: [],
baseUrl: sections.shift()
};

// EX: 80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o
// EX: 160# 90# 90# 5# 5# 2000# M$M# ys1MKEnwYXA1QAcFiugAA_cZ81Q
sections.forEach(function(value, idx) {
var data = value.split('#');
console.log(data);
var new_size = {
width : +data[0],
height : +data[1],
qual : +data[2], // image quality???
cols : +data[3],
rows : +data[4],
duration : +data[5], // duration of each snapshot in milliseconds
img_name : data[6],
sigh : data[7]
};
sb.sizes[idx] = new_size;
});
console.log(sb);
return sb;
}

function chooseBestStoryboardSize(sb) {
var sizes = sb.sizes;
var widest = 0;
var widest_idx = -1;
for (var i = 0; i < sizes.length; i++) {
if (widest < sizes[i].width || (widest == sizes[i].width && sizes[widest_idx].cols < sizes[i].cols)) {
if (sizes[i].cols >= MIN_WIDTH) {
widest = sizes[i].width;
widest_idx = i;
}
}
}
return widest_idx;
}




function killSPF() {
if (DISABLE_SPF) {
console.log('trying to kill SPF');
document.querySelectorAll('a.spf-link, a.yt-simple-endpoint').forEach(function(link) {
link.classList.remove('spf-link');
link.classList.remove('yt-simple-endpoint');
link.classList.add('old-endpoint');
// No need for `off()` in vanilla JavaScript, event listeners are specific to each function
});
}
//AND_BUTTS && document.querySelector('#info-contents h1.title').textContent += " and Butts";
}

function getParameters(str) {
var r = {};
str.split('&').forEach(str => {
var split = str.split('=');
r[split[0]] = decodeURIComponent(split[1]);
});
return r;
}


// Use addEventListener for event handling
window.addEventListener('spfdone', findStuff);
window.addEventListener('yt-navigate-finish', findStuff);
window.setTimeout(findStuff, 200);

發表回覆

登入以回復