murdos / musicbrainz-userscripts

Collection of userscripts for MusicBrainz, by various authors
553 stars 92 forks source link

Implement generic method of dealing with SPA sites #230

Open atj opened 5 years ago

atj commented 5 years ago

It would be really useful to have a method of dealing with SPA sites such as Beatport and Deezer, where currently the userscript runs only on the first visit, as subsequent clicks are loaded and rendered entirely via javascript. At the moment you have to manually reload the page in order to get the import buttons to appear.

I have implemented a quick and dirty solution for use with my Deezer userscript which works the vast majority of the time:

var LocationWatcher = {
    interval: 1000,
    timer: null,
    location: null,
    onChange: function(newLocation) {
        console.log('Changed: ' + this.location);
    },

    start: function() {
        if (this.timer === null) {
            let obj = this;
            this.timer = window.setInterval(function() {
                obj.checkLocation();
            }, this.interval);
        }
    },

    stop: function() {
        if (this.timer !== null) {
            window.clearInterval(this.timer);
            this.timer = null;
        }
    },

    run: function() {
        this.stop();
        this.start();
    },

    checkLocation: function() {
        let curLocation = window.location.href.replace(/\?.*$/, '').replace(/#.*$/, '');

        if (this.location !== curLocation) {
            if (this.onChange !== null) {
                this.onChange(curLocation);
            }
            this.location = curLocation;
        }
    },
};

This is then used like so:

$(document).ready(function() {
    /* setup code */

    let lw = Object.create(LocationWatcher);
    let urlRegex = RegExp('^https?://www.deezer.com/.+/album/.+$');

    lw.onChange = function(location) {
        if (!urlRegex.test(location)) {
            return;
        }

        /* page has changed so load release data and add import buttons etc. */
    };

    lw.run();
});

I'm no expert in JS so I'm sure it could be done much more elegantly than this. I'd be happy to work on something and submit a pull request but would appreciate hearing your thoughts first.

atj commented 5 years ago

Any comments on this? Happy to close and maintain something myself if nobody is interested or you don't think it's practical.

Jormangeud commented 5 years ago

I think it could be quite easy to hook to the function that fetches the new content. I did that with the qobuz script: https://github.com/murdos/musicbrainz-userscripts/blob/4cbedab826d13be72b480b2d81eec6dfacd67966/qobuz_importer.user.js#L303 And the same idea went into the spotify script in the PR.

That hooking could probably be implemented as a generic function in the library as a function getting the url to hook to and the link building function as parameters.