YouTube Thumbnails -- Full Video Thumbnails for YouTube

Shows complete video thumbnails for YouTube videos

目前為 2015-07-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        YouTube Thumbnails -- Full Video Thumbnails for YouTube
// @namespace   driver8.net
// @description Shows complete video thumbnails for YouTube videos
// @match		*://*.youtube.com/watch?*
// @version     0.3
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @grant       GM_addStyle
// @grant		unsafeWindow
// @grant		GM_registerMenuCommand
// @grant		GM_getValue
// @grant		GM_setValue
// ==/UserScript==

var AND_BUTTS = false;
var LOAD_DELAY_START = 200;
var LOAD_DELAY_FACTOR = 50;
var LOAD_DELAY_MAX = 500;
var TIMEOUT_DELAY = 2000;
var DIV_PADDING = 10;
var TD_PADDING = 2;
var DISABLE_SPF = GM_getValue('ytDisableSPF', false);
var LOGGING = false;
var MIN_WIDTH = 4;
var MAX_IMAGES = 30;

var $thumbDiv, $thumbHeader, storyboard_spec, storyboard, best_size_idx, best_size, len_seconds, videoId, tries;

function log(msg) {
	LOGGING && console.log(msg);
}
log("Hi");

setUp();
function setUp() {

	AND_BUTTS && $('#eow-title').text($('#eow-title').text() + " and Butts");

	// See if waiting will help w/spf bullshit (it won't)
	tries = 0;
	(function trySb() {
		try {
			storyboard_spec = unsafeWindow.ytplayer.config.args.storyboard_spec;
		} catch (e) {
			log("oops " + e);
			try {
				storyboard_spec = window.ytplayer.config.args.storyboard_spec;
			} catch (e2) {
				log("oops2 " + e2);
				if (tries++ < 2) {
					window.setTimeout(trySb, TIMEOUT_DELAY);
				}
			}
		}
	})();

	storyboard = parseStoryboardSpec(storyboard_spec);
	best_size_idx = chooseBestStoryboardSize(storyboard);
	best_size = storyboard.sizes[best_size_idx];
	log(best_size);
	len_seconds = parseInt(unsafeWindow.ytplayer.config.args.length_seconds);
	log("len: " + len_seconds);

	var $titleDiv = $('#watch-header');
	$thumbDiv = $('<div id="thumbDiv" class="yt-card"></div>');
	$thumbDiv.css('padding', DIV_PADDING +'px');
	$thumbHeader = $('<h1 width="100%">Thumbs</h1>');
	$thumbHeader.css({'cursor': 'pointer'});
	$thumbDiv.append($thumbHeader);
	$titleDiv.after($thumbDiv);

	$thumbHeader.click(showThumbs);

	log("Script done");

	styleIt();
}

function showThumbs() {
	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;
	}
	log("Thumb header clicked. Loading " + num_thumbs + " images.");
	var total_width = best_size.width * best_size.cols + DIV_PADDING * 2;
	var parent_diff = $thumbDiv.parent().innerWidth() - total_width;
	parent_diff < 0 && $thumbDiv.css({'left': + parent_diff + 'px'});
	$thumbDiv.css({'position': 'relative', 'width': total_width + 'px'});

//	Grab the youtube sessionlink ID for time links
	var sessionlink = $('#logo-container').data("sessionlink");

	var badImage = false;
	(function loadImage(imgNum) {
		if (badImage === false && imgNum < num_thumbs && imgNum < MAX_IMAGES) {
			// EX: https:\/\/i.ytimg.com\/sb\/2XY3AvVgDns\/storyboard3_L$L\/$N.jpg
			// EX: https://i.ytimg.com/sb/k4YRWT_Aldo/storyboard3_L2/M0.jpg?sigh=RVdv4fMsE-eDcsCUzIy-iCQNteI
			var link = storyboard.baseUrl.replace('\\', '');
			link = link.replace('$L', best_size_idx);
			link = link.replace('$N', best_size.img_name);
			link = link.replace('$M', imgNum);
			link += '?sigh=' + best_size.sigh;

			log(link);

			// Create a table for the timestamps over the image
			var $newTable = $('<table>');
			$newTable.addClass('imgTable');
			var x = imgNum * duration * best_size.rows * best_size.cols; // the starting time for this table
			var innerStr = '';
			var doclocation = document.location.href.replace(/\#.*/, '');
			for (var i = 0; i < best_size.rows; i++) {
				if (x <= len_seconds + 1) { // if we haven't reached the end of the video
					innerStr += '<tr>';
					for (var j = 0; j < best_size.cols; j++) {
						innerStr += '<td><a class="yt-uix-sessionlink" href="' + doclocation + '#t=' + x + '">';
						if (x <= len_seconds + 1) {
							var hrs = Math.floor(x / 3600);
							var mins = Math.floor((x % 3600) / 60);
							var secs = Math.round(x % 60);
							innerStr += hrs > 0 ? hrs + ':' : ''; // hrs
							innerStr += ( hrs > 0 && mins < 10 ? "0" + mins : mins ) + ':'; // mins
							innerStr += secs < 10 ? "0" + secs : secs; // secs
						}
						innerStr += '</a></td>';
						x += duration;
					}
					innerStr += '<tr>';
				}
			}
			$newTable.html(innerStr);
			$newTable.error(function() {
				badImage = true;
				$(this).remove();
				log("Hid bad image");
			});
			//$newTable.load(function() {
			//	loadImage(imgNum + 1);
			//});
			$newTable.css({'background-image': 'url(' + link + ')', 'width': best_size.width * best_size.cols});

			$thumbDiv.append($newTable);

			setTimeout(loadImage, Math.min(LOAD_DELAY_START + imgNum * LOAD_DELAY_FACTOR, LOAD_DELAY_MAX), imgNum + 1);
		}
	})(0);

	log("Done making thumb div");
	$thumbHeader.off('click');
	$thumbHeader.click(hideThumbs);
}

function hideThumbs() {
	$thumbDiv.children('table').hide();
	$thumbHeader.off('click');
	$thumbHeader.click(showThumbsAgain);
}

function showThumbsAgain() {
	$thumbDiv.children('table').show();
	$thumbHeader.off('click');
	$thumbHeader.click(hideThumbs);
}

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('|');
	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('#');
		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;
	});
	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 styleIt() {
	var userStyles0 =
		"table.imgTable { border-collapse: collapse; background-repeat: no-repeat; !important } " +
		".imgTable td { width: " + (best_size.width - 2 * TD_PADDING) + "px;" +
		" height: " + (best_size.height - 2 * TD_PADDING) + "px;" +
		" padding: " + TD_PADDING + "px;" +
		" border-width: 0px;" +
		" vertical-align: top;" +
		"	color: white;" +
		"	    text-shadow:" +
		"			-1px -1px 0 #000, " +
		"			1px -1px 0 #000, " +
		"			-1px 1px 0 #000, " +
		"			1px 1px 0 #000;  " +
		" 	!important}" +
		".imgTable a { text-decoration: none; color: white; padding-right: 100%; padding-bottom: 100%; !important }";
	GM_addStyle(userStyles0);

	if (DISABLE_SPF) {
		$('a.spf-link').each(function () {
			var $link = $(this);
			$link.removeClass('spf-link');
			$link.off();
		});
	}

	GM_registerMenuCommand( "Toggle SPF", function toggleSpf() {
		DISABLE_SPF = !DISABLE_SPF;
		GM_setValue('ytDisableSPF', DISABLE_SPF);
	}, 's' );
	log("DONE");
}


/// TRYING TO DEAL WITH YOUTUBE'S STUPID SPF SHIT WITHOUT DISABLING IT. BLAHH! ///

//window.addEventListener('spfrequest', function() { log("SPF WUT? spfrequest"); });
//window.addEventListener('spfpartprocess', function() { log("SPF WUT? spfpartprocess"); });
//window.addEventListener('spfpartdone', function() { log("SPF WUT? spfpartdone"); });
//window.addEventListener('spfprocess', function() { log("SPF WUT? spfprocess"); });
//window.addEventListener('spfdone', function() { log("SPF WUT? spfdone"); });
//window.addEventListener('spferror', function() { log("SPF WUT? spferror"); });
//window.addEventListener('spfreload', function() { log("SPF WUT? spfreload"); });
//window.addEventListener('spfjsbeforeunload', function() { log("SPF WUT? spfjsbeforeunload"); });

//window.addEventListener('spfdone', spfWut);

//function spfWut(a) {
//	log(a);
//	var parts = a.detail.response.parts;
//	var good_part = null;
//	parts.forEach(function(val, idx) {
//		good_part = good_part || (val.data && val.data.swfcfg);
//	});
//	log(good_part);
//	// var storyboard_spec = a.data.swfcfg.args.storyboard_spec;
//	storyboard_spec = good_part.args.storyboard_spec;
//	log("sbSpec: " + sb_spec);
//	best_size_idx_idx = chooseBestStoryboardSize(storyboard);
//	best_size = storyboard.sizes[best_size_idx_idx];
//	log(best_size);
//	len_seconds = parseInt(good_part.args.length_seconds);
//	log("len: " + len_seconds);
//	setUp();
//	log("BOOM");
//}
//
//(function transitionCheck() {
//	$('body').on('transitionend', function(event) {
//		log("transitioned prog");
//		if (event.target.id != 'progress') return false;
//		log("transitioned event");
//		setUp();
//		log("BOOM");
//		transitionCheck();
//	});
//})();