1# -*- coding: utf-8 -*- 2# This file is part of beets. 3# Copyright 2016, Adrian Sampson. 4# 5# Permission is hereby granted, free of charge, to any person obtaining 6# a copy of this software and associated documentation files (the 7# "Software"), to deal in the Software without restriction, including 8# without limitation the rights to use, copy, modify, merge, publish, 9# distribute, sublicense, and/or sell copies of the Software, and to 10# permit persons to whom the Software is furnished to do so, subject to 11# the following conditions: 12# 13# The above copyright notice and this permission notice shall be 14# included in all copies or substantial portions of the Software. 15 16"""Facilities for automatically determining files' correct metadata. 17""" 18 19from __future__ import division, absolute_import, print_function 20 21from beets import logging 22from beets import config 23 24# Parts of external interface. 25from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch # noqa 26from .match import tag_item, tag_album, Proposal # noqa 27from .match import Recommendation # noqa 28 29# Global logger. 30log = logging.getLogger('beets') 31 32 33# Additional utilities for the main interface. 34 35def apply_item_metadata(item, track_info): 36 """Set an item's metadata from its matched TrackInfo object. 37 """ 38 item.artist = track_info.artist 39 item.artist_sort = track_info.artist_sort 40 item.artist_credit = track_info.artist_credit 41 item.title = track_info.title 42 item.mb_trackid = track_info.track_id 43 item.mb_releasetrackid = track_info.release_track_id 44 if track_info.artist_id: 45 item.mb_artistid = track_info.artist_id 46 if track_info.data_source: 47 item.data_source = track_info.data_source 48 49 if track_info.lyricist is not None: 50 item.lyricist = track_info.lyricist 51 if track_info.composer is not None: 52 item.composer = track_info.composer 53 if track_info.composer_sort is not None: 54 item.composer_sort = track_info.composer_sort 55 if track_info.arranger is not None: 56 item.arranger = track_info.arranger 57 58 # At the moment, the other metadata is left intact (including album 59 # and track number). Perhaps these should be emptied? 60 61 62def apply_metadata(album_info, mapping): 63 """Set the items' metadata to match an AlbumInfo object using a 64 mapping from Items to TrackInfo objects. 65 """ 66 for item, track_info in mapping.items(): 67 # Artist or artist credit. 68 if config['artist_credit']: 69 item.artist = (track_info.artist_credit or 70 track_info.artist or 71 album_info.artist_credit or 72 album_info.artist) 73 item.albumartist = (album_info.artist_credit or 74 album_info.artist) 75 else: 76 item.artist = (track_info.artist or album_info.artist) 77 item.albumartist = album_info.artist 78 79 # Album. 80 item.album = album_info.album 81 82 # Artist sort and credit names. 83 item.artist_sort = track_info.artist_sort or album_info.artist_sort 84 item.artist_credit = (track_info.artist_credit or 85 album_info.artist_credit) 86 item.albumartist_sort = album_info.artist_sort 87 item.albumartist_credit = album_info.artist_credit 88 89 # Release date. 90 for prefix in '', 'original_': 91 if config['original_date'] and not prefix: 92 # Ignore specific release date. 93 continue 94 95 for suffix in 'year', 'month', 'day': 96 key = prefix + suffix 97 value = getattr(album_info, key) or 0 98 99 # If we don't even have a year, apply nothing. 100 if suffix == 'year' and not value: 101 break 102 103 # Otherwise, set the fetched value (or 0 for the month 104 # and day if not available). 105 item[key] = value 106 107 # If we're using original release date for both fields, 108 # also set item.year = info.original_year, etc. 109 if config['original_date']: 110 item[suffix] = value 111 112 # Title. 113 item.title = track_info.title 114 115 if config['per_disc_numbering']: 116 # We want to let the track number be zero, but if the medium index 117 # is not provided we need to fall back to the overall index. 118 if track_info.medium_index is not None: 119 item.track = track_info.medium_index 120 else: 121 item.track = track_info.index 122 item.tracktotal = track_info.medium_total or len(album_info.tracks) 123 else: 124 item.track = track_info.index 125 item.tracktotal = len(album_info.tracks) 126 127 # Disc and disc count. 128 item.disc = track_info.medium 129 item.disctotal = album_info.mediums 130 131 # MusicBrainz IDs. 132 item.mb_trackid = track_info.track_id 133 item.mb_releasetrackid = track_info.release_track_id 134 item.mb_albumid = album_info.album_id 135 if track_info.artist_id: 136 item.mb_artistid = track_info.artist_id 137 else: 138 item.mb_artistid = album_info.artist_id 139 item.mb_albumartistid = album_info.artist_id 140 item.mb_releasegroupid = album_info.releasegroup_id 141 142 # Compilation flag. 143 item.comp = album_info.va 144 145 # Track alt. 146 item.track_alt = track_info.track_alt 147 148 # Miscellaneous/nullable metadata. 149 misc_fields = { 150 'album': ( 151 'albumtype', 152 'label', 153 'asin', 154 'catalognum', 155 'script', 156 'language', 157 'country', 158 'albumstatus', 159 'albumdisambig', 160 'releasegroupdisambig', 161 'data_source', 162 ), 163 'track': ( 164 'disctitle', 165 'lyricist', 166 'media', 167 'composer', 168 'composer_sort', 169 'arranger', 170 ) 171 } 172 173 # Don't overwrite fields with empty values unless the 174 # field is explicitly allowed to be overwritten 175 for field in misc_fields['album']: 176 clobber = field in config['overwrite_null']['album'].as_str_seq() 177 value = getattr(album_info, field) 178 if value is None and not clobber: 179 continue 180 item[field] = value 181 182 for field in misc_fields['track']: 183 clobber = field in config['overwrite_null']['track'].as_str_seq() 184 value = getattr(track_info, field) 185 if value is None and not clobber: 186 continue 187 item[field] = value 188