Google Play Music – Find Songs Not in Any Playlists

google-play-music

Related but distinct from my question on finding which playlists a particular song is in, is there any way to find songs in my Google Play library that are not in any playlists? Maybe a sort-by-number-of-playlists-it's-in feature?

I'd be fine with a userscript or third party solution, though obviously a built in way is preferable.

Best Answer

First I downloaded a list of all of my songs in an array in the format "Artist - Song Name" using the following script (pasted into the console on a Google Play page).

Note: If you want the data in alphabetical order by song name, you need to change the format from "Artist - Song Name" to "Song Name - Artist" in both this script and the playlist script I mention below.

(function () {
        console.log("Just a moment locating music DB...");
        const data = [];
        indexedDB.databases().then(info => {
            const name = data.dbName = info.find(db => db.name.indexOf("music_") === 0).name;
            indexedDB.open(name).onsuccess = e => {
                console.log("Extracting tracks from DB " + name);
                const t = e.target.result.transaction("tracks", IDBTransaction.READ_ONLY); 
                t.oncomplete = () => {
                    Object.assign(document.createElement("a"), { download : name + ".tracks.txt", href: URL.createObjectURL(new Blob([JSON.stringify(data.sort())]), {type: "text/json" })})
                        .dispatchEvent(new MouseEvent("click", {view: window, bubbles: true, cancelable: true}));
                }
                t.objectStore("tracks").openCursor().onsuccess = e => {
                    if (e = e.target.result) { 
                        Object.values(JSON.parse(e.value)).forEach(t => data.push(t[3] + " - " + t[1]));
                        e.continue();
                    }
                }
            }
        }).catch(()=>console.log("Sorry, cannot complete data dump :("));
    })()

Other potentially useful indexes: 4 for album, 11 for genre, and 13 for length.

Then I compared this list with my playlist text list using the following script and recorded all songs that are not found in the playlist file to a new file (I made a .html page to make this easier - see this JSBin for a live example. Note that the text files should be in the JSON format that is the default of my exporting scripts).

<!DOCTYPE html>
<html>
<head>
    <title>Google Play songs not in playlist</title>
</head>
<body>


<label>Song text file (generated from <a href="https://webapps.stackexchange.com/a/125423/140514">this answer</a>): <input type="file" id="songInput"></label>
<br>
<label>Playlist text file (generated from <a href="https://webapps.stackexchange.com/a/106604/140514">this answer</a>): <input type="file" id="playlistInput"></label>
<br>
<textarea style="width: 500px; height: 200px;"></textarea>

<script type="text/javascript">
var songInput = document.getElementById("songInput"),
    playlistInput = document.getElementById("playlistInput"),
    result = document.querySelector("textarea");

var songList, 
    playlistData;

function loadData(id, elem) {
    if(elem.files
    && elem.files[0]) {
        let myFile = elem.files[0];
        let reader = new FileReader();

        reader.addEventListener('load', function (e) {
            if(id === "songInput")
                songList = JSON.parse(e.target.result);

            if(id === "playlistInput")
                playlistData = e.target.result;

            checkBothAdded();
        });

        reader.readAsBinaryString(myFile);
    }
}

function checkBothAdded() {
    if(songList
    && playlistData) {
        getSongsNotInPlaylist();
    }
}

var songsNotInPlaylist = [];
function getSongsNotInPlaylist() {
    for(let song of songList) {
        if(playlistData.indexOf(song) === -1) {
            songsNotInPlaylist.push(song);
        }
    }
    result.value = JSON.stringify(songsNotInPlaylist, null, '\t');
}

songInput.addEventListener("change", function() {
    loadData("songInput", this);
});

playlistInput.addEventListener("change", function() {
    loadData("playlistInput", this);
});
</script>
</body>
</html>