Skip to content

Commit

Permalink
Merge pull request #1 from metabrainz/1.0
Browse files Browse the repository at this point in the history
Update from original
  • Loading branch information
MetaTunes committed Aug 10, 2017
2 parents 7f14c15 + 3a666db commit fbe53ee
Show file tree
Hide file tree
Showing 5 changed files with 371 additions and 30 deletions.
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
language: python
python:
- "2.6"
- "2.7"
# - "3.2"
- "3.3"
- "3.4"
script: python test.py
38 changes: 12 additions & 26 deletions plugins/abbreviate_artistsort/abbreviate_artistsort.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

from __future__ import print_function
PLUGIN_NAME = u"Abbreviate artist-sort"
PLUGIN_AUTHOR = u"Sophist"
PLUGIN_DESCRIPTION = u'''Abbreviate Artist-Sort and Album-Artist-Sort Tags.
Expand Down Expand Up @@ -70,7 +69,6 @@
# Case c. If first word is same in sorted and unsorted, move words that match to new strings, then treat as b.
# Case d. Try to handle without abbreviating and get to next name which might not be foreign

_debug_level = 0
_abbreviate_tags = [
('albumartistsort', 'albumartist', '~albumartistsort_abbrev'),
('artistsort', 'artist', '~artistsort_abbrev'),
Expand All @@ -90,11 +88,9 @@ def abbreviate_artistsort(tagger, metadata, track, release):
unsorts = list(metadata.getall(unsortTag))
for i in range(0, min(len(sorts), len(unsorts))):
sort = sorts[i]
if _debug_level > 1:
print("%s: Trying to abbreviate '%s'." % (PLUGIN_NAME, sort))
log.debug("%s: Trying to abbreviate '%s'." % (PLUGIN_NAME, sort))
if sort in _abbreviate_cache:
if _debug_level > 3:
print(" Using abbreviation found in cache: '%s'." % (_abbreviate_cache[sort]))
log.debug(" Using abbreviation found in cache: '%s'." % (_abbreviate_cache[sort]))
sorts[i] = _abbreviate_cache[sort]
continue
unsort = unsorts[i]
Expand All @@ -104,17 +100,15 @@ def abbreviate_artistsort(tagger, metadata, track, release):
while len(sort) > 0 and len(unsort) > 0:

if not _split in sort:
if _debug_level > 3:
print(" Ending without separator '%s' - moving '%s'." % (_split, sort))
log.debug(" Ending without separator '%s' - moving '%s'." % (_split, sort))
new_sort += sort
new_unsort += unsort
sort = unsort = u""
continue

surname, rest = sort.split(_split, 1)
if rest == u"":
if _debug_level > 3:
print(" Ending with separator '%s' - moving '%s'." % (_split, surname))
log.debug(" Ending with separator '%s' - moving '%s'." % (_split, surname))
new_sort += sort
new_unsort += unsort
sort = unsort = u""
Expand All @@ -129,8 +123,7 @@ def abbreviate_artistsort(tagger, metadata, track, release):
temp = surname + _split
l = len(temp)
if unsort[:l] == temp:
if _debug_level > 3:
print(" No forename - moving '%s'." % (surname))
log.debug(" No forename - moving '%s'." % (surname))
new_sort += temp
new_unsort += temp
sort = sort[l:]
Expand All @@ -143,8 +136,7 @@ def abbreviate_artistsort(tagger, metadata, track, release):
if unsort.find(' ' + surname) == -1:
while surname.split(None, 1)[0] == unsort.split(None, 1)[0]:
x = unsort.split(None, 1)[0]
if _debug_level > 3:
print(" Moving matching word '%s'." % (x))
log.debug(" Moving matching word '%s'." % (x))
new_sort += x
new_unsort += x
surname = surname[len(x):]
Expand All @@ -165,8 +157,7 @@ def abbreviate_artistsort(tagger, metadata, track, release):
unsortTag,
unsort[i],
)
if _debug_level > 0:
print(" Could not match surname (%s) in remaining unsorted:" % (surname, unsort))
log.warning(" Could not match surname (%s) in remaining unsorted:" % (surname, unsort))
break

# Sorted: Surname, Forename(s)...
Expand All @@ -182,8 +173,7 @@ def abbreviate_artistsort(tagger, metadata, track, release):
unsortTag,
unsort[i],
)
if _debug_level > 0:
print(" Could not match forename (%s) for surname (%s) in remaining unsorted (%s):" % (forename, surname, unsort))
log.warning(" Could not match forename (%s) for surname (%s) in remaining unsorted (%s):" % (forename, surname, unsort))
break

inits = ' '.join([x[0] + '.' for x in forename.split()])
Expand Down Expand Up @@ -216,8 +206,7 @@ def abbreviate_artistsort(tagger, metadata, track, release):
inits,
sortTag,
)
if _debug_level > 2:
print("Abbreviated (%s, %s) to (%s, %s)." % (surname, forename, surname, inits))
log.debug("Abbreviated (%s, %s) to (%s, %s)." % (surname, forename, surname, inits))
else: # while loop ended without a break i.e. no errors
if unsorts[i] != new_unsort:
log.error(
Expand All @@ -228,13 +217,9 @@ def abbreviate_artistsort(tagger, metadata, track, release):
unsorts[i],
new_unsort,
)
if _debug_level > 0:
print()
print("Error: Unsorted text for %s has changed from '%s' to '%s'!" % (unsortTag, unsorts[i], new_unsort))
print()
log.warning("Error: Unsorted text for %s has changed from '%s' to '%s'!" % (unsortTag, unsorts[i], new_unsort))
_abbreviate_cache[sorts[i]] = new_sort
if _debug_level > 1:
print(" Abbreviated and cached (%s) as (%s)." % (sorts[i], new_sort))
log.debug(" Abbreviated and cached (%s) as (%s)." % (sorts[i], new_sort))
if sorts[i] != new_sort:
log.debug(_("%s: Abbreviated tag '%s' to '%s'."),
PLUGIN_NAME,
Expand All @@ -244,4 +229,5 @@ def abbreviate_artistsort(tagger, metadata, track, release):
sorts[i] = new_sort
metadata[sortTagNew] = sorts


register_track_metadata_processor(abbreviate_artistsort)
104 changes: 104 additions & 0 deletions plugins/abbreviate_artistsort2/abbreviate_artistsort2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

# This is the Abbreviate Artist plugin for MusicBrainz Picard.
# Copyright (C) 2013-2017 Sophist
#
# 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 2
# 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.

PLUGIN_NAME = u"Abbreviate Artist-Sort v2"
PLUGIN_AUTHOR = u"Sophist"
PLUGIN_DESCRIPTION = u'''
Abbreviate Artist-Sort and Album-Artist-Sort Tags.
e.g. "Vivaldi, Antonio" becomes "Vivaldi A"
This is particularly useful for classical albums that can have a long list of artists.
This version of the plugin differs from version 1 as it modifies the relevant metadata
in place (rather than copying it into new variables.
'''
PLUGIN_VERSION = "1.0"
PLUGIN_API_VERSIONS = ["1.4"]
PLUGIN_LICENSE = "GPL-2.0"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

import re, unicodedata

artist_tags = [
('artistsort', '~artists_sort', '~unabbrev_artistsort', '~unabbrev_artists_sort'),
('albumartistsort', '~albumartists_sort', '~unabbrev_albumartistsort', '~unabbrev_albumartists_sort'),
]

def abbreviate_name(artist):
if u"," not in artist:
return artist
surname, forenames = artist.split(u",", 1)
return surname + u" " + "".join([x[0] for x in re.split(ur"\W+", forenames.strip())])

def string_cleanup(string, locale="utf-8"):
if not string:
return u""
if not isinstance(string, unicode):
string = string.decode(locale)
# Replace with normalised unicode string
return unicodedata.normalize("NFKC", string)

def abbreviate_artist(artist, locale="utf-8"):
"""Title-case a string using a less destructive method than str.title."""
return abbreviate_name(string_cleanup(artist, locale))

assert "Bach JS" == abbreviate_artist("Bach, Johann Sebastian")
assert "The Beatles" == abbreviate_artist("The Beatles")

def abbreviate_artistsort(text, artists, abbrev_artists):
"""
Use the array of artists and the joined string
to identify artists to make title case
and the join strings to leave as-is.
"""
find = u"^(" + ur")(\s*\S+\s*)(".join((map(re.escape, map(string_cleanup,artists)))) + u")(.*$)"
replace = "".join([ur"%s\%d" % (a, x*2 + 2) for x, a in enumerate(abbrev_artists)])
result = re.sub(find, replace, string_cleanup(text), re.UNICODE)
return result

assert "Bach JS; London Symphony Orchestra" == abbreviate_artistsort(
"Bach, Johann Sebastian; London Symphony Orchestra",
["Bach, Johann Sebastian", "London Symphony Orchestra"],
["Bach JS", "London Symphony Orchestra"],
)

# Put this here so that above unit tests can run standalone before getting an import error
from picard import log
from picard.metadata import (
register_track_metadata_processor,
register_album_metadata_processor,
)

def abbrev_artistsort_metadata(tagger, metadata, release, track=None):
for artist_string, artists_list, original_artist, original_artists in artist_tags:
if artist_string in metadata and artists_list in metadata:
artists = metadata.getall(artists_list)
artist = metadata.getall(artist_string)
abbrev_artists = map(abbreviate_artist, artists)
abbrev_artist = [abbreviate_artistsort(x, artists, abbrev_artists) for x in artist]
if artists != abbrev_artists and artist != abbrev_artist:
log.debug("AbbrevArtistSort2: Abbreviated %s from %r to %r", artists_list, artists, abbrev_artists)
metadata[original_artists] = artists
metadata[artists_list] = abbrev_artists
log.debug("AbbrevArtistSort2: Abbreviated %s from %r to %r", artist_string, artist, abbrev_artist)
metadata[original_artist] = artist
metadata[artist_string] = abbrev_artist
elif artists != abbrev_artists or artist != abbrev_artist:
if artists != abbrev_artists:
log.warning("AbbrevArtistSort2: %s abbreviated, %s wasn't", artists_list, artist_string)
else:
log.warning("AbbrevArtistSort2: %s abbreviated, %s wasn't", artist_string, artists_list)

register_track_metadata_processor(abbrev_artistsort_metadata)
register_album_metadata_processor(abbrev_artistsort_metadata)
124 changes: 124 additions & 0 deletions plugins/albumartistextension/albumartistextension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
PLUGIN_NAME = 'AlbumArtist Extension'
PLUGIN_AUTHOR = 'Bob Swift (rdswift)'
PLUGIN_DESCRIPTION = '''
This plugin provides standardized, credited and sorted artist information
for the album artist. This is useful when your tagging or renaming scripts
require both the standardized artist name and the credited artist name, or
more detailed information about the album artists.
<br /><br />
The information is provided in the following variables:
<ul>
<li>_aaeStdAlbumArtists = The standardized version of the album artists.
<li>_aaeCredAlbumArtists = The credited version of the album artists.
<li>_aaeSortAlbumArtists = The sorted version of the album artists.
<li>_aaeStdPrimaryAlbumArtist = The standardized version of the first
(primary) album artist.
<li>_aaeCredPrimaryAlbumArtist = The credited version of the first (primary)
album artist.
<li>_aaeSortPrimaryAlbumArtist = The sorted version of the first (primary)
album artist.
<li>_aaeAlbumArtistCount = The number of artists comprising the album artist.
</ul>
PLEASE NOTE: Tagger scripts are required to make use of these hidden
variables.
'''

PLUGIN_VERSION = "0.5"
PLUGIN_API_VERSIONS = ["1.4"]
PLUGIN_LICENSE = "GPL-2.0 or later"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

from picard import config, log
from picard.metadata import register_album_metadata_processor
from picard.plugin import PluginPriority

class AlbumArtistStdName:

@staticmethod
def add_artist_std_name(album, album_metadata, releaseXmlNode):
albumid = releaseXmlNode.id
# Test for valid XML node for the release
if 'artist_credit' in releaseXmlNode.children:
# Initialize variables to default values
credArtist = ""
stdArtist = ""
sortArtist = ""
aCount = 0
# Get the current lists of _albumartists and _albumartists_sort
metaAlbumArtists = dict.get(album_metadata,"~albumartists")
metaAlbumArtists_Sort = dict.get(album_metadata,"~albumartists_sort")
# The 'name_credit' child should always be there.
# This check is to avoid a runtime error if it doesn't exist for some reason.
if 'name_credit' in releaseXmlNode.artist_credit[0].children:
for ncredit in releaseXmlNode.artist_credit[0].name_credit:
# Initialize temporary variables for each loop.
tempStdName = ""
tempCredName = ""
tempSortName = ""
tempPhrase = ""
# Check if there is a 'joinphrase' specified.
if 'joinphrase' in ncredit.attribs:
tempPhrase = ncredit.joinphrase
# Set the credit name from the AlbumArtist list if the
# 'Use standardized artist name' option is not checked
# in Picard, otherwise use the XML information.
if config.setting["standardize_artists"]:
# Check if there is a 'name' specified. This will be the
# credited name.
if 'name' in ncredit.children:
tempCredName = ncredit.name[0].text
else:
tempCredName = metaAlbumArtists[aCount]
# The 'artist' child should always be there.
# This check is to avoid a runtime error if it doesn't
# exist for some reason.
if 'artist' in ncredit.children:
# The 'name' child should always be there.
# This check is to avoid a runtime error if it
# doesn't exist for some reason.
if 'name' in ncredit.artist[0].children:
# Set the standardized name from the AlbumArtist
# list if the 'Use standardized artist name'
# option is checked in Picard, otherwise use the
# XML information.
tempStdName = metaAlbumArtists[aCount] if config.setting["standardize_artists"] else ncredit.artist[0].name[0].text
stdArtist += tempStdName + tempPhrase
tCredName = tempCredName if len(tempCredName) > 0 else tempStdName
credArtist += tCredName + tempPhrase
if aCount < 1:
album_metadata["~aaeStdPrimaryAlbumArtist"] = tempStdName
album_metadata["~aaeCredPrimaryAlbumArtist"] = tCredName
else:
log.error("%s: %r: Missing artist 'name' in XML contents: %s",
PLUGIN_NAME, albumid, releaseXmlNode)
# Get the artist sort name from the
# _albumartists_sort list
tempSortName = metaAlbumArtists_Sort[aCount]
sortArtist += tempSortName + tempPhrase
if aCount < 1:
album_metadata["~aaeSortPrimaryAlbumArtist"] = tempSortName
else:
log.error("%s: %r: Missing 'artist' in XML contents: %s",
PLUGIN_NAME, albumid, releaseXmlNode)
aCount += 1
else:
log.error("%s: %r: Missing 'name_credit' in XML contents: %s",
PLUGIN_NAME, albumid, releaseXmlNode)
if len(stdArtist) > 0:
album_metadata["~aaeStdAlbumArtists"] = stdArtist
if len(credArtist) > 0:
album_metadata["~aaeCredAlbumArtists"] = credArtist
if len(sortArtist) > 0:
album_metadata["~aaeSortAlbumArtists"] = sortArtist
if aCount > 0:
album_metadata["~aaeAlbumArtistCount"] = aCount
else:
log.error("%s: %r: Error reading XML contents: %s",
PLUGIN_NAME, albumid, releaseXmlNode)
return None

# Register the plugin to run at a LOW priority so that other plugins that
# modify the contents of the _albumartists and _albumartists_sort lists can
# complete their processing and this plugin is working with the latest
# updated data.
register_album_metadata_processor(AlbumArtistStdName().add_artist_std_name, priority=PluginPriority.LOW)

0 comments on commit fbe53ee

Please sign in to comment.