Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update from original #1

Merged
merged 25 commits into from
Aug 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0b71388
Use only py2 for testing
samj1912 Apr 16, 2017
0d318ba
New simpler version of Abbreviate Artist Sort
Sophist-UK Apr 11, 2017
eafb8a0
Create Smart Title Case plugin
Sophist-UK Apr 10, 2017
d2a133d
Handle leading brackets and quotes etc.
Sophist-UK Apr 11, 2017
0adf5e7
Fix glaring error and cleanup all unicode
Sophist-UK Apr 11, 2017
3f6f9a9
Clean up artists.
Sophist-UK Apr 11, 2017
f424c00
Title Case artists list for other plugins...
Sophist-UK Apr 11, 2017
baf8cfe
Correct introductory blurb inc copyright.
Sophist-UK Apr 11, 2017
9deec9d
Extra blank line
Sophist-UK Apr 11, 2017
ff0c429
Fix corner cases
Sophist-UK Apr 11, 2017
8b1bc30
Fix typos
Sophist-UK Apr 11, 2017
e112bb9
Add \u2010 to non-word-breaks
Sophist-UK Apr 19, 2017
566a1ad
Also add non-breaking hyphen
Sophist-UK Apr 19, 2017
ee047b2
Merge pull request #95 from Sophist-UK/patch-4
samj1912 Apr 27, 2017
0b36600
abbreviate_artistsort: Add enough conversion specifiers to a warning …
mineo Apr 17, 2017
2bd784f
abbreviate_artistsort: Get rid of print statements
mineo Apr 17, 2017
5a3fa26
Merge pull request #96 from Sophist-UK/patch-5
zas May 24, 2017
3910e5f
AlbumArtist Extension v0.3
rdswift Jun 8, 2017
cc63807
Corrected issue found during code review.
rdswift Jun 8, 2017
e15a97c
Version 0.4 update
rdswift Jun 8, 2017
1eb3e27
Remove unused variable noted during code review.
rdswift Jun 8, 2017
cfd370d
Update to v0.05
rdswift Jun 9, 2017
a998ec8
Correct error in plugin description
rdswift Jun 9, 2017
0672098
Merge pull request #106 from rdswift/1.0
zas Jul 12, 2017
3a666db
Do not build for py2.6
zas Aug 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)