YePpHa / YouTubeCenter

YouTube Center is a userscript designed to expand the functionality of YouTube. It includes the ability to download the video you're watching, auto selecting your preferred video quality and much more.
MIT License
2.89k stars 519 forks source link

Fix playlist autoplay control #1192

Open Yonezpt opened 9 years ago

Yonezpt commented 9 years ago

Continuation of https://github.com/YePpHa/YouTubeCenter/issues/904#issuecomment-64286671

YePpHa commented 9 years ago

Summary of bug: Previously YouTube Center had a feature where it could disable the auto-play for mix playlists (YouTube generated playlists). However, YouTube changed something which broke this. This thread is about working on re-implementing this feature.

Edit: Seems like YouTube is calling their generated playlists for 'Radio Playlist'.

YePpHa commented 9 years ago

It seems as though yt.www.watch.lists.getState() is still used to determine if it should auto-play the next video in the playlist. As you probably know getState() returns an object with the property autoPlay. If that value is manipulated to false it would stop the auto play of all playlists.

I forcefully made the auto-play button appear on Radio Playlists and it seems to work. Uploading new dev version for testing.

Yonezpt commented 9 years ago

So, essentially, your previous fix is still functional, which is great! Will test it out tomorrow.

Yonezpt commented 9 years ago

@YePpHa Tried the Prevent playlist auto-play and Playlist auto-play (each enabled and disabled) options with both player types, it still changed to the next video on a normal playlist. With YTC disabled what does your tooltip read when you hover the playlist repeat button? Is it "Repeat playlist" or "Autoplay"? Examples from #1139 Old playlist button tooltip screen shot 2014-11-13 at 4 53 51 pm New playlist button tooltip ss

I have the feeling that you still have the old playlist button and that is why that fix is still working for you and not for me.

MegaScience commented 9 years ago

I looked into how YouTube Center currently attempts toggling Autoplay and did some tests. When using the below script through Firebug in an active playlist video page, autoPlay would return false but still continue to the next video at the end. I conducted a similar test through userscript, of course applying unsafewindow as mirrored in YouTube Center's code, with similar effects:

var origGetState = null;

function newGetState() {
    var state = origGetState();
    state.autoPlay = "false";
    return state;
}

if (yt && yt.www && yt.www.watch && yt.www.watch.lists && yt.www.watch.lists.getState) {
  origGetState = yt.www.watch.lists.getState;
  yt.www.watch.lists.getState = newGetState;
}

console.log(yt.www.watch.lists.getState());

As far as I can tell, YouTube isn't using this value anymore. More tests are needed though.

YePpHa commented 9 years ago

@MegaScience it might be that there are two versions of YouTube where the autoPlay property works and not. Could you perhaps send me the code in Settings -> Debug -> YouTube Experiments in the text field beside the set code button.

Yonezpt commented 9 years ago

Since I am also experiencing the same behaviour as he is I took this opportunity to share the code that is running on my end in case it might be helpful in some way: dEwoJdNaN8U

Also you linked to this thread, maybe you thought you were in a different one while you linked it.

YePpHa commented 9 years ago

ohh yeah. I actually though I was in a different issue.

Edit: fixed.

kwliou commented 9 years ago

Update: After I cleared my Youtube cookies it now works correctly. Not sure why...

No idea if this is useful. But I ran into this same issue (I don't use Youtube Center but I write my own Firefox GreaseMonkey scripts for Youtube) and for some reason when I'm logged in, the JSON returned when going to the next video in a playlist always turns autoplay back on? Not even the usual temp cookie s_tempdata-<hash>=ei=<window.yt.config_.EVENT_ID>&feature=BFa&ved=<clicktracking>&autoplay_disabled=true; domain=youtube.com; path=/; expires=<5sec later> works.

But when I'm logged out in another browser, it works correctly...

So for now I'm just forcing autoplay off altogether:

var playlist = document.querySelector(".playlist-header-content");
if (playlist)
  playlist.setAttribute("data-initial-autoplay-state", "false");
window.yt.config_.__defineGetter__("WATCH_PLAYLIST_ALWAYS_AUTOPLAY", function() { return false; });
window.addEventListener("spfpartprocess", function(e) {
  var part = e.detail.part;
  var body = part.body;
  if (body) {
    body.content = body.content.replace(/( toggle-autoplay [^"]*) yt-uix-button-toggled/, "$1");
    body.content = body.content.replace("data-initial-autoplay-state=\"true\"", "data-initial-autoplay-state=\"false\"");
  } 
  // if (part.foot)
  //   part.foot = part.foot.replace("'WATCH_PLAYLIST_ALWAYS_AUTOPLAY': true", "'WATCH_PLAYLIST_ALWAYS_AUTOPLAY': false");
});
Yonezpt commented 9 years ago

@YePpHa I have been working on this ever since we discussed about it, but unlike the html5 resizing issue, this one took me longer to work the kinks out (I think).

So we have discussed about various methods that might work in https://github.com/YePpHa/YouTubeCenter/issues/904#issuecomment-64073138 and they do work, but neither is a proper fix, it breaks other functions that depend on those that we hijack and we are forced to go at great lengths to avoid those, causing the code to become bigger and heavier.

We also already know that youtube playlists (at least Youtube generated playlists) now ignore the control values that were once responsible for the autoplay status changes: yt.config_.LIST_AUTO_PLAY_VALUE, yt.config_.WATCH_PLAYLIST_ALWAYS_AUTOPLAY and yt.www.watch.lists.getState().autoPlay no longer work

However, I did find two days ago (more or less) that there is a control function responsible for player/playlist redirections which also receives the event type associated with the to-load url and this is the one that I had no issues (so far), and works perfectly for the autoplay functionality. The hijack I use is just like the one I used in the html5 resizing fix, but a bit more polished in my opinion:

// This variable is an example that represents if the
// user activated or not the autoplay button in the playlist
var user_wants_autoplay = false;

function autoplayDetour(b) {
    return function () {
        // First we will check wether or not the function contains a feature attribute
        // and if that attribute is not autoplay. If it is then we won't let it load
        // depending on the user choice, effectively working as an autoplay function
        var args = arguments;
        if(!user_wants_autoplay && (!args[1] || args[1].feature && args[1].feature !== 'autoplay') || user_wants_autoplay) {
            b.apply(this, arguments);
        }
    };
}

// The function we want to hijack is located in the _yt_www object, so
// we will iterate through its properties until we find the droids we are looking for
Object.keys(window._yt_www).some(function (b) {
    // From the _yt_www property list we will filter only the one that is a function and
    // contains a string that is less likely to change during minifier changes
    if(typeof window._yt_www[b] === 'function' && window._yt_www[b].toString().indexOf('window.spf.navigate') !== -1) {
        // Now that we found the function that we want we will attach
        // our detour function which will control the autoplay behaviour.
        // This function is perfect because it receives a .feature event
        // detailing exactly what kind of url call it is trying to load
        window._yt_www[b] = autoplayDetour(window._yt_www[b]);
        // return true is only here to stop the function from iterating
        // through the rest of the properties, there is no need to keep going
        // if we already found our droids
        return true;
    }
});

Copy + Pasting the above code directly into the console should block playlists from changing video whenever the current video stops. Changing user_wants_autoplay to true in the console restores the autoplay behaviour allowing the playlist to skip to the next video when the current video ends. This variable is only there for debugging only, replace it with whichever control setting you have in your script, the one that is affected whenever a user clicks on the autoplay function to enable/disable it.

By the way, remember to remove your PREFS cookie (I think it is this one that holds old experiment codes) in case you are still receiving the "Autoplay" tooltip on the new "Repeat Playlist" button. I really doubt this experiment (if it is one) will be deprecated.

There is no need to include my comments if you decide to use this code, it only adds bulk to your addon, I only write them for clarification in case anyone wants to understand it.

ReporterX commented 9 years ago

@Yonezpt Hmm... your code does not work. It only works for the first time, but after you click another video on the playlist, it will autoplay the next video.

Firefox 34.0.5 YTC Developer Version B461

@YePpHa @Yonezpt This Firefox addon works to stop the autoplay. https://addons.mozilla.org/En-US/firefox/addon/nextvid-stopper-for-youtube/

Yonezpt commented 9 years ago

@ReporterX That is probably because you have SPF disabled which causes the code to refresh when a new page loads, effectively "losing" the code you inserted via the console. With the code properly inserted it will work correctly even with SPF disabled.

Regarding that plugin you linked, it is not a proper fix. In fact, it doesn't work again because the author is relying on the youtube scripts not changing over time and it also relies on intervals which, in my opinion, is not a proper way to run a function. My solution doesn't depend on any of that, it only needs the target script to exist and a button for the user to toggle autoplay, and that is it.

Yonezpt commented 9 years ago

@ReporterX I converted my fix into a more complete userscript with a dedicated control button, which you can try from here: https://openuserjs.org/scripts/ParticleCore/Playlist_Autoplay_Control_for_YouTube

However, like the addon you linked, this doesn't affect when you are using fullscreen, I am still trying to find a way to make it work when in fullscreen.

@YePpHa Feel free to use the source code in any way you like if you find it usefull.

ReporterX commented 9 years ago

@Yonezpt Great script. It works for me even under full screen. I clicked the link of a playlist video. Press "full screen" mode. Wait till it plays to the end. It won't autoplay.

A minor issue. I have many userscripts. As time goes by, I may forget what this script is for since the name is just called PAC. I prefer a clearer name, like Playlist Autoplay Control for YouTube.

Yonezpt commented 9 years ago

It works even in fullscreen? It doesn't for me, not with the flash or html5 player.

You can change the name of the script to whatever you want, simply open it and change the name in the // @name line I also added a clear description to the script so you won't have trouble understanding what script it is.

But I guess it would be best to have a more intuitive name already displayed, I will change the script name.

And name is changed, if you trigger the update it will change the name automatically.

MegaScience commented 9 years ago

Instead of manually building the user-autoplay button from a static list of classes, why not find the Repeat button and use .cloneNode(true);? This way, all relevant classes are included as they are used in the current page, named correctly should YouTube change any names. This would mean you might have to use .removeAttribute();, change some attributes, but I find that better to the static strings used. You could fallback to manual button generation if the button isn't found.

I did that with my userscript for adding Video Tags back to the list area under the video. When YouTube changed the look of the description area last year, the script actually still worked, and looked correct. I also use a userstyle which makes the site dark (since I watch a lot of dark videos), and the matching format to the previous entry in the list keeps it working with that as well.

Yonezpt commented 9 years ago

@MegaScience It's a matter of personal preference, I prefer to have control with a dedicated button to avoid creating fallbacks that might account for any future possible changes to the original button. It's just a concept, if anyone wants to tweak the way they like then go for it, it makes me really happy that someone is using code that I wrote and also changing it to their taste.

MegaScience commented 9 years ago

Ah. I just meant, since you're using mostly YouTube-default classes, changes to those could easily make things a bit weird. It's easier to remove some you know you don't want, than add an entire list which might get removed. But I suppose the same issues are there either way.

ReporterX commented 9 years ago

@Yonezpt Yes it works for me in full screen, both flash and html5 player. Great work. Good change about the name. :)

Yonezpt commented 9 years ago

@MegaScience Just remembered today another big reason for why I prefer to have a dedicated button, it's because there aren't any buttons to clone when you are watching a youtube generated playlist/radio playlist, such as this one: https://www.youtube.com/watch?v=JRWox-i6aAk&list=RDHCs5QzqZ9y2oU

If I code the button from scratch then I have total control over it, even if the other buttons are not present. I know this comes a bit late, but I just wanted to leave this here for future reference.

ReporterX commented 9 years ago

@Yonezpt How come those buttons don't show up in all playlists? I hope there is a script/addon to focus those buttons to always show up in all kinds of playlists.

Yonezpt commented 9 years ago

Because YouTube chose to disable the playlist controls in radio playlists, which as far as I could see are the only playlists that have the controls disabled.

MegaScience commented 9 years ago

@Yonezpt I see your point. Worst case, you do techncailly have the Save to Playlists button, but I doubt that matches fully.

Djfe commented 9 years ago

I'm not sure if this belongs here, but I get a huge amount log entries in the Firefox console saying

"[Playlist] getState not found!"

while watching playlists

YePpHa commented 9 years ago

@Djfe it's probably because that part is from an old technique that is somewhat deprecated. It will just check if the yt.www.watch.lists.getState function exists and if it's not it will wait 2.5 seconds and try again. It will also print [Playlist] getState not found! into the console every time this happens.

Djfe commented 9 years ago

well if it's deprecated then it would be nice if you could remove it some day ;)