2

This question is about scripting the main YouTube site, client side from JavaScript.

While it looks easy as a click, I found no way to change the current video by a new non in context video ID without reloading.

This seems to be related with the polymer library in use, with a lot of shadow dom and some special behavior.

Here is the context: I am making for my own use a bookmarklet that load videos from the reddit json api, which supports CORS calls.

So far so good, I can load many videos, image previews and links into the YouTube sidebar, with this simple enough handcrafted script.

Bookmarklet to call the script:

javascript:void%20function(){target=document.getElementsByTagName(%22script%22)[0],inj=document.createElement(%22script%22),inj.src=%22https://webdev23.github.io/reddube/reddube.js%22,target.appendChild(inj)}();

This bookmarklet is loading into the DOM a js file, that is parsing the json api to get what I need.

Here is the content of the called file:

var reds = ["/r/videos","/r/unknownvideos","/r/DeepIntoYouTube","/r/newsreels","/r/fullmoviesonyoutube","/r/SF_Videos","/r/classicfilms","/r/Documentaries","/r/artdocumentaries","/r/ShowsonYT","/r/YTPL","/r/NotTimAndEric","/r/youtubehaiku","/r/PlayItAgainSam","/r/ObscureMedia","/r/360video","/r/AccidentalComedy","/r/amibeingdetained","/r/ArtisanVideos","/r/AwfulCommercials","/r/bestofworldstar","/r/cringe","/r/CommercialCuts","/r/contagiouslaughter","/r/cookingvideos","/r/curiousvideos","/r/deepintoyoutube","/r/documentaries","/r/educativevideos","/r/FastWorkers","/r/fightporn","/r/FuckingWithNature","/r/fullmoviesonyoutube","/r/happycrowds/","/r/idiotsfightingthings","/r/lectures","/r/mealtimevideos","/r/motivationvideos","/r/ObscureMedia","/r/playitagainsam","/r/Prematurecelebration","/r/PublicFreakout","/r/Roadcam","/r/streetfights","/r/sweetjustice","/r/TheWayWeWereOnVideo","/r/trailers","/r/UnexpectedThugLife","/r/videoporn","/r/vids","/r/vines","/r/virtualfreakout","/r/woahtube","/r/listentothis/","/r/Tekno/","/r/reggae/","/r/RootsReggae","r/ska","/r/dub","/r/hip_hop","/r/treemusic/","/r/stonerrock/","/r/frenchrap/","/r/trance/","/r/minimal/"]  
var rview = ["","/new/","/rising/","/controversial/","/top/"]  
related.innerHTML = "<div style='filter: sepia(38%) invert(100%) saturate(100%) brightness(1) grayscale(0%) hue-rotate(360deg) contrast(100%)'><span id='subR' data-ccc='25' style='color:white;background:#141e1b;font-size:1.44em;width:20px'></span><input type='range' value='0' max='64' id ='redR' style='float:right;width:230px' onchange='redList.innerHTML=\"\";redd(this.value)'><br><span onclick='redList.innerHTML=\"\";' style='float:right;margin:3px 0 0 0'><button id='rflt' data-filter='0' onclick='this.dataset.filter=0;redd(redR.value)'>hot</button><button onclick='rflt.dataset.filter=1;redd(redR.value)'>new</button><button onclick='rflt.dataset.filter=2;redd(redR.value)'>rising</button><button onclick='rflt.dataset.filter=3;redd(redR.value)'>controversial</button><button onclick='rflt.dataset.filter=4;redd(redR.value)'>top</button></span><hr /><hr /><hr /><hr /><hr /><tr><br></div><div id='redList'>"

function redd(it){
 console.log(it)    
 console.log(reds[1])
 
 subR.innerHTML = reds[it]
  
  xhr = new XMLHttpRequest
  xhr.open("GET","https://www.reddit.com"+reds[it]+rview[rflt.dataset.filter]+".json?limit=200",true)
  xhr.send(null)
  xhr.onreadystatechange = function() {
   if (xhr.readyState === xhr.DONE) {
    if(this.status === 200) {
     
      vids = JSON.parse(xhr.responseText) 
   
      cc = subR.dataset.ccc

    for (var j=0;j<=cc;j++){
     try{ 
      var rt = vids['data']['children'][j]['data']['title'],
      rl = vids['data']['children'][j]['data']['url'],
      rp = vids['data']['children'][j]['data']['secure_media']['oembed']['thumbnail_url'],
      rr = vids['data']['children'][j]['data']['permalink']     
      
      redList.innerHTML += "<td><a href='"+rl+"'><img style='width:150px;height:auto;max-width:120px' src='"+rp+"'></img></a><span style='max-width:68%;float:right;text-align:center;font-size:1.23em'><a class='yt-simple-endpoint style-scope ytd-compact-video-renderer' style='text-decoration:bold;font-size:1.23em;text-align:left;min-width:260px' href='"+rl+"'>"+rt+"</a><a target='blank' style='text-decoration:bold;color:black;float:right' href='https://www.reddit.com"+rr+"'>⮊</a></span></td><br>"
     }catch(e){console.log(e)}
     
    }   
      }
    }
  }
}

related.innerHTML += "</tr><button onclick='redList.innerHTML=\"\";redd(redR.value)'>Load more</button>"
redd(0)
 
window.onscroll = function() {
  var d = document.documentElement,
      offset = d.scrollTop + window.innerHeight,
      height = d.offsetHeight
  
  if (offset >= '2000' && offset <= '2300') {

     subR.dataset.ccc = 25
     
  }

  if (offset >= '2000' && offset <= '2100') {

     subR.dataset.ccc = 50
     
  }
  
  if (offset >= '3400' && offset <= '3500') {
   
     subR.dataset.ccc = 100
     
     
  }
  if (offset >= '5400' && offset <= '5500') {
  
     subR.dataset.ccc = 150
     
  }
  
  if (offset >= '7400' && offset <= '7500') {
   
     subR.dataset.ccc = 200
     
  }
}

At this stage, clicking a link is just reloading the page.

I tried many things, changing the content of elements, try to alter the "next" recommended video.

Also creating a link in the DOM, isn't working, it is reloading.

Here is the problem in a simple way:

Let's say you are in this YouTube page https://www.youtube.com/watch?v=YgGzAKP_HuM

From the console, how to load this non related id -q7ZVXOU3kM into the page, just like a link click on the recommendation sidebar?

The following is NOT working: Visibility monitor is not attached

 xhr = new XMLHttpRequest
 xhr.open("GET","https://www.youtube.com/watch?v=-q7ZVXOU3kM",true)
 xhr.send(null)
 document.body.innerHTML = xhr

enter image description here

18 OF MARCH 2018: END OF BOUNTY: QUESTION UNRESOLVED!!!

This is a fairly complex stuff.

The dom content is constantly changing at every reload, and it include recommended video id into the changing scripts. This id's only are allowed to use the ajax capabilities.

This recommendation are changing at any reload, but they are coming back in loop.

I am building a tool to deeply analyze differences, and I found many interesting things, much more that I was searching.

This topic isn't close!

It's not about the destination, it's about the journey.

enter image description here

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
NVRM
  • 11,480
  • 1
  • 88
  • 87
  • *"Also creating a link in the DOM, isn't working, it is reloading."* So if you dynamically create a link then append it, the page reloads? If you are able to inject an iframe, I believe that this is feasible. – zer00ne Mar 11 '18 at 15:10
  • Yes, when altering the dom, elements non in context acts as regular, with a document reload. I think there is some js to pull in first. The good parts is we can do ajax calls, because we are on the same domain^ I tried the iframe way, or just calls the full document html in an hidden div and replace the current one, but it fails. This is why i don't provide code, no success so far! – NVRM Mar 11 '18 at 15:17
  • Provide at least 2 working yt ids or if you already have please direct me to them. – zer00ne Mar 11 '18 at 15:25
  • Updated, thanks it will be useful. – NVRM Mar 11 '18 at 15:38
  • You can use **postMessage API**, I've made a [primitive playlist](https://embed.plnkr.co/jSARI280NoK7PUqQGO1t/) from an array of yt ids. – zer00ne Mar 11 '18 at 15:44
  • Absolutely, we are on the same origin. – NVRM Mar 11 '18 at 15:45
  • 1
    See updated comment for link, the first position video is dead but the others are still live. click the YT logo to open and close iframe – zer00ne Mar 11 '18 at 15:46
  • This isn't what i am looking for, but i am going to try to use this approach, will leave news if any success. I think i won't be able to load comments this way. – NVRM Mar 11 '18 at 15:53

3 Answers3

2

Despite the videoId being YgGzAKP_HuMif you inspect the <video> you will see something like that:

<video class="video-stream html5-main-video" src="blob:https://www.youtube.com/8e1ef216-1901-ec44-9b76-ea8276e368c6"></video>

You need to find how youtube create their Blob from a videoId and change the src of the player if you want to avoid reloading the page.

But maybe for your project you should think about using the IFrame Player API. It does exactly what you want. And if you want to stay on youtube.com you can replace the youtube original player by an iframe in the DOM.

Good Luck!

GuySake
  • 124
  • 1
  • 6
  • Thanks for reply. I am looking to change the whole page, including comments, titles. Here i am going to end-up with a video in the wrong context! It would be easier to replace directly the video by an iframe on this case! – NVRM Mar 15 '18 at 03:30
  • Then maybe you should also look at the all youtube API about [comments](https://developers.google.com/youtube/v3/docs/comments) and [comment treads](https://developers.google.com/youtube/v3/docs/commentThreads) – GuySake Mar 15 '18 at 09:18
1

it appears that there is no (public) api for the player on youtube :/ but there is one for the iframe youtube player - there you can just call

player.loadVideoById("Vw4KVoEVcr0", 0, "default");

for details see https://developers.google.com/youtube/iframe_api_reference#loadVideoById

That is a quick example I found where you see it in action: http://jsfiddle.net/QtBlueWaffle/8bpQ8/1/

If you really want to directly modify youtube you probably have to dig around in the obfuscated code :/ I tried to find it... but it's obfuscated so you can't easily find it. And even if you find it it will be something like _yt_player.h.xx.xy.h(). Also whenever it will be generated new (and maybe even it is even different for countries... ) your code may break.

Also, it appears to have nothing to do with polymer as there is a player element but it does not have a function to change the video. Apparently, it is only to place the player... control still resides within the js function.

I'm sorry to not have any better news - maybe someone else knows more.

daKmoR
  • 1,774
  • 12
  • 24
  • This is not matching my question, i can't say it resolve the problem. I have had further info. – NVRM Mar 17 '18 at 15:31
-1

Trying to find a hidden YouTube API is incredibly difficult and it's likely that the next tweak Google makes to YouTube will break your bookmarklet and you'd be back where you started. I think you should take another route altogether. Google provides an API to create, delete, and otherwise manipulate YouTube playlists here: https://developers.google.com/youtube/v3/docs/playlists.

If you’ve never used YouTube API before, see here: https://developers.google.com/youtube/v3/getting-started

Then, (finally!) you can write some JavaScript that creates YouTube playlists from reddit at your command.