Skip to content

Instantly share code, notes, and snippets.

@fxthomas

fxthomas/vgmdb2mb.py

Last active Nov 4, 2020
Embed
What would you like to do?
VGMDB to MusicBrainz import script (requires Python 3.6)
#!/usr/bin/python
# coding=utf-8
"""Python Script for bootstrapping a MusicBrainz release using a VGMDB album.
This script uses the unofficial VGMDB.info JSON API to prefill the MusicBrainz
"Add Release" form with data from VGMDB.
It is only meant as a first step to make adding a new MB release easier; please
check for missing/erroneous data and make sure the imported release follows the
MusicBrainz guidelines!
Because VGMDB has a lot of Japanese content, we import track/release titles from
this language before trying other languages, and try to guess values for some
fields (e.g. "Soundtrack" album types).
Documentation about the field format is found at:
https://musicbrainz.org/doc/Development/Release_Editor_Seeding
"""
import re
import sys
import json
import html
import argparse
import webbrowser
from urllib.request import urlopen
from tempfile import NamedTemporaryFile
from datetime import datetime
def vgmdb_get_album_url(album_id, format_="json"):
"""Return the VGMDB API URL for the given album ID"""
return "https://vgmdb.info/album/%d?format=%s" % (album_id, format_)
def vgmdb_get_album_data(album_url):
"""Retrieve data for a VGMDB album"""
return json.load(urlopen(album_url))
def write_musicbrainz_html_form(fd, album_data):
"""Write a local MusicBrainz import form containing album data"""
fd.write("""<!doctype html>""")
fd.write("""<meta charset="UTF-8">""")
fd.write("""<title>Add VGMDB album As Release...</title>""")
fd.write("""<form action="https://musicbrainz.org/release/add" method="post">""")
album_title = album_data['names'].get('ja')
album_title = album_title or next(iter(album_data["names"].values()))
fd.write(f"""<input type="hidden" name="name" value="{html.escape(album_title)}">""")
fd.write(f"""<input type="hidden" name="status" value="official">""")
if "soundtrack" in album_data["classification"].lower():
fd.write(f"""<input type="hidden" name="type" value="album">""")
fd.write(f"""<input type="hidden" name="type" value="soundtrack">""")
if "ja" in album_data['names'].keys():
fd.write(f"""<input type="hidden" name="language" value="jpn">""")
fd.write(f"""<input type="hidden" name="script" value="Jpan">""")
all_artists = []
composers = []
arrangers = []
performers = []
lyricists = []
for composer_data in album_data["composers"]:
composer_name = composer_data["names"].get("ja")
composer_name = composer_name or next(iter(composer_data["names"].values()))
if composer_name not in all_artists:
all_artists.append(composer_name)
if composer_name not in composers:
composers.append(composer_name)
for arranger_data in album_data["arrangers"]:
arranger_name = arranger_data["names"].get("ja")
arranger_name = arranger_name or next(iter(arranger_data["names"].values()))
if arranger_name not in all_artists:
all_artists.append(arranger_name)
if arranger_name not in arrangers:
arrangers.append(arranger_name)
for performer_data in album_data["performers"]:
performer_name = performer_data["names"].get("ja")
performer_name = performer_name or next(iter(performer_data["names"].values()))
if performer_name not in all_artists:
all_artists.append(performer_name)
if performer_name not in performers:
performers.append(performer_name)
for lyricist_data in album_data["lyricists"]:
lyricist_name = lyricist_data["names"].get("ja")
lyricist_name = lyricist_name or next(iter(lyricist_data["names"].values()))
if lyricist_name not in all_artists:
all_artists.append(lyricist_name)
if lyricist_name not in lyricists:
lyricists.append(lyricist_name)
artists = ["Various Artists"] if len(performers) >= 3 else all_artists
track_artists = performers or arrangers or composers or lyricists or all_artists
join_phrase = ", "
for artist_ix, artist_name in enumerate(artists):
fd.write(f"""<input type="hidden" name="artist_credit.names.{artist_ix}.artist.name" value="{html.escape(artist_name)}">""")
if artist_ix < len(artists)-1:
fd.write(f"""<input type="hidden" name="artist_credit.names.{artist_ix}.join_phrase" value="{html.escape(join_phrase)}">""")
release_date = datetime.strptime(album_data['release_date'], "%Y-%m-%d")
fd.write(f"""<input type="hidden" name="events.0.date.year" value="{release_date.year}">""")
fd.write(f"""<input type="hidden" name="events.0.date.month" value="{release_date.month}">""")
fd.write(f"""<input type="hidden" name="events.0.date.day" value="{release_date.day}">""")
if "ja" in album_data['names'].keys():
fd.write(f"""<input type="hidden" name="events.0.country" value="JP">""")
catalog_nr = album_data['catalog']
fd.write(f"""<input type="hidden" name="labels.0.catalog_number" value="{html.escape(catalog_nr)}">""")
vgmdb_link = album_data['vgmdb_link']
fd.write(f"""<input type="hidden" name="urls.0.url" value="{html.escape(vgmdb_link)}">""")
fd.write(f"""<input type="hidden" name="urls.0.link_type" value="86">""") # VGMDB
fd.write(f"""<input type="hidden" name="edit_note" value="Imported from {html.escape(vgmdb_link)}">""")
for disc_ix, disc_data in enumerate(album_data["discs"]):
if album_data["media_format"] == "CD":
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.format" value="CD">""")
for track_ix, track_data in enumerate(disc_data["tracks"]):
track_title = track_data["names"].get("Japanese")
track_title = track_title or next(iter(track_data["names"].values()))
if track_data["track_length"] and track_data["track_length"].lower() != "unknown":
track_length = datetime.strptime(track_data["track_length"], "%M:%S")
track_length = 1000 * (track_length.minute*60 + track_length.second)
else:
track_length = 0
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.name" value="{html.escape(track_title)}">""")
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.length" value="{track_length}">""")
for artist_ix, artist_name in enumerate(track_artists):
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.artist_credit.names.{artist_ix}.mbid" value="">""")
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.artist_credit.names.{artist_ix}.name" value="{html.escape(artist_name)}">""")
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.artist_credit.names.{artist_ix}.artist.name" value="{html.escape(artist_name)}">""")
fd.write(f"""<input type="hidden" name="mediums.{disc_ix}.track.{track_ix}.artist_credit.names.{artist_ix}.join_phrase" value=", ">""")
fd.write("""<input type="submit" value="Add Cluster As Release...">""")
fd.write("""</form>""")
fd.write("""<script>document.forms[0].submit()</script>""")
# Parse arguments
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("album_id_url", help="VGMDB album id or URL", type=str)
parser.add_argument("--show-api-page", "-s", help="Show API page instead of the MB form", action="store_true")
args = parser.parse_args()
# Parse album ID
album_id = None
m = re.match(r"(https?://)?vgmdb.net/album/(?P<album_id>\d+).*", args.album_id_url)
if m:
album_id = int(m.group("album_id"))
elif args.album_id_url.isdigit():
album_id = int(args.album_id_url)
else:
print("Invalid album ID or URL")
sys.exit(1)
# Retrieve album URL
if args.show_api_page:
album_url = vgmdb_get_album_url(album_id, format_="html")
print("Opening %s" % album_url)
webbrowser.open(album_url)
sys.exit(0)
# Retrieve album data, write and open MusicBrainz form
album_url = vgmdb_get_album_url(album_id)
album_data = vgmdb_get_album_data(album_url)
with NamedTemporaryFile(suffix=".html", encoding="utf-8", mode="w+", delete=False) as fd:
write_musicbrainz_html_form(fd, album_data)
print("Opening %s" % fd.name)
webbrowser.open(fd.name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.