clst / YouTubePlaylistAutoplayDisable

This userscript will turn YouTube's playlist autoplay off with every state change.
Creative Commons Zero v1.0 Universal
3 stars 0 forks source link

Script does not load when navigating to player with SPF (which seems to be always) #4

Open clst opened 9 years ago

clst commented 9 years ago

Possible fixes:

clst commented 9 years ago

Also disabling SPF via this userscript will work: https://openuserjs.org/scripts/JoeSimmons/YouTube_-_Disable_Red_Bar_aka_SPF

MegaScience commented 9 years ago

Edit2: Disregard this particular comment, as the next found a simple solution.

Another, slightly cleaner solution would be to use BrockA's waitForKeyElements.js library. I've been using this library to keep my personal Show YouTube Tags script working since SPF came into effect (Adds video tags back to the description). It looks to still use timing, but at least it is pretty clean. You could also inquire to the creator of this userscript, who has a somewhat different solution to SPF (see "start()" and "onNodeInserted()" commands): https://greasyfork.org/en/scripts/1317-download-youtube-videos-as-mp4/code

If you decide to use waitForKeyElements, you will need to add the first code block to the top and the second wherever you wish, modified accordingly (The library has commented documentation for further understanding):

// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require     https://gist.github.com/raw/2625891/waitForKeyElements.js
waitForKeyElements("#watch7-container", tagAdditionCommand, false);

Edit: I almost always open videos in a new page, but since I did make this post, I was doing tests and did find a problem: Opening a video by clicking doesn't activate any of my userscripts initially. Troublesome.

If I had my way, I'd just find a way to hook into SPF to cause events. In fact... This is a stretch, but considering it is open source with an issue/enhancement/etc. list, couldn't we request events for SPF activity? Of course it'd be a while before we see that, but it would allow us to follow them... Probably couldn't work that way, having looked into the idea, but I'd still rather the API tell everything else in the environment that it is doing something, than forcing the user to deal with it's actions second-hand. Seems like something they'd consider adding.

MegaScience commented 9 years ago

I've been doing research and found a simple, definite solution to this issue. You won't need to disable SPF to apply this. The downside is this script will have to target YouTube as a whole, since as you know you won't be loaded through an SPF page change. However, activating on SPF page changes is simple. I added this to the bottom of my tag script:

window.addEventListener('readystatechange', appendTags, true); window.addEventListener('spfdone', appendTags);

As you see, spfdone is the event handle for when SPF has done loading its new content. From there, you add a check to your script assuring you are in a video playlist page. For reference, this is where I found the absolute solution: https://github.com/YePpHa/YouTubeCenter/issues/1060 As well as their official example app page noting these listeners: https://github.com/youtube/spfjs/blob/39eab71764c49ad2bb169d6bf7a5eca27bda7266/src/server/demo/static/app.js

Edit: I adjusted your code and it appears to work. I added the fix, as well as auto-updating URLs and incrementing the version. Also noted this particular version was modified by me, although that wouldn't need to be included. Just for memory. This can be adjusted as needed:

// ==UserScript==
// @name        Playlist Autoplay needs to stay OFF!
// @version     0.06
// @description Why won't it stay OFF?!? This will turn playlist autoplay off from time to time
// @include     /^https?://(www.)?youtube\.com/.*$/
// @run-at      document-end
// @copyright   2014, Claudius Steinhauser - Modified by MegaScience
// @updateURL   https://github.com/clst/YouTubePlaylistAutoplayDisable/raw/master/YouTubePlaylistAutoplayDisable.user.js
// @downloadURL https://github.com/clst/YouTubePlaylistAutoplayDisable/raw/master/YouTubePlaylistAutoplayDisable.user.js
// ==/UserScript==

function turnOffAutoplay() {
    if(location.search.search(/(&|\?)list=/) == -1) return;

    //does not seem to be needed with Tampermonkey, uncomment if you want it:
    /*
    if(typeof onYouTubePlayerReady === 'function'){
        //causes a hang/recursion when YoutubeCenter was loaded first
        if(!(typeof ytcenter === 'object'))
            ap_old_onYouTubePlayerReady = onYouTubePlayerReady;
    }
    */

    window.onYouTubePlayerReady = onYouTubePlayerReady = function(ytplayerobj){
        if(console) console.log('YT AutoPlay Off loads...');
        ytplayerobj.addEventListener('onStateChange', ytaptogglefunc = function(){
            if((apbut = document.querySelectorAll('.playlist-nav-controls .yt-uix-button-player-controls.toggle-autoplay')) && (apbut = apbut[0]) && apbut.classList.contains('yt-uix-button-toggled')){
                if(console) console.log('YT AutoPlay Off toggles Autoplay off...');
                apbut.click();
            }
        }, false);
        ytaptogglefunc();
        if(typeof ap_old_onYouTubePlayerReady === 'function'){
            return ap_old_onYouTubePlayerReady(ytplayerobj);
        }
    };
}

window.addEventListener('DOMContentLoaded', turnOffAutoplay, true);
window.addEventListener('spfdone', turnOffAutoplay);
clst commented 9 years ago

cool, thanks a lot for your efforts. Would you mind if I updated my code with your changes? It's a lot cleaner. Does it work every time without the timeout? I noticed that sometimes the icon will go dark but the YouTube JS code that actually toggles the autoplay was not ready yet and will then still load the next video.

MegaScience commented 9 years ago

In its current state, it works as well as it would without SPF. I'll look into a more direct solution than executing a click on the button. I've been trying to set ytplayer.config.args.autoplay = 0;, but even doing this from console with Firebug well after the page has loaded and confirming it has been changed has yielded nothing. How it knows whether or not to autoplay has to be stored somewhere... Perhaps it checks the element attributes, like the aria attribute. I'd rather not piggyback more ready checks if these checks aren't particularly reliable, especially if I can influence it directly. I have some code in the works which just needs to find the right information to modify.

I changed the first event listener after you commented, which makes it a bit cleaner.

MegaScience commented 9 years ago

I looked into how YouTube Center accomplishes this task, and they are actually having issues as well. They were hooking into the getState command YouTube runs, but as far as I can tell (as well as others having issues), YouTube seems to be ignoring the autoPlay value from this now. Very bad, too. If you look into the getState command, it derives the value of autoPlay from a bunch of different checks. As such, it would be the first centralized place to influence the value, but it seems to be an impossibility for now.

In the meantime, I changed the listener implementation since the script activates multiple times, times after the first being out-of-scope:

// ==UserScript==
// @name        Playlist Autoplay needs to stay OFF!
// @version     0.06
// @description Why won't it stay OFF?!? This will turn playlist autoplay off from time to time
// @include     /^https?://(www.)?youtube\.com/.*$/
// @run-at      document-end
// @copyright   2014, Claudius Steinhauser - Modified by MegaScience
// @updateURL   https://github.com/clst/YouTubePlaylistAutoplayDisable/raw/master/YouTubePlaylistAutoplayDisable.user.js
// @downloadURL https://github.com/clst/YouTubePlaylistAutoplayDisable/raw/master/YouTubePlaylistAutoplayDisable.user.js
// ==/UserScript==

function turnOffAutoplay() {
    if(location.search.search(/(&|\?)list=/) == -1) return;

    //does not seem to be needed with Tampermonkey, uncomment if you want it:
    /*
    if(typeof onYouTubePlayerReady === 'function'){
        //causes a hang/recursion when YoutubeCenter was loaded first
        if(!(typeof ytcenter === 'object'))
            ap_old_onYouTubePlayerReady = onYouTubePlayerReady;
    }
    */

    window.onYouTubePlayerReady = onYouTubePlayerReady = function(ytplayerobj){
        if(console) console.log('YT AutoPlay Off loads...');
        ytplayerobj.addEventListener('onStateChange', ytaptogglefunc = function(){
            if((apbut = document.querySelectorAll('.playlist-nav-controls .yt-uix-button-player-controls.toggle-autoplay')) && (apbut = apbut[0]) && apbut.classList.contains('yt-uix-button-toggled')){
                if(console) console.log('YT AutoPlay Off toggles Autoplay off...');
                apbut.click();
            }
        }, false);
        ytaptogglefunc();
        if(typeof ap_old_onYouTubePlayerReady === 'function'){
            return ap_old_onYouTubePlayerReady(ytplayerobj);
        }
    };
}

if(window.top === window.self) {
    window.addEventListener('readystatechange', turnOffAutoplay, true);
    window.addEventListener('spfdone', turnOffAutoplay);
}
else {
    if(console) console.log("YT AutoPlay Off:  Warning - Only applying listeners to top-level.");
}
clst commented 9 years ago

Yes, that was why I resorted to just clicking the button in the first place. The internals of YouTube's Javascript seem a little weird.

MegaScience commented 9 years ago

Taking example from YouTube Center, I finally found a method to initially turn off. Note that you could probably clean up what I have here a bit: http://pastebin.com/ZFyQjhk6

Notes: The way the new method works would keep disabling autoplay each video change. As such, I added a check verses the page. If the last page was a video playing in the same playlist, the script stops itself. If you move out of the playlist and back in, it will disable autoplay again.

If you could assure the user always used SPF, it could be easier to do the above. You'd just keep an Old and New list value, and whenever the page changes, move New into Old and get the current into New. However, since we can't be sure, I'm using ytplayer's history attribute to figure out where they came from, which appears to work with and without SPF.

There is also an extensive error-checking command, so it will fail 'gracefully' if YouTube changes something which breaks this.

The current code won't work on the new playlist types that don't have an autoplay button, but it should be simple to adjust for that. Supporting all types might complicate this a bit, but it shouldn't be that much of an issue.

Besides the main part of the code, I've included settings for other areas related to autoplay, in case YouTube ever decides that these are required. We could leave them alone, but there would be inconsistencies between different autoplay values.

Edit: Added some comments to the code.

Edit2: Realized "referrer" property isn't even created if the page comes from a new tab. Adjusted the code to not require "referrer" exists to work. "list" is still required, though. Strange the variable isn't even put blank for future use, but whatever is most efficient I suppose.

MegaScience commented 9 years ago

Just to say it outright: Yes, this no longer works. I even turned on ALL the backup options I put into the script, but they don't work. No one else has found a way around it, without extremely hacky roundabout methods. It seems they completely tore out the old direct control, so we'll need to see if there's any other variables or parameters or anything lying around that could influence the behavior.

MegaScience commented 9 years ago

https://github.com/YePpHa/YouTubeCenter/issues/1192#issuecomment-68611967

As you see, Yonezpt figured out a way to stop autoplay again. He has also coded it in a way where it is less likely to be broken by future changes by YouTube (by searching the command through a unlikely-to-be-changed variable name within). I've tested this, and it definitely works. Finally, I can watch the little easter egg clips after the end panel of a video without worrying the page will abruptly shift.

Yonezpt commented 9 years ago

@MegaScience Be aware that the solution that I found is not complete, it only works when you are not in fullscreen (which is already a big step). I also created a userscript for it with a proper button to control it: https://openuserjs.org/scripts/ParticleCore/Playlist_Autoplay_Control_for_YouTube

I am still trying to figure out if there is a way to control it when in fullscreen as well. Hopefully YePpHa will do some magic with this when has more free time again, if not then I will keep trying to find a way.

EDIT: Oh no, I just noticed that this was a completely different repository, I am so sorry for "hijacking" it like this. If you wish for me to remove my comment just say the word.

MegaScience commented 9 years ago

@Yonezpt I wasn't aware, as I only ever use fullscreen when the video has text I'm interested in reading and a high enough resolution is available. I couldn't focus on an entire 1080p area anyway, so fullscreen would take away from the experience if anything.

I actually adjusted your code when I put it to use:

        var button = document.getElementsByClassName("toggle-autoplay");
        if(button.length && button[0])
            user_wants_autoplay = button[0].classList.contains("yt-uix-button-toggled");

I put this under autoplayDetour to use the old button as it used to work. Yes, I know they sorta repurposed that button, but I think it still counts since replay stopping is meant to stop autoplaying from the last video back to the first (right?) - basically making the button do EVERYTHNG with the script, again. I also know they could change names and break it, but the if would filter that and return to the default value behavior in that case.

Yonezpt commented 9 years ago

Of course you can repurpose the original autoplay button, having it control autoplay by default will also work as repeat playlist as well when it reaches the end of a playlist (I assume). Having a fallback filter in case they change the name is a good plan to have, especially during this period where they have been changing so many things so often.

If all goes well they will end up seeing that the benefit from the new "repeat playlist" change is not worth the number of negative feedback (the forum has a huge thread of users complaining about the change). The last change that caused so much revolt was the one where every video in a channel list would open with a playlist by default and they ended up reverting that one.

Here's to hoping that they revert this one as well, because to have a fully functioning autoplay control (from what I have seen so far) you'd need browser access api, something that userscripts cannot use (at least it can't use the necessary functions that control XHR comms), but only achievable by coding an extension/addon.

MegaScience commented 9 years ago

@Yonezpt I'm just astonished they seem to be going out of their way to block all our methods of preventing autoplay. Personally, I stopped viewing drastically since the change, since it was frustrating running all my videos through Watch Later, but the next video starting immediately. I wait until the end of the video to comment, and not all videos have an end-slate to pause on, so it has been infuriating.

As an added note, since your code, I've been watching videos regularly again. Just my part in the statistic... Maybe they want to reduce viewership to has an overall reduction in processing costs...

Yonezpt commented 9 years ago

Not at all, they want people to watch more videos and they know that there are millions that leave the playlist playing more times than not, which means more ads viewed and more money for them. This is their main goal with these kinds of changes, they have even added that new "autoplay suggested videos" outside of playlists: http://www.tubefilter.com/2014/12/12/youtube-autoplay-video-suggestions/

Which backfired a lot because even when you turned it off, it would turn on again and wouldn't stop.

But that behavior they have already changed, now they changed it so that it remembers when the user disabled the autoplay.

MegaScience commented 9 years ago

I have heard of that, but I doubt I'm the only one viewing less because of the change. Not even as boycotting of some kind, but because I actually feel less inclined to watch videos with autoplay forcibly turned on, or on in general for that matter.