mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
Merge branch 'master' into discogs_original_year
This commit is contained in:
commit
a840bc700b
28 changed files with 236 additions and 144 deletions
|
|
@ -40,6 +40,7 @@ def apply_item_metadata(item, track_info):
|
||||||
item.artist_credit = track_info.artist_credit
|
item.artist_credit = track_info.artist_credit
|
||||||
item.title = track_info.title
|
item.title = track_info.title
|
||||||
item.mb_trackid = track_info.track_id
|
item.mb_trackid = track_info.track_id
|
||||||
|
item.mb_releasetrackid = track_info.release_track_id
|
||||||
if track_info.artist_id:
|
if track_info.artist_id:
|
||||||
item.mb_artistid = track_info.artist_id
|
item.mb_artistid = track_info.artist_id
|
||||||
if track_info.data_source:
|
if track_info.data_source:
|
||||||
|
|
@ -129,6 +130,7 @@ def apply_metadata(album_info, mapping):
|
||||||
|
|
||||||
# MusicBrainz IDs.
|
# MusicBrainz IDs.
|
||||||
item.mb_trackid = track_info.track_id
|
item.mb_trackid = track_info.track_id
|
||||||
|
item.mb_releasetrackid = track_info.release_track_id
|
||||||
item.mb_albumid = album_info.album_id
|
item.mb_albumid = album_info.album_id
|
||||||
if track_info.artist_id:
|
if track_info.artist_id:
|
||||||
item.mb_artistid = track_info.artist_id
|
item.mb_artistid = track_info.artist_id
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,8 @@ class TrackInfo(object):
|
||||||
|
|
||||||
- ``title``: name of the track
|
- ``title``: name of the track
|
||||||
- ``track_id``: MusicBrainz ID; UUID fragment only
|
- ``track_id``: MusicBrainz ID; UUID fragment only
|
||||||
|
- ``release_track_id``: MusicBrainz ID respective to a track on a
|
||||||
|
particular release; UUID fragment only
|
||||||
- ``artist``: individual track artist name
|
- ``artist``: individual track artist name
|
||||||
- ``artist_id``
|
- ``artist_id``
|
||||||
- ``length``: float: duration of the track in seconds
|
- ``length``: float: duration of the track in seconds
|
||||||
|
|
@ -152,14 +154,15 @@ class TrackInfo(object):
|
||||||
may be None. The indices ``index``, ``medium``, and ``medium_index``
|
may be None. The indices ``index``, ``medium``, and ``medium_index``
|
||||||
are all 1-based.
|
are all 1-based.
|
||||||
"""
|
"""
|
||||||
def __init__(self, title, track_id, artist=None, artist_id=None,
|
def __init__(self, title, track_id, release_track_id=None, artist=None,
|
||||||
length=None, index=None, medium=None, medium_index=None,
|
artist_id=None, length=None, index=None, medium=None,
|
||||||
medium_total=None, artist_sort=None, disctitle=None,
|
medium_index=None, medium_total=None, artist_sort=None,
|
||||||
artist_credit=None, data_source=None, data_url=None,
|
disctitle=None, artist_credit=None, data_source=None,
|
||||||
media=None, lyricist=None, composer=None, composer_sort=None,
|
data_url=None, media=None, lyricist=None, composer=None,
|
||||||
arranger=None, track_alt=None):
|
composer_sort=None, arranger=None, track_alt=None):
|
||||||
self.title = title
|
self.title = title
|
||||||
self.track_id = track_id
|
self.track_id = track_id
|
||||||
|
self.release_track_id = release_track_id
|
||||||
self.artist = artist
|
self.artist = artist
|
||||||
self.artist_id = artist_id
|
self.artist_id = artist_id
|
||||||
self.length = length
|
self.length = length
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,10 @@ def album_info(release):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
all_tracks = medium['track-list']
|
all_tracks = medium['track-list']
|
||||||
|
if 'data-track-list' in medium:
|
||||||
|
all_tracks += medium['data-track-list']
|
||||||
|
track_count = len(all_tracks)
|
||||||
|
|
||||||
if 'pregap' in medium:
|
if 'pregap' in medium:
|
||||||
all_tracks.insert(0, medium['pregap'])
|
all_tracks.insert(0, medium['pregap'])
|
||||||
|
|
||||||
|
|
@ -302,8 +306,9 @@ def album_info(release):
|
||||||
index,
|
index,
|
||||||
int(medium['position']),
|
int(medium['position']),
|
||||||
int(track['position']),
|
int(track['position']),
|
||||||
len(medium['track-list']),
|
track_count,
|
||||||
)
|
)
|
||||||
|
ti.release_track_id = track['id']
|
||||||
ti.disctitle = disctitle
|
ti.disctitle = disctitle
|
||||||
ti.media = format
|
ti.media = format
|
||||||
ti.track_alt = track['number']
|
ti.track_alt = track['number']
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,7 @@ class Item(LibModel):
|
||||||
'mb_albumid': types.STRING,
|
'mb_albumid': types.STRING,
|
||||||
'mb_artistid': types.STRING,
|
'mb_artistid': types.STRING,
|
||||||
'mb_albumartistid': types.STRING,
|
'mb_albumartistid': types.STRING,
|
||||||
|
'mb_releasetrackid': types.STRING,
|
||||||
'albumtype': types.STRING,
|
'albumtype': types.STRING,
|
||||||
'label': types.STRING,
|
'label': types.STRING,
|
||||||
'acoustid_fingerprint': types.STRING,
|
'acoustid_fingerprint': types.STRING,
|
||||||
|
|
|
||||||
|
|
@ -1865,6 +1865,12 @@ class MediaFile(object):
|
||||||
StorageStyle('MUSICBRAINZ_TRACKID'),
|
StorageStyle('MUSICBRAINZ_TRACKID'),
|
||||||
ASFStorageStyle('MusicBrainz/Track Id'),
|
ASFStorageStyle('MusicBrainz/Track Id'),
|
||||||
)
|
)
|
||||||
|
mb_releasetrackid = MediaField(
|
||||||
|
MP3DescStorageStyle(u'MusicBrainz Release Track Id'),
|
||||||
|
MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Track Id'),
|
||||||
|
StorageStyle('MUSICBRAINZ_RELEASETRACKID'),
|
||||||
|
ASFStorageStyle('MusicBrainz/Release Track Id'),
|
||||||
|
)
|
||||||
mb_albumid = MediaField(
|
mb_albumid = MediaField(
|
||||||
MP3DescStorageStyle(u'MusicBrainz Album Id'),
|
MP3DescStorageStyle(u'MusicBrainz Album Id'),
|
||||||
MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'),
|
MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'),
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ def show_change(cur_artist, cur_album, match):
|
||||||
medium = track_info.disc
|
medium = track_info.disc
|
||||||
mediums = track_info.disctotal
|
mediums = track_info.disctotal
|
||||||
if config['per_disc_numbering']:
|
if config['per_disc_numbering']:
|
||||||
if mediums > 1:
|
if mediums and mediums > 1:
|
||||||
return u'{0}-{1}'.format(medium, medium_index)
|
return u'{0}-{1}'.format(medium, medium_index)
|
||||||
else:
|
else:
|
||||||
return six.text_type(medium_index or index)
|
return six.text_type(medium_index or index)
|
||||||
|
|
|
||||||
|
|
@ -498,9 +498,10 @@ class DiscogsPlugin(BeetsPlugin):
|
||||||
medium, medium_index, _ = self.get_track_index(track['position'])
|
medium, medium_index, _ = self.get_track_index(track['position'])
|
||||||
artist, artist_id = self.get_artist(track.get('artists', []))
|
artist, artist_id = self.get_artist(track.get('artists', []))
|
||||||
length = self.get_track_length(track['duration'])
|
length = self.get_track_length(track['duration'])
|
||||||
return TrackInfo(title, track_id, artist, artist_id, length, index,
|
return TrackInfo(title, track_id, artist=artist, artist_id=artist_id,
|
||||||
medium, medium_index, artist_sort=None,
|
length=length, index=index,
|
||||||
disctitle=None, artist_credit=None)
|
medium=medium, medium_index=medium_index,
|
||||||
|
artist_sort=None, disctitle=None, artist_credit=None)
|
||||||
|
|
||||||
def get_track_index(self, position):
|
def get_track_index(self, position):
|
||||||
"""Returns the medium, medium index and subtrack index for a discogs
|
"""Returns the medium, medium index and subtrack index for a discogs
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class KeyFinderPlugin(BeetsPlugin):
|
||||||
self.find_key(lib.items(ui.decargs(args)), write=ui.should_write())
|
self.find_key(lib.items(ui.decargs(args)), write=ui.should_write())
|
||||||
|
|
||||||
def imported(self, session, task):
|
def imported(self, session, task):
|
||||||
self.find_key(task.items)
|
self.find_key(task.imported_items())
|
||||||
|
|
||||||
def find_key(self, items, write=False):
|
def find_key(self, items, write=False):
|
||||||
overwrite = self.config['overwrite'].get(bool)
|
overwrite = self.config['overwrite'].get(bool)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ New features:
|
||||||
and tracklist positions. Track ids are stored in ``mb_trackid``. :bug:`#2336`
|
and tracklist positions. Track ids are stored in ``mb_trackid``. :bug:`#2336`
|
||||||
Thanks to :user:`dbogdanov`.
|
Thanks to :user:`dbogdanov`.
|
||||||
* :doc:`/plugins/discogs`: Fetch original year from master releases. :bug:`#1122`
|
* :doc:`/plugins/discogs`: Fetch original year from master releases. :bug:`#1122`
|
||||||
|
* As a first step to get :bug:`#406` implemented, beets now imports the
|
||||||
|
``musicbrainz_releasetrackid`` field into the library and tags media files
|
||||||
|
accordingly. Thanks to :user:`Rawrmonkeys`.
|
||||||
|
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
|
|
@ -104,6 +107,11 @@ Fixes:
|
||||||
to which a track belongs, not the total number of different mediums present
|
to which a track belongs, not the total number of different mediums present
|
||||||
on the release. :bug:`2887`
|
on the release. :bug:`2887`
|
||||||
Thanks to :user:`dbogdanov`.
|
Thanks to :user:`dbogdanov`.
|
||||||
|
* The importer now supports audio files contained in data tracks when they are
|
||||||
|
listed in MusicBrainz: the corresponding audio tracks are now merged into the
|
||||||
|
main track list. Thanks to :user:`jdetrey`. :bug:`1638`
|
||||||
|
* :doc:`/plugins/keyfinder`: Avoid a crash when trying to process unmatched
|
||||||
|
tracks. :bug:`2537`
|
||||||
|
|
||||||
|
|
||||||
For developers:
|
For developers:
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,7 @@ Audio information:
|
||||||
MusicBrainz and fingerprint information:
|
MusicBrainz and fingerprint information:
|
||||||
|
|
||||||
* mb_trackid
|
* mb_trackid
|
||||||
|
* mb_releasetrackid
|
||||||
* mb_albumid
|
* mb_albumid
|
||||||
* mb_artistid
|
* mb_artistid
|
||||||
* mb_albumartistid
|
* mb_albumartistid
|
||||||
|
|
|
||||||
181
extra/_beet
181
extra/_beet
|
|
@ -2,28 +2,29 @@
|
||||||
|
|
||||||
# zsh completion for beets music library manager and MusicBrainz tagger: http://beets.radbox.org/
|
# zsh completion for beets music library manager and MusicBrainz tagger: http://beets.radbox.org/
|
||||||
|
|
||||||
# NOTE: it will be very slow the first time you try to complete in a zsh shell (especially if you've enable many plugins)
|
# Cache will be updated if it is older than the beets database or binary.
|
||||||
# You can make it faster in future by creating a cached version:
|
# Need to set BEETS_LIBRARY to some preliminary value since it is used by the cache checking function.
|
||||||
# 1) perform a query completion with this file (_beet), e.g. do: beet list artist:"<TAB>
|
typeset -g BEETS_LIBRARY=~/.config/beets/library.db
|
||||||
# to create the completion function (takes a few seconds)
|
zstyle ":completion:${curcontext}:" cache-policy _beet_check_cache
|
||||||
# 2) save a copy of the completion function: which _beet > _beet_cached
|
_beet_check_cache () {
|
||||||
# 3) save a copy of the query completion function: which _beet_query > _beet_query_cached
|
[[ ! -a "${1}" ]] || [[ ! -a ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot =beet ]]
|
||||||
# 4) copy the contents of _beet_query_cached to the top of _beet_cached
|
}
|
||||||
# 5) copy and paste the _beet_field_values function from _beet to the top of _beet_cached
|
# Try to retrieve the cache, and find out if it needs to be updated
|
||||||
# 6) add the following line to the top of _beet_cached: #compdef beet
|
if ! _retrieve_cache beets || _cache_invalid beets; then
|
||||||
# 7) add the following line to the bottom of _beet_cached: _beet "$@"
|
local updatecache=1
|
||||||
# 8) save _beet_cached to your completions directory (e.g. /usr/share/zsh/functions/Completion)
|
# Location of database
|
||||||
# 9) add the following line to your .zshrc file: compdef _beet_cached beet
|
typeset -g BEETS_LIBRARY="$(beet config|grep library|cut -f 2 -d ' ')"
|
||||||
# You will need to repeat this proceedure each time you enable new plugins if you want them to complete properly.
|
# List of all fields
|
||||||
|
local -a fields
|
||||||
|
fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`)
|
||||||
|
fi
|
||||||
|
|
||||||
# useful: argument to _regex_arguments for matching any word
|
# useful: argument to _regex_arguments for matching any word
|
||||||
local matchany=/$'[^\0]##\0'/
|
local matchany=/$'[^\0]##\0'/
|
||||||
|
|
||||||
# Deal with completions for querying and modifying fields..
|
# Deal with completions for querying and modifying fields..
|
||||||
local fieldargs matchquery matchmodify
|
local fieldargs matchquery matchmodify
|
||||||
local -a fields
|
|
||||||
# get list of all fields
|
|
||||||
fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`)
|
|
||||||
# regexps for matching query and modify terms on the command line
|
# regexps for matching query and modify terms on the command line
|
||||||
matchquery=/"(${(j/|/)fields[@]})"$':[^\0]##\0'/
|
matchquery=/"(${(j/|/)fields[@]})"$':[^\0]##\0'/
|
||||||
matchmodify=/"(${(j/|/)fields[@]})"$'(=[^\0]##|!)\0'/
|
matchmodify=/"(${(j/|/)fields[@]})"$'(=[^\0]##|!)\0'/
|
||||||
|
|
@ -43,14 +44,17 @@ function _join_lines() {
|
||||||
function _beet_field_values()
|
function _beet_field_values()
|
||||||
{
|
{
|
||||||
local -a output fieldvals
|
local -a output fieldvals
|
||||||
local library="$(beet config|grep library|cut -f 2 -d ' ')"
|
local sqlcmd="select distinct $1 from items;"
|
||||||
output=$(sqlite3 ${~library} "select distinct $1 from items;")
|
|
||||||
case $1
|
case $1
|
||||||
in
|
in
|
||||||
lyrics)
|
lyrics)
|
||||||
fieldvals=
|
fieldvals=
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
if [[ "$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>&1)" =~ "no such column" ]]; then
|
||||||
|
sqlcmd="select distinct value from item_attributes where key=='$1' and value!='';"
|
||||||
|
fi
|
||||||
|
output="$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>/dev/null | sed -rn '/^-+$/,${{/^[- ]+$/n};p}')"
|
||||||
fieldvals=("${(f)output[@]}")
|
fieldvals=("${(f)output[@]}")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
@ -68,8 +72,7 @@ queryelem="_values -S : 'query field (add an extra : to match by regexp)' '::' $
|
||||||
# store call to _values function for completing modify terms (no need to complete field values)
|
# store call to _values function for completing modify terms (no need to complete field values)
|
||||||
modifyelem="_values -S = 'modify field (replace = with ! to remove field)' $(echo "'${^fields[@]}:: '")"
|
modifyelem="_values -S = 'modify field (replace = with ! to remove field)' $(echo "'${^fields[@]}:: '")"
|
||||||
# Create completion function for queries
|
# Create completion function for queries
|
||||||
_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \
|
_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \( "$matchquery" ":query:query string:$queryelem" \) \#
|
||||||
\( "$matchquery" ":query:query string:$queryelem" \) \#
|
|
||||||
# store regexps for completing lists of queries and modifications
|
# store regexps for completing lists of queries and modifications
|
||||||
local -a query modify
|
local -a query modify
|
||||||
query=( \( "$matchquery" ":query:query string:{_beet_query}" \) \( "$matchquery" ":query:query string:{_beet_query}" \) \# )
|
query=( \( "$matchquery" ":query:query string:{_beet_query}" \) \( "$matchquery" ":query:query string:{_beet_query}" \) \# )
|
||||||
|
|
@ -174,73 +177,76 @@ function _beet_subcmd_options()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Now build the arguments to _regex_arguments for each subcommand.
|
# Now build the arguments to _regex_arguments for each subcommand.
|
||||||
local -a options regex_words_subcmds regex_words_help
|
if [[ -n $updatecache ]]; then
|
||||||
local subcmd cmddesc
|
local -a options regex_words_subcmds regex_words_help
|
||||||
for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]}
|
local subcmd cmddesc
|
||||||
do
|
for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]}
|
||||||
subcmd="${i[(w)1]}"
|
do
|
||||||
# remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes
|
subcmd="${i[(w)1]}"
|
||||||
cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}"
|
# remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes
|
||||||
case $subcmd
|
cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}"
|
||||||
|
case $subcmd
|
||||||
in
|
in
|
||||||
(config)
|
(config)
|
||||||
_regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt"
|
_regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt"
|
||||||
options=("${reply[@]}")
|
options=("${reply[@]}")
|
||||||
;;
|
;;
|
||||||
(import)
|
(import)
|
||||||
_regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\
|
_regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\
|
||||||
"$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\
|
"$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\
|
||||||
"$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt"
|
"$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt"
|
||||||
options=( "${reply[@]}" \# "${files[@]}" \# )
|
options=( "${reply[@]}" \# "${files[@]}" \# )
|
||||||
;;
|
;;
|
||||||
(list)
|
(list)
|
||||||
_regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt"
|
_regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt"
|
||||||
options=( "$reply[@]" \# "${query[@]}" )
|
options=( "$reply[@]" \# "${query[@]}" )
|
||||||
;;
|
;;
|
||||||
(modify)
|
(modify)
|
||||||
_regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \
|
_regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \
|
||||||
"$noconfirmopt" "$formatopt"
|
"$noconfirmopt" "$formatopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" )
|
options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" )
|
||||||
;;
|
;;
|
||||||
(move)
|
(move)
|
||||||
_regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt"
|
_regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}")
|
options=( "${reply[@]}" \# "${query[@]}")
|
||||||
;;
|
;;
|
||||||
(remove)
|
(remove)
|
||||||
_regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt"
|
_regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}" )
|
options=( "${reply[@]}" \# "${query[@]}" )
|
||||||
;;
|
;;
|
||||||
(stats)
|
(stats)
|
||||||
_regex_words options "stats options" "$helpopt" "$exactopt"
|
_regex_words options "stats options" "$helpopt" "$exactopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}" )
|
options=( "${reply[@]}" \# "${query[@]}" )
|
||||||
;;
|
;;
|
||||||
(update)
|
(update)
|
||||||
_regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt"
|
_regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}" )
|
options=( "${reply[@]}" \# "${query[@]}" )
|
||||||
;;
|
;;
|
||||||
(write)
|
(write)
|
||||||
_regex_words options "write options" "$helpopt" "$pretendopt"
|
_regex_words options "write options" "$helpopt" "$pretendopt"
|
||||||
options=( "${reply[@]}" \# "${query[@]}" )
|
options=( "${reply[@]}" \# "${query[@]}" )
|
||||||
;;
|
;;
|
||||||
(fields|migrate|version)
|
(fields|migrate|version)
|
||||||
options=()
|
options=()
|
||||||
;;
|
;;
|
||||||
(help)
|
(help)
|
||||||
# The help subcommand is treated separately
|
# The help subcommand is treated separately
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
(*) # completions for plugin commands are generated using _beet_subcmd_options
|
(*) # completions for plugin commands are generated using _beet_subcmd_options
|
||||||
_beet_subcmd_options "$subcmd"
|
_beet_subcmd_options "$subcmd"
|
||||||
options=( \( "${reply[@]}" \# "${query[@]}" \) )
|
options=( \( "${reply[@]}" \# "${query[@]}" \) )
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
# Create variable for holding option for this subcommand, and assign to it (needs to have a unique name).
|
# Create variable for holding option for this subcommand, and assign to it (needs to have a unique name).
|
||||||
typeset -a opts_for_$subcmd
|
typeset -a opts_for_$subcmd
|
||||||
set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail).
|
set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail).
|
||||||
regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}")
|
regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}")
|
||||||
# Add to regex_words args for help subcommand
|
# Add to regex_words args for help subcommand
|
||||||
regex_words_help+=("$subcmd:$cmddesc")
|
regex_words_help+=("$subcmd:$cmddesc")
|
||||||
done
|
done
|
||||||
|
_store_cache beets regex_words_subcmds regex_words_help BEETS_LIBRARY fields
|
||||||
|
fi
|
||||||
|
|
||||||
local -a opts_for_help
|
local -a opts_for_help
|
||||||
_regex_words subcmds "subcommands" "${regex_words_help[@]}"
|
_regex_words subcmds "subcommands" "${regex_words_help[@]}"
|
||||||
|
|
@ -253,7 +259,7 @@ _regex_words options "global options" "$configopt" "$debugopt" "$libopt" "$helpo
|
||||||
globalopts=("${reply[@]}")
|
globalopts=("${reply[@]}")
|
||||||
|
|
||||||
# Create main completion function
|
# Create main completion function
|
||||||
#local -a subcmds
|
local -a subcmds
|
||||||
_regex_words subcmds "subcommands" "${regex_words_subcmds[@]}"
|
_regex_words subcmds "subcommands" "${regex_words_subcmds[@]}"
|
||||||
subcmds=("${reply[@]}")
|
subcmds=("${reply[@]}")
|
||||||
_regex_arguments _beet "$matchany" \( "${globalopts[@]}" \# \) "${subcmds[@]}"
|
_regex_arguments _beet "$matchany" \( "${globalopts[@]}" \# \) "${subcmds[@]}"
|
||||||
|
|
@ -267,3 +273,4 @@ _beet "$@"
|
||||||
# Local Variables:
|
# Local Variables:
|
||||||
# mode:shell-script
|
# mode:shell-script
|
||||||
# End:
|
# End:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ def item(lib=None):
|
||||||
mb_albumid='someID-2',
|
mb_albumid='someID-2',
|
||||||
mb_artistid='someID-3',
|
mb_artistid='someID-3',
|
||||||
mb_albumartistid='someID-4',
|
mb_albumartistid='someID-4',
|
||||||
|
mb_releasetrackid='someID-5',
|
||||||
album_id=None,
|
album_id=None,
|
||||||
mtime=12345,
|
mtime=12345,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -103,9 +103,9 @@ def _make_item(title, track, artist=u'some artist'):
|
||||||
|
|
||||||
def _make_trackinfo():
|
def _make_trackinfo():
|
||||||
return [
|
return [
|
||||||
TrackInfo(u'one', None, u'some artist', length=1, index=1),
|
TrackInfo(u'one', None, artist=u'some artist', length=1, index=1),
|
||||||
TrackInfo(u'two', None, u'some artist', length=1, index=2),
|
TrackInfo(u'two', None, artist=u'some artist', length=1, index=2),
|
||||||
TrackInfo(u'three', None, u'some artist', length=1, index=3),
|
TrackInfo(u'three', None, artist=u'some artist', length=1, index=3),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -827,15 +827,15 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil):
|
||||||
trackinfo.append(TrackInfo(
|
trackinfo.append(TrackInfo(
|
||||||
u'oneNew',
|
u'oneNew',
|
||||||
u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||||
u'artistOneNew',
|
artist=u'artistOneNew',
|
||||||
u'a05686fc-9db2-4c23-b99e-77f5db3e5282',
|
artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282',
|
||||||
index=1,
|
index=1,
|
||||||
))
|
))
|
||||||
trackinfo.append(TrackInfo(
|
trackinfo.append(TrackInfo(
|
||||||
u'twoNew',
|
u'twoNew',
|
||||||
u'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
u'40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||||
u'artistTwoNew',
|
artist=u'artistTwoNew',
|
||||||
u'80b3cf5e-18fe-4c59-98c7-e5bb87210710',
|
artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710',
|
||||||
index=2,
|
index=2,
|
||||||
))
|
))
|
||||||
self.info = AlbumInfo(
|
self.info = AlbumInfo(
|
||||||
|
|
|
||||||
|
|
@ -1819,6 +1819,7 @@ def mocked_get_release_by_id(id_, includes=[], release_status=[],
|
||||||
'id': id_,
|
'id': id_,
|
||||||
'medium-list': [{
|
'medium-list': [{
|
||||||
'track-list': [{
|
'track-list': [{
|
||||||
|
'id': 'baz',
|
||||||
'recording': {
|
'recording': {
|
||||||
'title': 'foo',
|
'title': 'foo',
|
||||||
'id': 'bar',
|
'id': 'bar',
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ import mock
|
||||||
|
|
||||||
class MBAlbumInfoTest(_common.TestCase):
|
class MBAlbumInfoTest(_common.TestCase):
|
||||||
def _make_release(self, date_str='2009', tracks=None, track_length=None,
|
def _make_release(self, date_str='2009', tracks=None, track_length=None,
|
||||||
track_artist=False, medium_format='FORMAT'):
|
track_artist=False, data_tracks=None,
|
||||||
|
medium_format='FORMAT'):
|
||||||
release = {
|
release = {
|
||||||
'title': 'ALBUM TITLE',
|
'title': 'ALBUM TITLE',
|
||||||
'id': 'ALBUM ID',
|
'id': 'ALBUM ID',
|
||||||
|
|
@ -62,12 +63,15 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
'country': 'COUNTRY',
|
'country': 'COUNTRY',
|
||||||
'status': 'STATUS',
|
'status': 'STATUS',
|
||||||
}
|
}
|
||||||
|
i = 0
|
||||||
|
track_list = []
|
||||||
if tracks:
|
if tracks:
|
||||||
track_list = []
|
for recording in tracks:
|
||||||
for i, recording in enumerate(tracks):
|
i += 1
|
||||||
track = {
|
track = {
|
||||||
|
'id': 'RELEASE TRACK ID %d' % i,
|
||||||
'recording': recording,
|
'recording': recording,
|
||||||
'position': i + 1,
|
'position': i,
|
||||||
'number': 'A1',
|
'number': 'A1',
|
||||||
}
|
}
|
||||||
if track_length:
|
if track_length:
|
||||||
|
|
@ -87,12 +91,24 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
track_list.append(track)
|
track_list.append(track)
|
||||||
release['medium-list'].append({
|
data_track_list = []
|
||||||
'position': '1',
|
if data_tracks:
|
||||||
'track-list': track_list,
|
for recording in data_tracks:
|
||||||
'format': medium_format,
|
i += 1
|
||||||
'title': 'MEDIUM TITLE',
|
data_track = {
|
||||||
})
|
'id': 'RELEASE TRACK ID %d' % i,
|
||||||
|
'recording': recording,
|
||||||
|
'position': i,
|
||||||
|
'number': 'A1',
|
||||||
|
}
|
||||||
|
data_track_list.append(data_track)
|
||||||
|
release['medium-list'].append({
|
||||||
|
'position': '1',
|
||||||
|
'track-list': track_list,
|
||||||
|
'data-track-list': data_track_list,
|
||||||
|
'format': medium_format,
|
||||||
|
'title': 'MEDIUM TITLE',
|
||||||
|
})
|
||||||
return release
|
return release
|
||||||
|
|
||||||
def _make_track(self, title, tr_id, duration, artist=False, video=False):
|
def _make_track(self, title, tr_id, duration, artist=False, video=False):
|
||||||
|
|
@ -183,6 +199,7 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||||
release = self._make_release(tracks=[tracks[0]])
|
release = self._make_release(tracks=[tracks[0]])
|
||||||
second_track_list = [{
|
second_track_list = [{
|
||||||
|
'id': 'RELEASE TRACK ID 2',
|
||||||
'recording': tracks[1],
|
'recording': tracks[1],
|
||||||
'position': '1',
|
'position': '1',
|
||||||
'number': 'A1',
|
'number': 'A1',
|
||||||
|
|
@ -354,6 +371,18 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||||
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||||
|
|
||||||
|
def test_no_skip_audio_data_tracks(self):
|
||||||
|
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||||
|
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||||
|
data_tracks = [self._make_track('TITLE AUDIO DATA', 'ID DATA TRACK',
|
||||||
|
100.0 * 1000.0)]
|
||||||
|
release = self._make_release(tracks=tracks, data_tracks=data_tracks)
|
||||||
|
d = mb.album_info(release)
|
||||||
|
self.assertEqual(len(d.tracks), 3)
|
||||||
|
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||||
|
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||||
|
self.assertEqual(d.tracks[2].title, 'TITLE AUDIO DATA')
|
||||||
|
|
||||||
def test_skip_video_tracks_by_default(self):
|
def test_skip_video_tracks_by_default(self):
|
||||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||||
self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0,
|
self._make_track('TITLE VIDEO', 'ID VIDEO', 100.0 * 1000.0,
|
||||||
|
|
@ -365,6 +394,17 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||||
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||||
|
|
||||||
|
def test_skip_video_data_tracks_by_default(self):
|
||||||
|
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||||
|
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||||
|
data_tracks = [self._make_track('TITLE VIDEO', 'ID VIDEO',
|
||||||
|
100.0 * 1000.0, False, True)]
|
||||||
|
release = self._make_release(tracks=tracks, data_tracks=data_tracks)
|
||||||
|
d = mb.album_info(release)
|
||||||
|
self.assertEqual(len(d.tracks), 2)
|
||||||
|
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||||
|
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||||
|
|
||||||
def test_no_skip_video_tracks_if_configured(self):
|
def test_no_skip_video_tracks_if_configured(self):
|
||||||
config['match']['ignore_video_tracks'] = False
|
config['match']['ignore_video_tracks'] = False
|
||||||
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||||
|
|
@ -378,6 +418,19 @@ class MBAlbumInfoTest(_common.TestCase):
|
||||||
self.assertEqual(d.tracks[1].title, 'TITLE VIDEO')
|
self.assertEqual(d.tracks[1].title, 'TITLE VIDEO')
|
||||||
self.assertEqual(d.tracks[2].title, 'TITLE TWO')
|
self.assertEqual(d.tracks[2].title, 'TITLE TWO')
|
||||||
|
|
||||||
|
def test_no_skip_video_data_tracks_if_configured(self):
|
||||||
|
config['match']['ignore_video_tracks'] = False
|
||||||
|
tracks = [self._make_track('TITLE ONE', 'ID ONE', 100.0 * 1000.0),
|
||||||
|
self._make_track('TITLE TWO', 'ID TWO', 200.0 * 1000.0)]
|
||||||
|
data_tracks = [self._make_track('TITLE VIDEO', 'ID VIDEO',
|
||||||
|
100.0 * 1000.0, False, True)]
|
||||||
|
release = self._make_release(tracks=tracks, data_tracks=data_tracks)
|
||||||
|
d = mb.album_info(release)
|
||||||
|
self.assertEqual(len(d.tracks), 3)
|
||||||
|
self.assertEqual(d.tracks[0].title, 'TITLE ONE')
|
||||||
|
self.assertEqual(d.tracks[1].title, 'TITLE TWO')
|
||||||
|
self.assertEqual(d.tracks[2].title, 'TITLE VIDEO')
|
||||||
|
|
||||||
|
|
||||||
class ParseIDTest(_common.TestCase):
|
class ParseIDTest(_common.TestCase):
|
||||||
def test_parse_id_correct(self):
|
def test_parse_id_correct(self):
|
||||||
|
|
@ -504,6 +557,7 @@ class MBLibraryTest(unittest.TestCase):
|
||||||
'id': mbid,
|
'id': mbid,
|
||||||
'medium-list': [{
|
'medium-list': [{
|
||||||
'track-list': [{
|
'track-list': [{
|
||||||
|
'id': 'baz',
|
||||||
'recording': {
|
'recording': {
|
||||||
'title': 'foo',
|
'title': 'foo',
|
||||||
'id': 'bar',
|
'id': 'bar',
|
||||||
|
|
|
||||||
|
|
@ -318,29 +318,30 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
|
||||||
"""
|
"""
|
||||||
|
|
||||||
full_initial_tags = {
|
full_initial_tags = {
|
||||||
'title': u'full',
|
'title': u'full',
|
||||||
'artist': u'the artist',
|
'artist': u'the artist',
|
||||||
'album': u'the album',
|
'album': u'the album',
|
||||||
'genre': u'the genre',
|
'genre': u'the genre',
|
||||||
'composer': u'the composer',
|
'composer': u'the composer',
|
||||||
'grouping': u'the grouping',
|
'grouping': u'the grouping',
|
||||||
'year': 2001,
|
'year': 2001,
|
||||||
'month': None,
|
'month': None,
|
||||||
'day': None,
|
'day': None,
|
||||||
'date': datetime.date(2001, 1, 1),
|
'date': datetime.date(2001, 1, 1),
|
||||||
'track': 2,
|
'track': 2,
|
||||||
'tracktotal': 3,
|
'tracktotal': 3,
|
||||||
'disc': 4,
|
'disc': 4,
|
||||||
'disctotal': 5,
|
'disctotal': 5,
|
||||||
'lyrics': u'the lyrics',
|
'lyrics': u'the lyrics',
|
||||||
'comments': u'the comments',
|
'comments': u'the comments',
|
||||||
'bpm': 6,
|
'bpm': 6,
|
||||||
'comp': True,
|
'comp': True,
|
||||||
'mb_trackid': '8b882575-08a5-4452-a7a7-cbb8a1531f9e',
|
'mb_trackid': '8b882575-08a5-4452-a7a7-cbb8a1531f9e',
|
||||||
'mb_albumid': '9e873859-8aa4-4790-b985-5a953e8ef628',
|
'mb_releasetrackid': 'c29f3a57-b439-46fd-a2e2-93776b1371e0',
|
||||||
'mb_artistid': '7cf0ea9d-86b9-4dad-ba9e-2355a64899ea',
|
'mb_albumid': '9e873859-8aa4-4790-b985-5a953e8ef628',
|
||||||
'art': None,
|
'mb_artistid': '7cf0ea9d-86b9-4dad-ba9e-2355a64899ea',
|
||||||
'label': u'the label',
|
'art': None,
|
||||||
|
'label': u'the label',
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_fields = [
|
tag_fields = [
|
||||||
|
|
@ -366,6 +367,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
|
||||||
'bpm',
|
'bpm',
|
||||||
'comp',
|
'comp',
|
||||||
'mb_trackid',
|
'mb_trackid',
|
||||||
|
'mb_releasetrackid',
|
||||||
'mb_albumid',
|
'mb_albumid',
|
||||||
'mb_artistid',
|
'mb_artistid',
|
||||||
'art',
|
'art',
|
||||||
|
|
@ -773,7 +775,7 @@ class MusepackTest(ReadWriteTestBase, unittest.TestCase):
|
||||||
extension = 'mpc'
|
extension = 'mpc'
|
||||||
audio_properties = {
|
audio_properties = {
|
||||||
'length': 1.0,
|
'length': 1.0,
|
||||||
'bitrate': 23458,
|
'bitrate': 24023,
|
||||||
'format': u'Musepack',
|
'format': u'Musepack',
|
||||||
'samplerate': 44100,
|
'samplerate': 44100,
|
||||||
'bitdepth': 0,
|
'bitdepth': 0,
|
||||||
|
|
@ -871,7 +873,7 @@ class ApeTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
|
||||||
extension = 'ape'
|
extension = 'ape'
|
||||||
audio_properties = {
|
audio_properties = {
|
||||||
'length': 1.0,
|
'length': 1.0,
|
||||||
'bitrate': 112040,
|
'bitrate': 112608,
|
||||||
'format': u'APE',
|
'format': u'APE',
|
||||||
'samplerate': 44100,
|
'samplerate': 44100,
|
||||||
'bitdepth': 16,
|
'bitdepth': 16,
|
||||||
|
|
@ -883,7 +885,7 @@ class WavpackTest(ReadWriteTestBase, unittest.TestCase):
|
||||||
extension = 'wv'
|
extension = 'wv'
|
||||||
audio_properties = {
|
audio_properties = {
|
||||||
'length': 1.0,
|
'length': 1.0,
|
||||||
'bitrate': 108744,
|
'bitrate': 109312,
|
||||||
'format': u'WavPack',
|
'format': u'WavPack',
|
||||||
'samplerate': 44100,
|
'samplerate': 44100,
|
||||||
'bitdepth': 0,
|
'bitdepth': 0,
|
||||||
|
|
@ -895,7 +897,7 @@ class OpusTest(ReadWriteTestBase, unittest.TestCase):
|
||||||
extension = 'opus'
|
extension = 'opus'
|
||||||
audio_properties = {
|
audio_properties = {
|
||||||
'length': 1.0,
|
'length': 1.0,
|
||||||
'bitrate': 57984,
|
'bitrate': 66792,
|
||||||
'format': u'Opus',
|
'format': u'Opus',
|
||||||
'samplerate': 48000,
|
'samplerate': 48000,
|
||||||
'bitdepth': 0,
|
'bitdepth': 0,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue