-
-
Save avillp/29137b5abf7be44dd46e29ad8ff39e2e to your computer and use it in GitHub Desktop.
/* | |
Unportify is a script that exports your Google Play music to text. | |
Copyright (C) 2016 Arnau Villoslada | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License along | |
with this program; if not, write to the Free Software Foundation, Inc., | |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
Copy of License: | |
https://www.gnu.org/licenses/gpl-3.0.html | |
*/ | |
/* | |
HOW TO USE (Chrome): | |
1. Go to Google Play Music | |
2. Once loaded, right click anywhere in the page and left click on "Inspect" (or press CTRL + SHIFT + I) | |
3. Go to the "Console" tab. | |
4. There's a little dropdown, select "top". | |
5. Paste this script next to the ">" and press enter. | |
6. You can now close the console. (Click the X at the right or press CTRL + SHIFT + I). | |
7. Go to the playlist you want to export. | |
8. Press 0 to start the export process. | |
If pressing 0 does nothing, reload the page and make sure you you inject the code on "top" (step #4). | |
9. Follow on-screen instructions. | |
10. Once it's done, a file containing your playlist will download, paste it here: | |
https://labs.villoslada.me/unportify. | |
NOTE: If the number of catched songs is lower than the number of songs in your playlist, | |
you might have duplicates, if not, try lowering the scroll speed. | |
*/ | |
/* Pre-start variables */ | |
unportify_url = 'https://labs.villoslada.me/unportify'; | |
message = {}; | |
message['startup-1'] = 'Unportify v1.4.3 injected, you can now close this window.'; | |
message['startup-2'] = 'Press 0 at any time to start exporting your playlist.'; | |
message['albums'] = 'Would you like to also export the albums? \n(Not needed for Unportify importer)'; | |
message['scroll'] = 'How fast would you like me to scroll? (Leave at default if you\'re not sure).\n\ | |
Lower if it\'s not catching the entire playlist', '100'; | |
message['scroll-error'] = 'Please, only use numbers.\nDefault should work fine on a average speed Internet connection'; | |
message['scroll-startup'] = 'I\'m going to start scrolling, I\'ll warn you when I\'m done.\nPlease, don\'t switch tabs.'; | |
message['open-unportify'] = 'Would you like to import this to Spotify? \n(Opens ' + unportify_url + ')'; | |
console.log(message['startup-1']); | |
alert(message['startup-2']); | |
// Sets the starting variables | |
function startingVariables() { | |
// JSON containing playlist | |
playlist_string = '{"tracks": ['; | |
// Google Music uses a main container to scroll instead of the browser window, get it. | |
main_container = document.querySelector("#mainContainer"); | |
// Ask if the user would like to export albums | |
export_albums = confirm(message['albums']); | |
// This function keeps asking for the scroll speed until a number is given | |
function askAutoScrollSpeed() { | |
auto_scroll_speed = prompt(message['scroll'], '100'); | |
if (isNaN(parseInt(auto_scroll_speed))) { | |
alert(message['scroll-error']); | |
askAutoScrollSpeed(); | |
} | |
} | |
askAutoScrollSpeed(); | |
// Scroll margin. Used to make sure the script detects that the bottom of the page has been reached. | |
scroll_margin = 50; | |
alert(message['scroll-startup']); | |
// Set the scroll speed to the user's desired speed | |
auto_scroll_speed = parseInt(auto_scroll_speed); | |
// Go to the top of the playlist | |
main_container.scrollTop = 0; | |
// Set the starting auto scroll position | |
auto_scroll_pos = main_container.scrollTop; | |
} | |
// Gets all of the loaded songs from the playlist | |
function getSongs() { | |
var i, j, playlist_div = document.querySelectorAll('.song-row'); | |
for (i = 0, j = playlist_div.length; i < j; i++) { | |
var track = playlist_div[i]; | |
var title = encodeURI(track.querySelector('[data-col="title"] span.column-content').textContent); | |
var artist = encodeURI(track.querySelector('[data-col="artist"] span.column-content').textContent); | |
var album = encodeURI(track.querySelector('[data-col="album"] span.column-content').textContent); | |
// Clean possible characters that could mess up the JSON | |
title = title.replace(/"/g, '\\"'); | |
artist = artist.replace(/"/g, '\\"'); | |
album = album.replace(/"/g, '\\"'); | |
// Checks if the album needs to be exported | |
if (export_albums) { | |
var song = ('{"artist": "' + artist + '", "title": "' + title + '", "album": "' + album + '"},'); | |
} else { | |
var song = ('{"artist": "' + artist + '", "title": "' + title + '"},'); | |
} | |
// Only add if the song isn't already added the list | |
if (playlist_string.includes(song) != true) { | |
playlist_string += song; | |
} | |
// Will contain the last processed song | |
last = track; | |
} | |
} | |
// As Trump would say, we will do many many things, and it will be great. | |
function scrollAction() { | |
var last_song_pos = last.offsetTop; | |
var window_scroll_pos = main_container.scrollTop; | |
// Checks if we reached the last loaded song | |
if(window_scroll_pos >= last_song_pos) { | |
getSongs(); | |
} | |
var limit = main_container.scrollHeight - main_container.offsetHeight - scroll_margin; | |
// Checks if we arrived at the bottom of the playlist | |
if (window_scroll_pos >= limit) { | |
// Stops the page scroll | |
clearTimeout(scroll_delay); | |
// Get the playlist name | |
var playlist_title = document.querySelector('h2[slot="title"]').textContent; | |
// Remove theh last , | |
playlist_string = playlist_string.replace(/.$/, ''); | |
// Close JSON. | |
playlist_string += '], "playlist": "' + playlist_title + '", "exported_by": "Unportify"}'; | |
// Convert to human readable JSON | |
playlist_string = JSON.stringify(JSON.parse(playlist_string), null, 4); | |
// Exports the playlist | |
alert('Catched ' + (playlist_string.match(/"artist":/g) || []).length + ' songs.'); | |
var blob = new Blob([playlist_string], {type: 'text/plain;charset=utf-8'}); | |
saveAs(blob, playlist_title + '.txt'); | |
if (confirm(message['open-unportify'])) { | |
window.open(unportify_url, '_blank'); | |
} | |
// Awaits for the user to press the start key again | |
window.onkeypress = function(event){startExport(event)} | |
} | |
} | |
// This funcion makes the page scroll automatically. | |
function pageScroll() { | |
main_container.scrollTop = auto_scroll_pos; | |
scroll_delay = setTimeout(pageScroll,10); | |
scrollAction(); | |
auto_scroll_pos = auto_scroll_pos + auto_scroll_speed; | |
} | |
// Starts everything when the user presses the start key. | |
function startExport(event) { | |
if (event.keyCode == 48) { | |
window.onkeypress = null; | |
startingVariables(); | |
getSongs(); | |
pageScroll(); | |
} | |
} | |
window.onkeypress = function(event){startExport(event)}; | |
/* Everything below is not my code */ | |
// Used to save the playlists in a file. | |
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ | |
var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/constructor/i.test(e.HTMLElement),f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(i){u(i)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,s){if(!s){t=p(t)}var v=this,w=t.type,m=w===d,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&a)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;i(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define([],function(){return saveAs})} |
Thanks for making this.
As @se-bastiaan pointed out it doesn't work on this link: https://play.google.com/music/listen?u=0#/all
For future reference this is the error:
TypeError: Cannot read property 'textContent' of null at scrollAction (<anonymous>:145:72) at pageScroll (<anonymous>:175:5) at c (https://play-music.gstatic.com/fe/5fe97bf3f03d8f910c21eadc88b3532e/listen.js:1169:261)
The fix by @se-bastiaan also works. Would you mind incorporating it into your script?
For some reason it does not work when I'm pressing "0" but calling
startExport({keyCode: 48})
in the console window works fine ;)
I don't know why but when pressing "0", it seems that no KeyboardEvent is passed to the startExport() method. When adding a console.log() to the startExport() method, I can see KeyboardEvents for other keys like "u". But "p" for example is opening the playlists menu in Google Music and does not output a log message. I think Google is grabbing and handling the "0" before Unportify can do.
Oh and by the way, MANY THANKS FOR YOUR EFFORT! ♥
Ctrl + 0 also works
@TheDegree0 thank you so much for putting this together, wanted to switch a few years ago but was dreading rebuilding some of these playlists.
@x3rAx thank you, that worked or me.
Thank you,
For some reason Ctrl + 0 worked but just pressing 0 didn't haha
Does it still work having some issues.
It worked flawlessly! Thanks man, you spared me a lot of time