mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
- expand docstrings - collapse some small/duplicative helper functions - simplify control flow
125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
# This file is part of beets.
|
|
# Copyright 2013, Verrus, <github.com/Verrus/beets-plugin-featInTitle>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
|
|
"""Moves "featured" artists to the title from the artist field.
|
|
"""
|
|
from beets.plugins import BeetsPlugin
|
|
from beets import ui
|
|
import re
|
|
|
|
|
|
def split_on_feat(artist):
|
|
"""Given an artist string, split the "main" artist from any artist
|
|
on the right-hand side of a string like "feat". Return the main
|
|
artist, which is always a string, and the featuring artist, which
|
|
may be a string or None if none is present.
|
|
"""
|
|
parts = re.split(
|
|
r'[fF]t\.|[fF]eaturing|[fF]eat\.|\b[wW]ith\b|&|vs\.|and',
|
|
artist,
|
|
1, # Only split on the first "feat".
|
|
)
|
|
if len(parts) == 1:
|
|
return parts[0], None
|
|
else:
|
|
return parts
|
|
|
|
|
|
def contains_feat(title):
|
|
"""Determine whether the title contains a "featured" marker.
|
|
"""
|
|
return bool(re.search(
|
|
r'[fF]t\.|[fF]eaturing|[fF]eat\.|\b[wW]ith\b|&',
|
|
title,
|
|
))
|
|
|
|
|
|
def update_metadata(track, albumartist, title, feat_part, sort_artist):
|
|
"""Choose how to add new artists to the title and write the new
|
|
metadata.
|
|
"""
|
|
print track.path
|
|
|
|
# In all cases, update the artist fields.
|
|
track.artist = albumartist
|
|
track.artist_sort, _ = split_on_feat(sort_artist) # Strip featured.
|
|
|
|
# If the title already contains a featured artist, leave it alone.
|
|
if contains_feat(title):
|
|
print "new artist field", albumartist.strip()
|
|
|
|
# Otherwise, add "feat. (artist)" to the title.
|
|
else:
|
|
# do replace title.
|
|
print "albumartist:", albumartist
|
|
print "title:", title
|
|
print "featured artist:", feat_part
|
|
track.title = title.strip() + " feat." + feat_part
|
|
|
|
track.write()
|
|
|
|
|
|
def ft_in_title(item):
|
|
"""Look for featured artists in the item's artist fields and move
|
|
them to the title.
|
|
"""
|
|
artist = item.artist.strip()
|
|
title = item.title.strip()
|
|
albumartist = item.albumartist.strip()
|
|
sort_artist = item.artist_sort.strip()
|
|
|
|
# Check whether there is a featured artist on this track and the
|
|
# artist field does not exactly match the album artist field. In
|
|
# that case, we attempt to move the featured artist to the title.
|
|
_, featured = split_on_feat(artist)
|
|
if featured and albumartist != artist:
|
|
feat_part = None
|
|
|
|
# Look for the album artist in the artist field. If it's not
|
|
# present, give up.
|
|
albumartist_split = artist.split(albumartist)
|
|
if len(albumartist_split) <= 1:
|
|
print 'album artist not present in artist; skipping:', item.path
|
|
|
|
# If the last element of the split (the right-hand side of the
|
|
# album artist) is nonempty, then it probably contains the
|
|
# featured artist.
|
|
elif albumartist_split[-1] != '':
|
|
# Extract the featured artist from the right-hand side.
|
|
_, feat_part = split_on_feat(albumartist_split[-1])
|
|
|
|
# Otherwise, if there's nothing on the right-hand side, look for a
|
|
# featuring artist on the left-hand side.
|
|
else:
|
|
lhs, rhs = split_on_feat(albumartist_split[0])
|
|
if rhs:
|
|
feat_part = lhs
|
|
|
|
# If we have a featuring artist, move it to the title.
|
|
if feat_part:
|
|
update_metadata(item, albumartist, title, feat_part, sort_artist)
|
|
else:
|
|
print 'found no featuring artists:', item.path
|
|
|
|
|
|
class FtInTitlePlugin(BeetsPlugin):
|
|
def commands(self):
|
|
cmd = ui.Subcommand('ftintitle',
|
|
help='move featured artists to the title field')
|
|
def func(lib, opts, args):
|
|
for item in lib.items():
|
|
ft_in_title(item)
|
|
print "Manual 'beet update' run is recommended. "
|
|
cmd.func = func
|
|
return [cmd]
|