Youtube Timestamps

Allows to create, store, access, export and import video timestamps.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name        Youtube Timestamps
// @name:fr     Horodatage youtube
// @namespace   YTime
// @include     *youtube.com* 
// @version     1.1.0
// @author      lapincroyable
// @description Allows to create, store, access, export and import video timestamps.
// @description:fr  Permet de créer, stocker, accéder, exporter et importer des horodatages (timestamps) vidéos .
// @run-at document-idle 
// @grant GM_getValue 
// @grant GM_setValue 
// ==/UserScript==
// -----------------------
var YTimeList = [];
var YTimeValue = GM_getValue("YTIMELIST");
if (YTimeValue != undefined){	YTimeList = JSON.parse(YTimeValue); }
var IDVid ;
var Player ;
var btnStyle = {
  borderRadius : "16px",
  border : "2px solid black",
  margin : "5px",
  padding : "0px",
  heigth : "20px",
  width : "20px"
};
var RemAllStyle = {
  backgroundColor : "lightsalmon"
};
var AddStyle = {
  backgroundColor : "lightgreen"
};
var RemStyle = {
  backgroundColor : "black",
  color : "white",
  "margin-left" : "-26px"
};
var TSStyle = {
  "padding" : "0px 30px 0px 10px",
  backgroundColor :"#ffffff80",
  width : "auto"
}
var OptStyle = {
  backgroundColor :"#76b5c5",
  float : "left"
}
// -----------------------
function ConvSec(timestamp){
  let split = timestamp.split(":");
  let result = 0;
  if(split.length > 2){
    result = parseInt(split[0])*3600 + parseInt(split[1])*60 + parseInt(split[2]);
  } else {
    result = parseInt(split[0])*60 + parseInt(split[1]);
  }
  return result;
}
function GetTimeStamp(){
  let ptime = Math.floor(Player.currentTime);
  let timestamp = "";
  let hr = false;
  if (Math.floor(ptime/3600) > 0) {
    timestamp+=Math.floor(ptime/3600)+":";
    ptime = ptime%3600;
    hr = true;
  }
  if ((Math.floor(ptime/60) < 10) & hr){
    timestamp+="0";
  }
  timestamp+=Math.floor(ptime/60)+":";
  ptime = ptime%60;
  if (Math.floor(ptime%60) < 10){
    timestamp+="0";
  }
  timestamp+=ptime%60;
  return(timestamp);
}
function ToggleOptn(){
  let div = document.getElementById("optionsbar");
  if (div.style.display === "none") {
    div.style.display = "block";
  } else {
    div.style.display = "none";
  }
}
function ExportTS(){
  let indexvid = YTimeList.findIndex(i => i[0]===IDVid);
  if(indexvid != -1){
    let TSString = YTimeList[indexvid].toString();
    let FileName = "Timestamps - " + document.title+".txt";
    let TSFile = new File([TSString], FileName, {type:TSString.type});
    let FileLink = document.createElement("a");
    let FileURL = URL.createObjectURL(TSFile);
    FileLink.href = FileURL;
    FileLink.download = FileName;
    document.body.appendChild(FileLink);
    FileLink.click();
    setTimeout(function() {
      document.body.removeChild(FileLink);
      window.URL.revokeObjectURL(FileURL);  
    }, 0); 
  }
}
function AskFile(){
  let FileInput = document.getElementById('fileinput');
  FileInput.click();  
}
function ImportTS(){
  let TSFile = document.getElementById('fileinput').files[0];
  let reader = new FileReader();
  reader.readAsText(TSFile);
  reader.onload = function() {
    let array = reader.result.split(',');
    if (array[0] == IDVid){
      array.shift();
      array.sort();
      array.forEach(element => AddTS(element));
    }
  };
  reader.onerror = function() {
    console.log(reader.error);
  };
}
function LoadTimes(){
  CleanBar();
  let indexvid = YTimeList.findIndex(i => i[0]===IDVid);
  if(indexvid != -1){
    YTimeList[indexvid].shift();
    YTimeList[indexvid].sort();
    for (let indexstamp in YTimeList[indexvid]){
      AddTSButton(YTimeList[indexvid][indexstamp]);  
    }
    YTimeList[indexvid].unshift(IDVid);
  }
}
function AddTS(value){
  let timestamp;
  console.log("value:"+value);
  if (value === null){
    timestamp = GetTimeStamp();
  } else {
    timestamp = value;
  }
  let indexvid = YTimeList.findIndex(i => i[0]===IDVid);
  if(indexvid == -1){
    YTimeList.push([IDVid,timestamp]);
    GM_setValue("YTIMELIST",JSON.stringify(YTimeList))
    AddTSButton(timestamp);
  }else{
    let indexstamp = YTimeList[indexvid].findIndex(i => i===timestamp);
      if(indexstamp == -1){
        YTimeList[indexvid].push(timestamp);
        GM_setValue("YTIMELIST",JSON.stringify(YTimeList))
        AddTSButton(timestamp);
      }
  }
}
function AddTSButton(timestamp){
  let TSBar = document.getElementById("tsbar");
  let Sec = ConvSec(timestamp);   
  let TSButton= document.createElement("button");
  Object.assign(TSButton , {innerHTML : timestamp , id : "TSButton" , onclick : function(){
    document.getElementsByClassName("html5-main-video")[0].currentTime = Sec;
    document.getElementsByClassName("html5-main-video")[0].play();
  }});
  Object.assign(TSButton.style,btnStyle,TSStyle);
  let RemTSButton = document.createElement("button");
  Object.assign (RemTSButton, {innerHTML : "x" , id : "TSButton", onclick : function(){RemTS(timestamp)}});
  Object.assign(RemTSButton.style,btnStyle,RemStyle);
  TSBar.appendChild(TSButton);
  TSBar.appendChild(RemTSButton);
}
function RemTS(timestamp){
  let indexvid = YTimeList.findIndex(i => i[0]===IDVid);
  let indexstamp = YTimeList[indexvid].findIndex(i => i===timestamp);
  YTimeList[indexvid].splice(indexstamp,1);
  if (YTimeList[indexvid].length == 1){
    YTimeList.splice(indexvid,1);
  }
  GM_setValue("YTIMELIST",JSON.stringify(YTimeList));
  LoadTimes();
}
function RemAllTS(){
  let indexvid = YTimeList.findIndex(i => i[0]===IDVid);
  if(indexvid != -1){
    YTimeList.splice(indexvid,1);
    GM_setValue("YTIMELIST",JSON.stringify(YTimeList));
    CleanBar();
  }
}
function CleanBar(){
  let TSBar = document.getElementById("tsbar");
	let TSButtonList = TSBar.querySelectorAll("#TSButton");
	for (let TSButton of TSButtonList) {
		TSBar.removeChild(TSButton);
	}
}
function check(changes, observer) {
    if(Player.baseURI.match("watch")){
      if(IDVid != (Player.baseURI.split("v=")[1].split("&")[0])){
        IDVid = Player.baseURI.split("v=")[1].split("&")[0];
        LoadTimes();
      }
    } 
}
function loadonwatch(changes, observer) {
  if(document.baseURI.match("watch") && document.getElementById("info-contents") && document.getElementsByClassName("html5-main-video")){
    observer.disconnect();
    Player = document.getElementsByClassName("html5-main-video")[0];
    IDVid = Player.baseURI.split("v=")[1].split("&")[0];
    let ZoneInfo = document.getElementById("info-contents");
    let OptionsButton = document.createElement("button");
    Object.assign (OptionsButton, {innerHTML : "&#10033;" , onclick : ToggleOptn});
    Object.assign(OptionsButton.style,btnStyle,OptStyle);
    let OptionsBar = document.createElement("div");
    //OptionsBar.setAttribute();
    Object.assign(OptionsBar, {"id":"optionsbar"})
    Object.assign(OptionsBar.style,{display : "none"});
    let ExportButton = document.createElement("button");
    Object.assign(ExportButton, {innerHTML : "&mapstoup;", onclick : ExportTS});
    Object.assign(ExportButton.style,btnStyle,OptStyle);
    let ImportButton = document.createElement("button");
    Object.assign(ImportButton,{innerHTML : "&DownArrowBar;", onclick : AskFile});
    Object.assign(ImportButton.style,btnStyle,OptStyle);
    let FileInput = document.createElement("input");
    Object.assign(FileInput,{id : "fileinput", type :"file", accept :".txt", hidden : "true", oninput : ImportTS});
    let TSBar = document.createElement("div");
    Object.assign(TSBar, {"id":"tsbar"});
    let RemAllButton = document.createElement("button");
    Object.assign(RemAllButton, {innerHTML : "-" , onclick : RemAllTS});
    Object.assign(RemAllButton.style,btnStyle,RemAllStyle);
    let AddButton = document.createElement("button");
    Object.assign (AddButton, {innerHTML : "+" , onclick : function(){AddTS(null)}});
    Object.assign(AddButton.style,btnStyle,AddStyle);
    ZoneInfo.parentNode.insertBefore(OptionsButton,ZoneInfo);
    ZoneInfo.parentNode.insertBefore(OptionsBar,ZoneInfo);
    OptionsBar.appendChild(ExportButton);
    OptionsBar.appendChild(ImportButton);
    OptionsBar.appendChild(FileInput);
    ZoneInfo.parentNode.insertBefore(TSBar,ZoneInfo);
    TSBar.appendChild(RemAllButton);
    TSBar.appendChild(AddButton);
    LoadTimes();
    (new MutationObserver(check)).observe(Player, {attributes: true, subtree: true});
  }
}
(new MutationObserver(loadonwatch)).observe(document, {attributes: true, subtree: true});