def test_create_file_from_template(self): self.ogsrelid = "3083" # construct config with only default values tagger_config = TaggerConfig(os.path.join(parentdir, "test/empty.conf")) dummy_response = TestDummyResponse(self.ogsrelid) discogs_album = DummyDiscogsAlbum(dummy_response) self.album = discogs_album.map() taggerutils = TaggerUtils(self.source_dir, self.target_dir, self.tagger_config, self.album) create_file = os.path.join(self.target_dir, "info.nfo") assert taggerutils.create_file_from_template("/info.txt", create_file) assert os.path.exists(create_file) assert taggerutils.create_nfo(self.target_dir) # copy file to source directory and rename it self.copy_files_single_album(17) taggerutils = TaggerUtils(self.source_dir, self.target_dir, self.tagger_config, self.album) taggerutils._get_target_list() assert self.album.discs[0].tracks[0].new_file == "01-yonderboi-intro.flac" assert taggerutils.create_m3u(self.target_dir)
def test_create_file_from_template(self): self.ogsrelid = "3083" # construct config with only default values tagger_config = TaggerConfig(os.path.join(parentdir, "test/empty.conf")) dummy_response = TestDummyResponse(self.ogsrelid) discogs_album = DummyDiscogsAlbum(dummy_response) self.album = discogs_album.map() taggerutils = TaggerUtils(self.source_dir, self.target_dir, self.tagger_config, self.album) create_file = os.path.join(self.target_dir, "info.nfo") assert taggerutils.create_file_from_template("/info.txt", create_file) assert os.path.exists(create_file) assert taggerutils.create_nfo(self.target_dir) # copy file to source directory and rename it self.copy_files_single_album(17) taggerutils = TaggerUtils(self.source_dir, self.target_dir, self.tagger_config, self.album) taggerutils._get_target_list() assert self.album.discs[0].tracks[ 0].new_file == "01-yonderboi-intro.flac" assert taggerutils.create_m3u(self.target_dir)
def tagger(conf, destination, releaseid, source): _log = init_logging(conf) if not destination: destination = source cfg = TaggerConfig(source, destination, conf) if cfg.id_tag in cfg.release_tags: releaseid = cfg.release_tags[cfg.id_tag].strip() if releaseid: release_id = releaseid if not releaseid: click.echo('Please specify the discogs.com releaseid ("-r")') sys.exit(1) _log.info('Attempting to tag files from target destination={0}'.format(destination)) discogs_release = DiscogsAlbum(DiscogsWrapper().discogs, release_id, cfg.split_artists, cfg.split_genres_and_styles) release = TaggerUtils(discogs_release, cfg) # ensure we were able to map the release appropriately. if not release.tag_map: _log.fatal("Unable to map available audio files to the number of tracks in the Discogs release '{0}'. Exiting".format( release_id)) sys.exit(1) artist = cfg.split_artists.join(release.album.artists) artist = release.album.clean_name(artist) _log.info("Tagging album '{0} - {1}'".format(artist, release.album.title)) dest_dir_name = release.dest_dir_name if os.path.exists(dest_dir_name): _log.fatal('Destination directory already exists. directory={0}. Aborting operation'.format(dest_dir_name)) sys.exit(1) else: _log.info("Creating destination directory '{0}'".format(dest_dir_name)) mkdir_p(dest_dir_name) _log.info("Downloading and storing images") release.album.get_images(dest_dir_name, cfg.images_format, cfg.first_image_name) disc_names = dict() folder_names = dict() if release.album.disctotal > 1 and cfg.split_discs_folder: _log.debug("Creating disc structure") for i in range(1, release.album.disctotal + 1): folder_name = "%s%.d" % (release.album_folder_name, i) disc_dir_name = os.path.join(dest_dir_name, folder_name) mkdir_p(disc_dir_name) # This is duplicate, remove one of the following statements disc_names[i] = disc_dir_name folder_names[i] = folder_name else: folder_names[1] = "" for track in release.tag_map: # copy old file into new location if release.album.disctotal > 1 and cfg.split_discs_folder: target_folder = disc_names[int(track['discnumber'])] else: target_folder = dest_dir_name _log.debug("Source file {0}".format(os.path.join(source, track['orig_file']))) _log.info("Writing file {0}".format(os.path.join(target_folder, track['new_file']))) _log.debug("metadata -> {0:2d} {1} - {2}".format(track['tracknumber'], track['artist'], track['title'])) _log.debug("----------> {0}".format(track['new_file'])) shutil.copyfile(track['orig_file'], os.path.join(target_folder, track['new_file'])) # load metadata information metadata = MediaFile(os.path.join(target_folder, track['new_file'])) # read already existing (and still wanted) properties keep_tags = {} if cfg.keep_tags: for name in cfg.keep_tags.split(","): try: getattr(metadata, name) except AttributeError: _log.warn('Unable to keep_tag. tag={0}'.format(name)) continue keep_tags[name] = getattr(metadata, name) # remove current metadata metadata.delete() # set album metadata metadata.album = release.album.title if cfg.split_discs_folder and release.album.disctotal > 1: # the fileext should be stored on the album/track as well fileext = os.path.splitext(track['orig_file'])[1] disc_title_extension = release._value_from_tag_format(cfg.split_discs_extension, track['tracknumber'], track['position'] - 1, fileext) metadata.album = "{0}{1}".format(metadata.album, disc_title_extension) metadata.composer = artist metadata.albumartist = artist metadata.albumartist_sort = release.album.sort_artist metadata.label = release.album.label metadata.year = release.album.year metadata.country = release.album.country metadata.url = release.album.url # add styles to the grouping tag (right now, we can just use one) metadata.grouping = release.album.styles # adding two as there is no standard. discogstagger pre v1 # used (TXXX desc="Catalog #") # mediafile uses TXXX desc="CATALOGNUMBER" metadata.catalognum = release.album.catno metadata.catalognumber = release.album.catno # use the correct genre field, on config use the first style genre = release.album.genres if cfg.use_style: genre = release.album.style metadata.genre = genre metadata.discogs_id = release_id if release.album.disctotal and release.album.disctotal > 1 and track['discnumber']: _log.debug("writing disctotal and discnumber") metadata.disc = track['discnumber'] metadata.disctotal = release.album.disctotal if release.album.is_compilation: metadata.comp = True metadata.comments = release.album.note # encoder if cfg.encoder_tag is not None: metadata.encoder = cfg.encoder_tag # if track.discsubtotal: # metadata.discsubtotal = track.discsubtotal # set track metadata metadata.title = track['title'] metadata.artist = track['artist'] metadata.artist_sort = track['sortartist'] metadata.track = track['tracknumber'] # the following value will be wrong, if the disc has a name or is a multi # disc release --> fix it metadata.tracktotal = release.album.tracktotal_on_disc(track['discnumber']) # it does not make sense to store this in the "common" configuration, but only in the # id.txt. we use a special naming convention --> most probably we should reuse the # configuration parser for this one as well, no? for name, value in list(cfg.release_tags.items()): if name.startswith("tag:"): name = name.split(":") name = name[1] setattr(metadata, name, value) first_image_name = cfg.first_image_name # this should be done in a cleaner way to avoid multiple images in different # folders (use the dest_dir again....) if cfg.embed_coverart and os.path.exists(os.path.join(dest_dir_name, first_image_name)): imgdata = open(os.path.join(dest_dir_name, first_image_name), 'rb').read() imgtype = imghdr.what(None, imgdata) if imgtype in ("jpeg", "png"): _log.debug("Embedding album art.") metadata.art = imgdata if keep_tags is not None: for name in keep_tags: setattr(metadata, name, keep_tags[name]) metadata.save() # start supplementary actions if cfg.write_nfo: _log.info("Generating .nfo file") release.create_nfo() # adopt for multi disc support if cfg.write_m3u: _log.info("Generating .m3u file") release.create_m3u(folder_names) # copy "other files" on request if cfg.copy_other_files and len(release.copy_files) > 0: _log.info("copying files from source directory") copy_files = release.copy_files dir_list = os.listdir(source) _log.debug("start_dir: {0}".format(source)) _log.debug("dir list: {0}".format(dir_list)) file_list = [os.path.join(source, x) for x in dir_list if not x.lower().endswith(TaggerUtils.FILE_TYPE) and os.path.isfile(os.path.join(source, x))] copy_files.extend(file_list) for fname in copy_files: if not fname.endswith(".m3u"): _log.debug("source: {0}".format(fname)) _log.debug("target: {0}".format(os.path.join(dest_dir_name, os.path.basename(fname)))) shutil.copyfile(fname, os.path.join(dest_dir_name, os.path.basename(fname))) # remove source directory, if configured as such. if not cfg.keep_original: _log.info("Deleting source directory '{0}'".format(source)) shutil.rmtree(source) _log.info("Tagging complete.")
fileHandler.get_images(connector) logger.debug("Embedding Albumart") fileHandler.embed_coverart_album() if options.replaygain: logger.debug("Add ReplayGain tags (if necessary)") fileHandler.add_replay_gain_tags() # !TODO make this more generic to use different templates and files, # furthermore adopt to reflect multi-disc-albums logger.debug("Generate m3u") taggerUtils.create_m3u(album.target_dir) logger.debug("Generate nfo") taggerUtils.create_nfo(album.target_dir) fileHandler.create_done_file() except Exception as ex: if releaseid: msg = "Error during tagging ({0}), {1}: {2}".format(releaseid, source_dir, ex) else: msg = "Error during tagging (no relid) {0}: {1}".format(source_dir, ex) logger.error(msg) discs_with_errors.append(msg) continue # !TODO - make this a check during the taggerutils run # ensure we were able to map the release appropriately. #if not release.tag_map: # logger.error("Unable to match file list to discogs release '%s'" %
fileHandler.get_images(connector) logger.debug("Embedding Albumart") fileHandler.embed_coverart_album() if options.replaygain: logger.debug("Add ReplayGain tags (if necessary)") fileHandler.add_replay_gain_tags() # !TODO make this more generic to use different templates and files, # furthermore adopt to reflect multi-disc-albums logger.debug("Generate m3u") taggerUtils.create_m3u(album.target_dir) logger.debug("Generate nfo") taggerUtils.create_nfo(album.target_dir) fileHandler.create_done_file() except Exception as ex: if releaseid: msg = "Error during tagging ({0}), {1}: {2}".format( releaseid, source_dir, ex) else: msg = "Error during tagging (no relid) {0}: {1}".format( source_dir, ex) logger.error(msg) discs_with_errors.append(msg) continue # !TODO - make this a check during the taggerutils run # ensure we were able to map the release appropriately.
def processSourceDirs(source_dirs, tagger_config): # initialize connection (could be a problem if using multiple sources...) discogs_connector = DiscogsConnector(tagger_config) local_discogs_connector = LocalDiscogsConnector(discogs_connector) # try to re-use search, may be useful if working with several releases by the same artist discogsSearch = DiscogsSearch(tagger_config) logger.info("start tagging") discs_with_errors = [] converted_discs = 0 for source_dir in source_dirs: releaseid = None release = None connector = None try: done_file = tagger_config.get("details", "done_file") done_file_path = os.path.join(source_dir, done_file) if os.path.exists(done_file_path) and not options.forceUpdate: logger.warn( 'Do not read {}, because {} exists and forceUpdate is false' .format(source_dir, done_file)) continue # reread config to make sure, that the album specific options are reset for each # album tagger_config = TaggerConfig(options.conffile) if options.releaseid is not None: releaseid = options.releaseid else: releaseid = file_utils.read_id_file(source_dir, id_file, options) if not releaseid: searchParams = discogsSearch.getSearchParams(source_dir) # release = discogsSearch.search_discogs(searchParams) release = discogsSearch.search_discogs() # reuse the Discogs Release class, it saves re-fetching later if release is not None and type(release).__name__ in ( 'Release', 'Version'): releaseid = release.id connector = discogs_connector if not releaseid: logger.warn('No releaseid for {}'.format(source_dir)) continue # if not releaseid: # p.error("Please specify the discogs.com releaseid ('-r')") logger.info('Found release ID: {} for source dir: {}'.format( releaseid, source_dir)) # read destination directory # !TODO if both are the same, we are not copying anything, # this should be "configurable" if not options.destdir: destdir = source_dir else: destdir = options.destdir logger.debug('destdir set to {}'.format(options.destdir)) logger.info('Using destination directory: {}'.format(destdir)) logger.debug("starting tagging...") if releaseid is not None and release is None: #! TODO this is dirty, refactor it to be able to reuse it for later enhancements if tagger_config.get("source", "name") == "local": release = local_discogs_connector.fetch_release( releaseid, source_dir) connector = local_discogs_connector else: release = discogs_connector.fetch_release(releaseid) connector = discogs_connector discogs_album = DiscogsAlbum(release) try: album = discogs_album.map() except AlbumError as ae: msg = "Error during mapping ({0}), {1}: {2}".format( releaseid, source_dir, ae) logger.error(msg) discs_with_errors.append(msg) continue logger.info('Tagging album "{} - {}"'.format( album.artist, album.title)) tagHandler = TagHandler(album, tagger_config) taggerUtils = TaggerUtils(source_dir, destdir, tagger_config, album) fileHandler = FileHandler(album, tagger_config) try: taggerUtils._get_target_list() except TaggerError as te: msg = "Error during Tagging ({0}), {1}: {2}".format( releaseid, source_dir, te) logger.error(msg) discs_with_errors.append(msg) continue tagHandler.tag_album() taggerUtils.gather_addional_properties() # reset the target directory now that we have discogs metadata and # filedata - otherwise this is declared too early in the process album.target_dir = taggerUtils.dest_dir_name fileHandler.copy_files() logger.debug("Tagging files") # Do replaygain analysis before copying other files, the directory # contents are cleaner, less prone to mistakes if options.replaygain: logger.debug("Add ReplayGain tags (if requested)") fileHandler.add_replay_gain_tags() logger.debug("Copy other interesting files (on request)") fileHandler.copy_other_files() logger.debug("Downloading and storing images") fileHandler.get_images(connector) logger.debug("Embedding Albumart") fileHandler.embed_coverart_album() # !TODO make this more generic to use different templates and files, # furthermore adopt to reflect multi-disc-albums logger.debug("Generate m3u") taggerUtils.create_m3u(album.target_dir) logger.debug("Generate nfo") taggerUtils.create_nfo(album.target_dir) fileHandler.create_done_file() except Exception as ex: if releaseid: msg = "Error during tagging ({0}), {1}: {2}".format( releaseid, source_dir, ex) else: msg = "Error during tagging (no relid) {0}: {1}".format( source_dir, ex) logger.error(msg) discs_with_errors.append(msg) continue # !TODO - make this a check during the taggerutils run # ensure we were able to map the release appropriately. #if not release.tag_map: # logger.error("Unable to match file list to discogs release '%s'" % # releaseid) # sys.exit() converted_discs = converted_discs + 1 logger.info("Converted %d/%d" % (converted_discs, len(source_dirs))) logger.info("Tagging complete.") logger.info("converted successful: %d" % converted_discs) logger.info("converted with Errors %d" % len(discs_with_errors)) logger.info("releases touched: %s" % len(source_dirs)) if discs_with_errors: logger.error("The following discs could not get converted.") for msg in discs_with_errors: logger.error(msg)
def tagger(conf, destination, releaseid, source): _log = init_logging(conf) if not destination: destination = source cfg = TaggerConfig(source, destination, conf) if cfg.id_tag in cfg.release_tags: releaseid = cfg.release_tags[cfg.id_tag].strip() if releaseid: release_id = releaseid if not releaseid: click.echo('Please specify the discogs.com releaseid ("-r")') sys.exit(1) _log.info('Attempting to tag files from target destination={0}'.format( destination)) discogs_release = DiscogsAlbum(DiscogsWrapper().discogs, release_id, cfg.split_artists, cfg.split_genres_and_styles) release = TaggerUtils(discogs_release, cfg) # ensure we were able to map the release appropriately. if not release.tag_map: _log.fatal( "Unable to map available audio files to the number of tracks in the Discogs release '{0}'. Exiting" .format(release_id)) sys.exit(1) artist = cfg.split_artists.join(release.album.artists) artist = release.album.clean_name(artist) _log.info("Tagging album '{0} - {1}'".format(artist, release.album.title)) dest_dir_name = release.dest_dir_name if os.path.exists(dest_dir_name): _log.fatal( 'Destination directory already exists. directory={0}. Aborting operation' .format(dest_dir_name)) sys.exit(1) else: _log.info("Creating destination directory '{0}'".format(dest_dir_name)) mkdir_p(dest_dir_name) _log.info("Downloading and storing images") release.album.get_images(dest_dir_name, cfg.images_format, cfg.first_image_name) disc_names = dict() folder_names = dict() if release.album.disctotal > 1 and cfg.split_discs_folder: _log.debug("Creating disc structure") for i in range(1, release.album.disctotal + 1): folder_name = "%s%.d" % (release.album_folder_name, i) disc_dir_name = os.path.join(dest_dir_name, folder_name) mkdir_p(disc_dir_name) # This is duplicate, remove one of the following statements disc_names[i] = disc_dir_name folder_names[i] = folder_name else: folder_names[1] = "" for track in release.tag_map: # copy old file into new location if release.album.disctotal > 1 and cfg.split_discs_folder: target_folder = disc_names[int(track['discnumber'])] else: target_folder = dest_dir_name _log.debug("Source file {0}".format( os.path.join(source, track['orig_file']))) _log.info("Writing file {0}".format( os.path.join(target_folder, track['new_file']))) _log.debug("metadata -> {0:2d} {1} - {2}".format( track['tracknumber'], track['artist'], track['title'])) _log.debug("----------> {0}".format(track['new_file'])) shutil.copyfile(track['orig_file'], os.path.join(target_folder, track['new_file'])) # load metadata information metadata = MediaFile(os.path.join(target_folder, track['new_file'])) # read already existing (and still wanted) properties keep_tags = {} if cfg.keep_tags: for name in cfg.keep_tags.split(","): try: getattr(metadata, name) except AttributeError: _log.warn('Unable to keep_tag. tag={0}'.format(name)) continue keep_tags[name] = getattr(metadata, name) # remove current metadata metadata.delete() # set album metadata metadata.album = release.album.title if cfg.split_discs_folder and release.album.disctotal > 1: # the fileext should be stored on the album/track as well fileext = os.path.splitext(track['orig_file'])[1] disc_title_extension = release._value_from_tag_format( cfg.split_discs_extension, track['tracknumber'], track['position'] - 1, fileext) metadata.album = "{0}{1}".format(metadata.album, disc_title_extension) metadata.composer = artist metadata.albumartist = artist metadata.albumartist_sort = release.album.sort_artist metadata.label = release.album.label metadata.year = release.album.year metadata.country = release.album.country metadata.url = release.album.url # add styles to the grouping tag (right now, we can just use one) metadata.grouping = release.album.styles # adding two as there is no standard. discogstagger pre v1 # used (TXXX desc="Catalog #") # mediafile uses TXXX desc="CATALOGNUMBER" metadata.catalognum = release.album.catno metadata.catalognumber = release.album.catno # use the correct genre field, on config use the first style genre = release.album.genres if cfg.use_style: genre = release.album.style metadata.genre = genre metadata.discogs_id = release_id if release.album.disctotal and release.album.disctotal > 1 and track[ 'discnumber']: _log.debug("writing disctotal and discnumber") metadata.disc = track['discnumber'] metadata.disctotal = release.album.disctotal if release.album.is_compilation: metadata.comp = True metadata.comments = release.album.note # encoder if cfg.encoder_tag is not None: metadata.encoder = cfg.encoder_tag # if track.discsubtotal: # metadata.discsubtotal = track.discsubtotal # set track metadata metadata.title = track['title'] metadata.artist = track['artist'] metadata.artist_sort = track['sortartist'] metadata.track = track['tracknumber'] # the following value will be wrong, if the disc has a name or is a multi # disc release --> fix it metadata.tracktotal = release.album.tracktotal_on_disc( track['discnumber']) # it does not make sense to store this in the "common" configuration, but only in the # id.txt. we use a special naming convention --> most probably we should reuse the # configuration parser for this one as well, no? for name, value in list(cfg.release_tags.items()): if name.startswith("tag:"): name = name.split(":") name = name[1] setattr(metadata, name, value) first_image_name = cfg.first_image_name # this should be done in a cleaner way to avoid multiple images in different # folders (use the dest_dir again....) if cfg.embed_coverart and os.path.exists( os.path.join(dest_dir_name, first_image_name)): imgdata = open(os.path.join(dest_dir_name, first_image_name), 'rb').read() imgtype = imghdr.what(None, imgdata) if imgtype in ("jpeg", "png"): _log.debug("Embedding album art.") metadata.art = imgdata if keep_tags is not None: for name in keep_tags: setattr(metadata, name, keep_tags[name]) metadata.save() # start supplementary actions if cfg.write_nfo: _log.info("Generating .nfo file") release.create_nfo() # adopt for multi disc support if cfg.write_m3u: _log.info("Generating .m3u file") release.create_m3u(folder_names) # copy "other files" on request if cfg.copy_other_files and len(release.copy_files) > 0: _log.info("copying files from source directory") copy_files = release.copy_files dir_list = os.listdir(source) _log.debug("start_dir: {0}".format(source)) _log.debug("dir list: {0}".format(dir_list)) file_list = [ os.path.join(source, x) for x in dir_list if not x.lower().endswith(TaggerUtils.FILE_TYPE) and os.path.isfile(os.path.join(source, x)) ] copy_files.extend(file_list) for fname in copy_files: if not fname.endswith(".m3u"): _log.debug("source: {0}".format(fname)) _log.debug("target: {0}".format( os.path.join(dest_dir_name, os.path.basename(fname)))) shutil.copyfile( fname, os.path.join(dest_dir_name, os.path.basename(fname))) # remove source directory, if configured as such. if not cfg.keep_original: _log.info("Deleting source directory '{0}'".format(source)) shutil.rmtree(source) _log.info("Tagging complete.")