Youtube Timestamp Buttons from FireBase Helper

Uses FireBase Helper Function to Call Youtube Data API 3 to get Comments with TimeStamps

目前為 2019-07-10 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Youtube Timestamp Buttons from FireBase Helper
// @namespace    http://greasyfork.org
// @version      0.1
// @description  Uses FireBase Helper Function to Call Youtube Data API 3 to get Comments with TimeStamps
// @author       636597
// @include      *://*youtube.com/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

var button_menu_element = false;
var load_button_element = false;
var next_button_element = false;
var previous_button_element = false;

var time_stamp_data_paragraph_parent_element = false;
var time_stamp_data_paragraph_element = false;
var time_stamp_button_elements = [];

var current_video_id = false;
var current_playlist_circular_array = false;
var x1_player = undefined;

function CustomLog( message ) {
	console.log( "YoutubeTimestampButtons.js === " + message );
}

function CircularArrayIterator( list , starting_index ) {
	if ( !list ) { return false; }
	if ( list.length < 1 ) { return false; }
	const llen = list.length;
	return {
		current_index: starting_index || 0 ,
		list: list ,
		list_length: llen ,
		push: function( item , index ) {
			if ( !item ) { return; }
			if ( index ) {
				this.list.splice( index , 0 , item );
			}
			else {
				this.list.push( item );
			}
			this.list_length += 1;
		} ,
		pop: function( index ) {
			if ( index ) {
				this.list.splice( index , 1 );
			}
			else {
				this.list.pop();
			}
			this.list_length -= 1;
		} ,
		current: function() {
			return this.list[ this.current_index ];
		} ,
		next: function() {
			CustomLog( "next" );
			this.current_index += 1;
			if ( this.current_index > ( this.list_length - 1 ) ) {
				this.current_index = 0;
			}
			return this.list[ this.current_index ];
		} ,
		next_index: function() {
			CustomLog( "next index" );
			this.current_index += 1;
			if ( this.current_index > ( this.list_length - 1 ) ) {
				this.current_index = 0;
			}
			return this.current_index;
		} ,
		previous: function() {
			CustomLog( "previous" );
			this.current_index -= 1;
			if ( this.current_index < 0 ) {
				this.current_index = ( this.list_length - 1 );
			}
			return this.list[ this.current_index ];
		} ,
		previous_index: function() {
			CustomLog( "previous index" );
			this.current_index -= 1;
			if ( this.current_index < 0 ) {
				this.current_index = ( this.list_length - 1 );
			}
			return this.current_index;
		}
	};
}

function removeDuplicates(originalArray, prop) {
	var newArray = [];
	var lookupObject  = {};

	for( var i in originalArray ) {
		lookupObject[originalArray[i][prop]] = originalArray[i];
	}

	for(i in lookupObject) {
		newArray.push(lookupObject[i]);
	}
	return newArray;
}

// function playlist_next() {
// 	CustomLog( "NEXT()" );
// 	if ( !x1_player ) { return; }
// 	var current_time = x1_player.getCurrentTime();
// 	if ( !current_time ) { return; }
// 	current_time = parseInt( current_time );
// 	CustomLog( "CURRENT TIME === " + current_time.toString() );
// 	CustomLog( "CURRENT === [ " + ( current_playlist_circular_array.current_index + 1 ).toString() + " ] of " + current_playlist_circular_array.list_length.toString() + " === " + current_playlist_circular_array.list[ current_playlist_circular_array.current_index ].url_seconds.toString() );
// 	console.log( current_playlist_circular_array.list[ current_playlist_circular_array.current_index ] );

// 	var current_url_seconds = current_playlist_circular_array.list[ current_playlist_circular_array.current_index ].url_seconds;
// 	var current_item = current_playlist_circular_array.list[ current_playlist_circular_array.current_index ];
// 	while ( current_url_seconds <= current_time ) {
// 		CustomLog( "current_url_seconds <= current_time" );
// 		CustomLog( current_url_seconds.toString() + " <= " + current_time.toString() );
// 		CustomLog( "current_index === " + current_playlist_circular_array.current_index.toString() );
// 		CustomLog( "Current Item === " );
// 		console.log( current_item );
// 		CustomLog( "NEXT()" );
// 		current_playlist_circular_array.next_index();
// 		current_item = current_playlist_circular_array.list[ current_playlist_circular_array.current_index ];
// 		current_url_seconds = current_item.url_seconds;
// 		CustomLog( current_url_seconds.toString() + " <= " + current_time.toString() );
// 		CustomLog( "current_index === " + current_playlist_circular_array.current_index.toString() );
// 		CustomLog( "Current Item === " );
// 		console.log( current_item );
// 		if ( current_playlist_circular_array.current_index === 0 ) { break; }
// 	}
// 	//next = current_playlist_circular_array.next();
// 	CustomLog( "NEXT()-FINAL === [ " + ( current_playlist_circular_array.current_index + 1 ).toString() + " ] of " + current_playlist_circular_array.list_length.toString() );
// 	console.log( current_item );

// 	x1_player.loadVideoById( current_video_id , current_item.url_seconds );
// }

// function playlist_previous() {
// 	CustomLog( "PREVIOUS()" );
// 	if ( !x1_player ) { return; }
// 	var current_time = x1_player.getCurrentTime();
// 	if ( !current_time ) { return; }
// 	current_time = parseInt( current_time );
// 	CustomLog( "CURRENT TIME === " + current_time.toString() );
// 	CustomLog( "CURRENT === [ " + ( current_playlist_circular_array.current_index + 1 ).toString() + " ] of " + current_playlist_circular_array.list_length.toString() );
// 	while ( current_time > current_playlist_circular_array.list[ current_playlist_circular_array.current_index ].url_seconds ) {
// 		CustomLog( "current_time > current_index" );
// 		current_playlist_circular_array.previous_index();
// 		CustomLog( "CURRENT === [ " + ( current_playlist_circular_array.current_index + 1 ).toString() + " ] of " + current_playlist_circular_array.list_length.toString() );
// 		if ( current_playlist_circular_array.current_index === current_playlist_circular_array.list_length ) { break; }
// 	}
// 	//next = current_playlist_circular_array.next();
// 	CustomLog( "CURRENT === [ " + ( current_playlist_circular_array.current_index + 1 ).toString() + " ] of " + current_playlist_circular_array.list_length.toString() );

// 	x1_player.loadVideoById( current_video_id , current_playlist_circular_array.list[ current_playlist_circular_array.current_index ].url_seconds );
// }

function playlist_seek( seconds ) {
	CustomLog( "SEEKING TO === " + seconds.toString() );
	x1_player.loadVideoById( current_video_id , seconds );
}

function add_paragraph_with_time_stamp_data() {
	time_stamp_data_paragraph_parent_element = document.querySelector( "ytd-comments#comments" );
	if ( time_stamp_data_paragraph_parent_element ) {
		time_stamp_data_paragraph_parent_element = time_stamp_data_paragraph_parent_element.parentElement;
		var reversed = current_playlist_circular_array.list;
		reversed.reverse();
		for ( var i = 0; i < reversed.length; ++i ) {
			var id = "x1_p_ts_entry_" + i.toString();
			var p_string = '<button id="' + id + '">' + reversed[ i ].time_string + '</button>';
			//CustomLog( p_string );
			var template = document.createElement( 'template' );
			template.innerHTML = p_string;
			var fragment = template.content
			//time_stamp_data_paragraph_parent_element.appendChild( fragment );
			time_stamp_data_paragraph_parent_element.insertBefore( fragment ,  time_stamp_data_paragraph_parent_element.childNodes[ 3 ] );
			var b_elem = document.body.querySelector( "#" + id );
			b_elem.setAttribute( "url_seconds" , current_playlist_circular_array.list[ i ].url_seconds );
			//b_elem.setAttribute( "playlist_index" , reversed[ i ].index );
			b_elem.addEventListener( "click" , function( event ) {
				//current_playlist_circular_array.current_index = parseInt( this.getAttribute( "playlist_index" ) );
				playlist_seek( parseInt( this.getAttribute( "url_seconds" ) ) );
			});
			time_stamp_button_elements.push( b_elem );
		}
	}
	load_button_element.style.visibility = "visible";
}

function add_previous_next_buttons() {
	if ( button_menu_element ) {
		button_menu_element.insertAdjacentHTML( 'beforeend' , '<button id="x1_playlist_previous">Previous</button>' );
		previous_button_element = document.body.querySelector( "#x1_playlist_previous" );
		previous_button_element.addEventListener( "click" , playlist_previous );
		button_menu_element.insertAdjacentHTML( 'beforeend' , '<button id="x1_playlist_next">Next</button>' );
		next_button_element = document.body.querySelector( "#x1_playlist_next" );
		next_button_element.addEventListener( "click" , playlist_next );
	}
}

function load_keyboard_watchers() {
	add_previous_next_buttons();
	add_paragraph_with_time_stamp_data();
	// TODO: Need To figure out Javscript Circular List, COGGERS FailFish
	/*
	document.body.addEventListener( "keydown" , function( event ) {
		if ( event.key === "n" ) {
			playlist_next();
		}
		else if ( event.key === "b" ) {
			playlist_previous();
		}
	});
	*/
}

function get_video_stats() {
  var url_string = x1_player.getVideoUrl();
  current_video_id = url_string.split( "v=" )[ 1 ].split( "&" )[ 0 ];
  /*
  current_playlist_id = url_string.split( "list=" );
  if ( current_playlist_id ) {
	if ( current_playlist_id.length > 1 ) {
		current_playlist_id = current_playlist_id[ 1 ].split( "&" )[ 0 ];
		current_playlist_index = x1_player.getPlaylistIndex();
		CustomLog( current_playlist_id );
		CustomLog( current_playlist_index );
	}
  }
  */
  CustomLog( current_video_id );
}

function parse_firebase_helper( comments ) {
	CustomLog( "Parsing Recieved Comment Data" );
	let final = [];
	for ( let i = 0; i < comments.length; ++i ) {
		final.push( ...comments[ i ].time_stamp_parts );
	}
	let uniqueArray = removeDuplicates( final , "url_seconds" );
	uniqueArray = uniqueArray.sort( ( a , b ) => {
		b.url_seconds - a.url_seconds
	});
	current_playlist_circular_array = CircularArrayIterator( uniqueArray );
	add_paragraph_with_time_stamp_data();
	console.log( uniqueArray );
}

var fburlb = atob( 'aHR0cHM6Ly91cy1jZW50cmFsMS1oZWxwZXItZTNkODYuY2xvdWRmdW5jdGlvbnMubmV0L3RpbWVzdGFtcHM/aWQ9' );
function load_comments_from_firebase_helper() {
	reset_simple();
	x1_player = document.getElementById( "movie_player" );
	if ( !x1_player ) { return; }
	get_video_stats();
	if ( !current_video_id ) { return; }
	load_button_element.style.visibility = "hidden";
	CustomLog( "Fetching Comments from FireBase Helper" );
	// fetch( 'https://us-central1-helper-e3d86.cloudfunctions.net/timestamps?id=' + current_video_id , {
	// 	headers: new Headers({
	// 		'Access-Control-Allow-Origin': '*' ,
	// 		'Access-Control-Allow-Headers':'application/json',
	// 		'Access-Control-Allow-Headers': 'Content-Type, Authorization'
	// 	})
	// })
	fetch( fburlb + current_video_id )
	.then( response => response.json() )
	.then( data => {
		console.log( data );
		parse_firebase_helper( data.top_time_stamp_comments );
	})
	.catch(error => console.error(error))
}

function add_load_button() {
	button_menu_element = document.body.querySelector( "ytd-menu-renderer" );
	if ( button_menu_element ) {
		button_menu_element.insertAdjacentHTML( 'beforeend' , '<button id="x1_load_ts">Load Timestamps</button>' );
		load_button_element = document.body.querySelector( "#x1_load_ts" );
		load_button_element.addEventListener( "click" , load_comments_from_firebase_helper );
	}
}

function reset_simple() {
	try { next_button_element.parentNode.removeChild( next_button_element ); } catch( e ){}
	next_button_element = false;
	try { previous_button_element.parentNode.removeChild( previous_button_element ); } catch( e ){}
	previous_button_element = false;
	for ( var i = 0; i < time_stamp_button_elements.length; ++i ) {
		try { time_stamp_button_elements[ i ].parentNode.removeChild( time_stamp_button_elements[ i ] ); } catch( e ){}
	}
	//time_stamp_data_paragraph_parent_element = false;
	//time_stamp_data_paragraph_element = false;
	time_stamp_button_elements = [];
	current_video_id = false;
	// current_playlist_id = false;
	// current_playlist_index = 0;
	// current_index = 0;
	// current_playlist = [];
	current_playlist_circular_array = undefined;
	x1_player = undefined;
}

function reset_all() {
	button_menu_element = false;
	try { load_button_element.parentNode.removeChild( load_button_element ); } catch( e ){}
	load_button_element = false;
	try { next_button_element.parentNode.removeChild( next_button_element ); } catch( e ){}
	next_button_element = false;
	try { previous_button_element.parentNode.removeChild( previous_button_element ); } catch( e ){}
	previous_button_element = false;
	try { time_stamp_data_paragraph_parent_element.parentNode.removeChild( time_stamp_data_paragraph_parent_element ); } catch( e ){}
	time_stamp_data_paragraph_parent_element = false;
	time_stamp_data_paragraph_element = false;
	time_stamp_button_elements = [];
	current_video_id = false;
	//current_playlist_id = false;
	//current_playlist_index = 0;
	//current_index = 0;
	//current_playlist = [];
	current_playlist_circular_array = undefined;
	x1_player = undefined;
}

function init() {
	reset_all();
	CustomLog( "LOADING" );
	setTimeout(function(){
		add_load_button();
		//CustomLog( "LOADED" );
	} , 3000 );
}

(function() {
	window.addEventListener ( "load", init );
})();