|
|
|
@ -1,36 +1,49 @@ |
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
|
|
|
|
|
from gmusicapi import Mobileclient |
|
|
|
|
import Mobileclient from gmusicapi |
|
|
|
|
import argparse |
|
|
|
|
import sys |
|
|
|
|
import requests |
|
|
|
|
import json |
|
|
|
|
import urllib.parse |
|
|
|
|
|
|
|
|
|
creds_path = sys.path[0] + '/mobileclient.cred' |
|
|
|
|
|
|
|
|
|
creds_path=sys.path[0] + '/mobileclient.cred' |
|
|
|
|
|
|
|
|
|
def authenticate(api): |
|
|
|
|
creds = api.perform_oauth(storage_filepath=creds_path, open_browser=False) |
|
|
|
|
|
|
|
|
|
def uploadLibrary(mudbase_api, songs): |
|
|
|
|
def uploadLibrary(mudbase_api, mudbase_user, mudbase_password, songs): |
|
|
|
|
# First, attempt to login and start a session. |
|
|
|
|
s = requests.Session() |
|
|
|
|
response = s.post(mudbase_api + '/login?username=' |
|
|
|
|
+ urllib.parse.quote(mudbase_user) |
|
|
|
|
+ '&password=' |
|
|
|
|
+ urllib.parse.quote(mudbase_password)) |
|
|
|
|
|
|
|
|
|
if response.status_code != 200: |
|
|
|
|
print("Unable to log in to MuDBase API.") |
|
|
|
|
|
|
|
|
|
# Helpers |
|
|
|
|
def getArtistStoreIds(song): |
|
|
|
|
if 'artistId' in song: |
|
|
|
|
return [ song['artistId'][0] ] |
|
|
|
|
return []; |
|
|
|
|
return [song['artistId'][0]] |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
def getSongStoreIds(song): |
|
|
|
|
if 'storeId' in song: |
|
|
|
|
return [ song['storeId'] ] |
|
|
|
|
return [song['storeId']] |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
# Create GPM import tag |
|
|
|
|
gpmTagIdResponse = requests.post(mudbase_api + '/tag', data = { |
|
|
|
|
gpmTagIdResponse = s.post(mudbase_api + '/tag', data={ |
|
|
|
|
'name': 'GPM Import' |
|
|
|
|
}).json() |
|
|
|
|
gpmTagId = gpmTagIdResponse['id'] |
|
|
|
|
print(f"Created tag \"GPM Import\", response: {gpmTagIdResponse}") |
|
|
|
|
|
|
|
|
|
# Create the root genre tag |
|
|
|
|
genreRootResponse = requests.post(mudbase_api + '/tag', data = { |
|
|
|
|
genreRootResponse = s.post(mudbase_api + '/tag', data={ |
|
|
|
|
'name': 'Genre' |
|
|
|
|
}).json() |
|
|
|
|
genreRootTagId = genreRootResponse['id'] |
|
|
|
@ -47,75 +60,80 @@ def uploadLibrary(mudbase_api, songs): |
|
|
|
|
# Determine artist properties. |
|
|
|
|
artist = { |
|
|
|
|
'name': song['artist'], |
|
|
|
|
'storeLinks': [ 'https://play.google.com/music/m' + id for id in getArtistStoreIds(song) ], |
|
|
|
|
'tagIds': [ gpmTagId ] |
|
|
|
|
'storeLinks': ['https://play.google.com/music/m' + id for id in getArtistStoreIds(song)], |
|
|
|
|
'tagIds': [gpmTagId] |
|
|
|
|
} if 'artist' in song else None |
|
|
|
|
|
|
|
|
|
# Determine album properties. |
|
|
|
|
album = { |
|
|
|
|
'name': song['album'], |
|
|
|
|
'tagIds': [ gpmTagId ] |
|
|
|
|
'tagIds': [gpmTagId] |
|
|
|
|
} if 'album' in song else None |
|
|
|
|
|
|
|
|
|
# Determine genre properties. |
|
|
|
|
genre = { |
|
|
|
|
'name': song['genre'], |
|
|
|
|
'parentId': genreRootTagId |
|
|
|
|
'parentId': genreRootTagId |
|
|
|
|
} if 'genre' in song else None |
|
|
|
|
|
|
|
|
|
# Upload artist if not already done |
|
|
|
|
artistId = None |
|
|
|
|
if artist: |
|
|
|
|
for key,value in storedArtists.items(): |
|
|
|
|
for key, value in storedArtists.items(): |
|
|
|
|
if value == artist: |
|
|
|
|
artistId = key |
|
|
|
|
break |
|
|
|
|
if not artistId: |
|
|
|
|
response = requests.post(mudbase_api + '/artist', json = artist).json() |
|
|
|
|
response = s.post(mudbase_api + '/artist', json=artist).json() |
|
|
|
|
artistId = response['id'] |
|
|
|
|
print(f"Created artist \"{artist['name']}\", response: {response}") |
|
|
|
|
print( |
|
|
|
|
f"Created artist \"{artist['name']}\", response: {response}") |
|
|
|
|
storedArtists[artistId] = artist |
|
|
|
|
|
|
|
|
|
# Upload album if not already done |
|
|
|
|
albumId = None |
|
|
|
|
if album: |
|
|
|
|
for key,value in storedAlbums.items(): |
|
|
|
|
for key, value in storedAlbums.items(): |
|
|
|
|
if value == album: |
|
|
|
|
albumId = key |
|
|
|
|
break |
|
|
|
|
if not albumId: |
|
|
|
|
response = requests.post(mudbase_api + '/album', json = album).json() |
|
|
|
|
response = s.post(mudbase_api + '/album', json=album).json() |
|
|
|
|
albumId = response['id'] |
|
|
|
|
print(f"Created album \"{album['name']}\", response: {response}") |
|
|
|
|
print( |
|
|
|
|
f"Created album \"{album['name']}\", response: {response}") |
|
|
|
|
storedAlbums[albumId] = album |
|
|
|
|
|
|
|
|
|
# Upload genre if not already done |
|
|
|
|
genreTagId = None |
|
|
|
|
if genre: |
|
|
|
|
for key,value in storedGenreTags.items(): |
|
|
|
|
for key, value in storedGenreTags.items(): |
|
|
|
|
if value == genre: |
|
|
|
|
genreTagId = key |
|
|
|
|
break |
|
|
|
|
if not genreTagId: |
|
|
|
|
response = requests.post(mudbase_api + '/tag', json = genre).json() |
|
|
|
|
response = s.post(mudbase_api + '/tag', json=genre).json() |
|
|
|
|
genreTagId = response['id'] |
|
|
|
|
print(f"Created genre tag \"Genre / {genre['name']}\", response: {response}") |
|
|
|
|
print( |
|
|
|
|
f"Created genre tag \"Genre / {genre['name']}\", response: {response}") |
|
|
|
|
storedGenreTags[genreTagId] = genre |
|
|
|
|
|
|
|
|
|
# Upload the song itself |
|
|
|
|
tagIds = [ gpmTagId ] |
|
|
|
|
tagIds = [gpmTagId] |
|
|
|
|
if genreTagId: |
|
|
|
|
tagIds.append(genreTagId) |
|
|
|
|
_song = { |
|
|
|
|
'title': song['title'], |
|
|
|
|
'artistIds': [ artistId ] if artistId != None else [], |
|
|
|
|
'albumIds': [ albumId ] if albumId != None else [], |
|
|
|
|
'artistIds': [artistId] if artistId != None else [], |
|
|
|
|
'albumIds': [albumId] if albumId != None else [], |
|
|
|
|
'tagIds': tagIds, |
|
|
|
|
'storeLinks': [ 'https://play.google.com/music/m/' + id for id in getSongStoreIds(song) ], |
|
|
|
|
'storeLinks': ['https://play.google.com/music/m/' + id for id in getSongStoreIds(song)], |
|
|
|
|
} |
|
|
|
|
response = requests.post(mudbase_api + '/song', json = _song).json() |
|
|
|
|
print(f"Created song \"{song['title']}\" with artist ID {artistId}, album ID {albumId}, response: {response}") |
|
|
|
|
|
|
|
|
|
response = s.post(mudbase_api + '/song', json=_song).json() |
|
|
|
|
print( |
|
|
|
|
f"Created song \"{song['title']}\" with artist ID {artistId}, album ID {albumId}, response: {response}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def getData(api): |
|
|
|
|
return { |
|
|
|
|
"songs": api.get_all_songs(), |
|
|
|
@ -125,7 +143,7 @@ def getData(api): |
|
|
|
|
|
|
|
|
|
def getSongs(data): |
|
|
|
|
# Get songs from library |
|
|
|
|
songs = [] #data['songs'] |
|
|
|
|
songs = [] # data['songs'] |
|
|
|
|
|
|
|
|
|
# Append songs from playlists |
|
|
|
|
for playlist in data['playlists']: |
|
|
|
@ -135,16 +153,27 @@ def getSongs(data): |
|
|
|
|
|
|
|
|
|
# Uniquify by using a dict. After all, same song may appear in |
|
|
|
|
# multiple playlists. |
|
|
|
|
sI = lambda song: song['artist'] + '-' + song['title'] if 'artist' in song and 'title' in song else 'z' |
|
|
|
|
def sI(song): return song['artist'] + '-' + \ |
|
|
|
|
song['title'] if 'artist' in song and 'title' in song else 'z' |
|
|
|
|
return list(dict((sI(song), song) for song in songs).values()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
api = Mobileclient() |
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Import Google Music library into MudBase.") |
|
|
|
|
parser.add_argument('--authenticate', help="Generate credentials for authentication", action="store_true") |
|
|
|
|
parser.add_argument('--store-to', help="Store GPM library to JSON for later upload", action='store', dest='store_to') |
|
|
|
|
parser.add_argument('--load-from', help="Load GPM library from JSON for upload", action='store', dest='load_from') |
|
|
|
|
parser.add_argument('--mudbase_api', help="Address for the Mudbase back-end API to upload to", action='store', dest='mudbase_api') |
|
|
|
|
parser = argparse.ArgumentParser( |
|
|
|
|
description="Import Google Music library into MudBase.") |
|
|
|
|
parser.add_argument( |
|
|
|
|
'--authenticate', help="Generate credentials for authentication", action="store_true") |
|
|
|
|
parser.add_argument('--store-to', help="Store GPM library to JSON for later upload", |
|
|
|
|
action='store', dest='store_to') |
|
|
|
|
parser.add_argument('--load-from', help="Load GPM library from JSON for upload", |
|
|
|
|
action='store', dest='load_from') |
|
|
|
|
parser.add_argument('--mudbase_api', help="Address for the Mudbase back-end API to upload to", |
|
|
|
|
action='store', dest='mudbase_api') |
|
|
|
|
parser.add_argument('--mudbase_user', help="Username for the Mudbase API", |
|
|
|
|
action='store', dest="mudbase_user") |
|
|
|
|
parser.add_argument('--mudbase_password', help="Password for the Mudbase API", |
|
|
|
|
action='store', dest="mudbase_password") |
|
|
|
|
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
|
@ -155,7 +184,8 @@ data = None |
|
|
|
|
|
|
|
|
|
# Determine whether we need to log in to GPM and get songs |
|
|
|
|
if args.store_to or (not args.load_from and args.mudbase_api): |
|
|
|
|
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=creds_path) |
|
|
|
|
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, |
|
|
|
|
oauth_credentials=creds_path) |
|
|
|
|
data = getData(api) |
|
|
|
|
|
|
|
|
|
# Determine whether to save to a file |
|
|
|
@ -172,5 +202,7 @@ songs = getSongs(data) |
|
|
|
|
print(f"Found {len(songs)} songs.") |
|
|
|
|
|
|
|
|
|
if args.mudbase_api: |
|
|
|
|
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=creds_path) |
|
|
|
|
uploadLibrary(args.mudbase_api, songs) |
|
|
|
|
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, |
|
|
|
|
oauth_credentials=creds_path) |
|
|
|
|
uploadLibrary(args.mudbase_api, args.mudbase_user, |
|
|
|
|
args.mudbase_password, songs) |
|
|
|
|