YouTube Thumbnails -- Full Video Thumbnails for YouTube

Shows complete video thumbnails for YouTube videos

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

// ==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();
//	});
//})();