parent
8fcc67022d
commit
ddb8d16d13
5 changed files with 6758 additions and 85787 deletions
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@ |
||||
venv |
||||
mobileclient.cred |
File diff suppressed because it is too large
Load Diff
@ -1,217 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
from gmusicapi import Mobileclient |
||||
import argparse |
||||
import sys |
||||
import requests |
||||
import json |
||||
import urllib.parse |
||||
|
||||
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, mudbase_user, mudbase_password, tracks): |
||||
# 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(track): |
||||
if 'artistId' in track: |
||||
return [track['artistId'][0]] |
||||
return [] |
||||
|
||||
def getTrackStoreIds(track): |
||||
if 'storeId' in track: |
||||
return [track['storeId']] |
||||
return [] |
||||
|
||||
# Create GPM import tag |
||||
gpmTagIdResponse = s.post(mudbase_api + '/tag', json={ |
||||
'name': 'GPM Import', |
||||
'mbApi_typename': 'tag', |
||||
'parentId': None, |
||||
}).json() |
||||
gpmTagId = gpmTagIdResponse['id'] |
||||
print(f"Created tag \"GPM Import\", response: {gpmTagIdResponse}") |
||||
|
||||
# Create the root genre tag |
||||
genreRootResponse = s.post(mudbase_api + '/tag', json={ |
||||
'name': 'Genre', |
||||
'mbApi_typename': 'tag', |
||||
'parentId': None, |
||||
}).json() |
||||
genreRootTagId = genreRootResponse['id'] |
||||
print(f"Created tag \"Genre\", response: {genreRootResponse}") |
||||
|
||||
# For keeping track what we have already created. |
||||
storedArtists = dict() |
||||
storedAlbums = dict() |
||||
storedGenreTags = dict() |
||||
|
||||
for track in tracks: |
||||
# TODO: check if these items already exist |
||||
|
||||
# Determine artist properties. |
||||
artist = { |
||||
'mbApi_typename': 'artist', |
||||
'name': track['artist'], |
||||
'storeLinks': ['https://play.google.com/music/m' + id for id in getArtistStoreIds(track)], |
||||
'tagIds': [gpmTagId] |
||||
} if 'artist' in track else None |
||||
|
||||
# Upload artist if not already done |
||||
artistId = None |
||||
if artist: |
||||
for key, value in storedArtists.items(): |
||||
if value == artist: |
||||
artistId = key |
||||
break |
||||
if not artistId: |
||||
response = s.post(mudbase_api + '/artist', json=artist).json() |
||||
artistId = response['id'] |
||||
print( |
||||
f"Created artist \"{artist['name']}\", response: {response}") |
||||
storedArtists[artistId] = artist |
||||
|
||||
# Determine album properties. |
||||
album = { |
||||
'mbApi_typename': 'album', |
||||
'name': track['album'], |
||||
'tagIds': [gpmTagId], |
||||
'artistIds': [artistId], |
||||
} if 'album' in track else None |
||||
|
||||
# Determine genre properties. |
||||
genre = { |
||||
'mbApi_typename': 'tag', |
||||
'name': track['genre'], |
||||
'parentId': genreRootTagId |
||||
} if 'genre' in track else None |
||||
|
||||
# Upload album if not already done |
||||
albumId = None |
||||
if album: |
||||
for key, value in storedAlbums.items(): |
||||
if value == album: |
||||
albumId = key |
||||
break |
||||
if not albumId: |
||||
response = s.post(mudbase_api + '/album', json=album).json() |
||||
albumId = response['id'] |
||||
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(): |
||||
if value == genre: |
||||
genreTagId = key |
||||
break |
||||
if not genreTagId: |
||||
response = s.post(mudbase_api + '/tag', json=genre).json() |
||||
genreTagId = response['id'] |
||||
print( |
||||
f"Created genre tag \"Genre / {genre['name']}\", response: {response}") |
||||
storedGenreTags[genreTagId] = genre |
||||
|
||||
# Upload the track itself |
||||
tagIds = [gpmTagId] |
||||
if genreTagId: |
||||
tagIds.append(genreTagId) |
||||
_track = { |
||||
'mbApi_typename': 'track', |
||||
'name': track['title'], |
||||
'artistIds': [artistId] if artistId != None else [], |
||||
'albumId': albumId if albumId else None, |
||||
'tagIds': tagIds, |
||||
'storeLinks': ['https://play.google.com/music/m/' + id for id in getTrackStoreIds(track)], |
||||
} |
||||
response = s.post(mudbase_api + '/track', json=_track).json() |
||||
print( |
||||
f"Created track \"{track['title']}\" with artist ID {artistId}, album ID {albumId}, response: {response}") |
||||
|
||||
|
||||
def getData(api): |
||||
return { |
||||
"tracks": api.get_all_tracks(), |
||||
"playlists": api.get_all_user_playlist_contents() |
||||
} |
||||
|
||||
|
||||
def getTracks(data): |
||||
# Get tracks from library |
||||
tracks = [] # data['tracks'] |
||||
|
||||
# Append tracks from playlists |
||||
for playlist in data['playlists']: |
||||
for track in playlist['tracks']: |
||||
if 'track' in track: |
||||
tracks.append(track['track']) |
||||
|
||||
# Uniquify by using a dict. After all, same track may appear in |
||||
# multiple playlists. |
||||
def sI(track): return track['artist'] + '-' + \ |
||||
track['title'] if 'artist' in track and 'title' in track else 'z' |
||||
return list(dict((sI(track), track) for track in tracks).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.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() |
||||
|
||||
if args.authenticate: |
||||
authenticate(api) |
||||
|
||||
data = None |
||||
|
||||
# Determine whether we need to log in to GPM and get tracks |
||||
if args.store_to or (not args.load_from and args.mudbase_api): |
||||
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, |
||||
oauth_credentials=creds_path) |
||||
data = getData(api) |
||||
|
||||
# Determine whether to save to a file |
||||
if args.store_to: |
||||
with open(args.store_to, 'w') as outfile: |
||||
json.dump(data, outfile, sort_keys=True, indent=2) |
||||
|
||||
# Determine whether to load from a file |
||||
if args.load_from: |
||||
with open(args.load_from, 'r') as f: |
||||
data = json.load(f) |
||||
|
||||
tracks = getTracks(data) |
||||
print(f"Found {len(tracks)} tracks.") |
||||
|
||||
if args.mudbase_api: |
||||
api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, |
||||
oauth_credentials=creds_path) |
||||
uploadLibrary(args.mudbase_api, args.mudbase_user, |
||||
args.mudbase_password, tracks) |
@ -1,3 +0,0 @@ |
||||
gmusicapi |
||||
argparse |
||||
requests |
Loading…
Reference in new issue