Google Play Music is being discontinued starting October 2020. The successor, Youtube Music, requires payment or the music will stop when you turn your screen off. And other disadvantages. So you want to migrate to m3u
playlist files again.
-
Export from Google Takeout by selecting only Google Play Music. The format of the playlists is a weird csv directory structure.
-
Set configuration options of this script.
-
python convert.py > out.txt
-
Congrats! You now have
- Playlists with absolute paths matched to your existing high-quality music files
- Playlists using relative paths for the same
- No more duplicate files in your library
-
(Optional: Have a look at my wiki pages to see how I make use of those files.)
git clone https://github.com/lucidBrot/migrate-from-google-play-music.git
The environment.yml was generated with conda. You should only need to install mutagen
(with pip or conda). If you are not using conda, be aware that I have used python 3.8
for development. (See this issue, it seems like 3.7
will work as well.)
conda install -c conda-forge mutagen
Then read the Options and Run it.
The following options can and/or should be set near the top of the file by modifying the script before you run it. I advise to have a look at all of them, but you must modify at least these:
- OUTPUT_PLAYLIST_DIR_RELATIVE and MAKE_PLAYLISTS_RELATIVE_TO_OUTPUT_PLAYLIST_DIR
- REDUCE_PLAYLIST_REDUNDANCIES and DELETE_REDUNDANT_FILES_IN_MUSIC_PATH
- PLAYLISTS_PATH
- MUSIC_PATH
- GPM_FALLBACK_TRACK_PATHS
Default False
. If True
, the script will perform some fuzzy matching that is likely to yield wrong results, but at least they are results and don't require manual intervention. In my case I did not require this.
Default True
. The "Thumbs up" playlist has a different format and hence must be handled differently. If False
, that playlist will be ignored.
Default output_playlists
relative to the current working dir. You can specify also an absolute path instead.
The playlist versions with absolute paths will be generated here.
Where the playlist files are to be stored. Their referenced music files will be relative to this directory.
Default ['@eaDir', os.path.basename(OUTPUT_PLAYLIST_DIR_RELATIVE)]
ignores synology indexing files and the relative playlist directory when collecting a list of all locally stored music files.
Default True
. If False
, no relative playlist files will be generated.
Default True
. If False
no absolute playlist files will be generated.
Default True
. Increases runtime by about 3 Minutes to compute hashes of all files in MUSIC_PATH
and only reference one of those with equal hashes. This means deduplication becomes possible.
If False
, the script will finish faster, but other options become unusable.
Default True
. Writes the found redundancies as a JSON object to a file in the OUTPUT_PLAYLIST_DIR. This file is not further used by the script, it's just for you.
Default True
. If True
, any files with the same hash will be deleted except for one each - the one which is used in the generated playlists thanks to REDUCE_PLAYLIST_REDUNDANCIES.
Should have no effect if said option is not set to True
.
If False
, nothing will be deleted
Recommend to set to a path. As a safety measure, files that would be deleted by virtue of DELETE_REDUNDANT_FILES_IN_MUSIC_PATH are instead moved to that path so you can manually review them before deletion.
My usage: MOVE_FILES_INSTEAD_OF_DELETION=os.path.normpath('N:\Temp\GPM_Deletion')
Where your Google Play Music Takeout export resides, so that their weird csv folder structure is there.
My usage:PLAYLISTS_PATH = os.path.normpath('N:\Files\Backups\GPM_export\Takeout\Google Play Music\Playlists')
You have local music files that you prefer over the ones exported from Google Play Music? Likely some FLACs and some MP3s that are not well-sorted and not all are tagged correctly? That's one of the main reasons why this script exists.
My usage: MUSIC_PATH = os.path.normpath('N:\Files\Musik')
If you do not have any such directory and just want to use the GPM exported mp3 files, set this to the GPM Tracks directory and deactivate COPY_FALLBACK_GPM_MUSIC.
Default True
. Copy files over to the music directory at MUSIC_PATH if and only if they are used in a playlist. If False
, no files will be copied over.
If True
, you also need to set COPY_FALLBACKS_TO_PATH
.
My usage:
COPY_FALLBACK_GPM_MUSIC=True
COPY_FALLBACKS_TO_PATH=os.path.normpath(os.path.join(MUSIC_PATH, "2020", "gpm-migration"))
Specify 0 or more fallback paths. Those will be searched if no match in the MUSIC_PATH was found for a song. This setting can be used even without COPY_FALLBACK_GPM_MUSIC enabled.
My usage:
# Path to where the export from GPM resides.
# Files are assumed to be lower quality
# Also add any other fallback paths here.
GPM_FALLBACK_TRACK_PATHS = [
os.path.normpath('N:\Files\Backups\GPM_export\Takeout\Google Play Music\Tracks'),
os.path.normpath('F:\PlayMusic'),
]
Default False
. If True
, any redundancy checks for REDUCE_PLAYLIST_REDUNDANCIES are only trusted if the files actually differ, not just their hashes. This will take longer, and is not tested. Feel free to create a PR if you had to fix something.
Set the options as outlined in the Options Section, then run it with
python convert.py > out.txt
The important or interactive messages will appear in stderr and hence the terminal, the rest is just logging. You can delete out.txt
after the run if you wish - it is never read.
This run took about 4 minutes on my machine in order to match 20 playlists to a local library of 4000 music files and deleting 200 redundant files. I had to manually find 30 files.
If the script prompts you in the terminal to provide information, you'll have to open the file _missing_matches.json
in the current working directory and insert the paths that should be used for those files. They may lie outside of MUSIC_PATH
and if COPY_FALLBACK_GPM_MUSIC
is set to True
(default) they will be copied over to the MUSIC_PATH
. Note that a json path needs to have backslashes escaped, so the lines may look something like this:
"SongInfo(title='Grey', artist='Meinhard', liked=True, album='', title_stripped='Grey')": "N:\\Files\\Musik\\2020\\april\\Meinhard Grey_LalU7ej8Kwk.mp3",
If a path is invalid or inexistent, the program will tell you. Just save the file, then press ENTER in the Terminal. When done, it will say Thanks!
.
For entering those paths manually, I've found "Everything Search" on Windows to be useful. I found the local files with it, copied the paths, and in the end I used Notepad++ to do a quick find-and-replace so that I have the correct number of backslashes in my paths.
If you just want a list of the Information from each GPM-exported "Playlist", in a single file for one playlist, then run
python -c "import convert; convert.read_gpm_playlist('/path-to/Google Play Music/Playlists/MyPlaylistDir/');"
If you want to have that information for each playlist, and saved to files, then run
python -c "import convert; convert.generate_songlists();"
Of course, relevant Options need to be set.
python convert.py -c "from convert import *; lmfi=debug_create_lmfi_sans_tags(); compute_redundant_files(lmfi);"
If DUMP_REDUNDANCIES_AS_JSON_TO_OUTPUT_PLAYLIST_DIR
is True
, this dumps to a file in the folder where the absolute path playlists would also be stored.