Esempio n. 1
0
def test_mp4_movie_tagging():

    movie = tmdb.record_from_query('Star Wars Episode II')
    filename = pkg_resources.resource_filename(
        __name__, os.path.join('data', 'movie.mp4'))
    with tempfile.NamedTemporaryFile() as tmp:
        with open(filename, 'rb') as original:
            tmp.write(original.read())
        tmp.flush()
        tmp.seek(0)
        tmdb.retag(tmp.name, movie)
        rewritten = mp4.MP4(tmp)
        nose.tools.eq_(rewritten.tags['\xa9nam'][0], movie.title)
        nose.tools.eq_(rewritten.tags['desc'][0], movie.tagline)
        nose.tools.eq_(rewritten.tags['ldes'][0], movie.overview)
        nose.tools.eq_(rewritten.tags['\xa9day'][0], movie.release_date)
        nose.tools.eq_(rewritten.tags['stik'], [9])
        #nose.tools.eq_(rewritten.tags['hdvd"] = self.HD
        nose.tools.eq_(sorted(rewritten.tags['\xa9gen']),
                       sorted([k['name'] for k in movie.genres]))
        us_cert = rewritten.tags["----:com.apple.iTunes:iTunEXTC"][0].decode()
        nose.tools.eq_(us_cert, 'mpaa|PG|200|')

        #ensures we can parse the embedded XML document
        plist = rewritten.tags['----:com.apple.iTunes:iTunMOVI'][0]
        xml = ElementTree.fromstring(plist)
        nose.tools.eq_(xml.tag, 'plist')

        #check cover is present
        covr = rewritten['covr']
        nose.tools.eq_(len(covr), 1)
        covr = covr[0]
        assert len(covr) != 0
Esempio n. 2
0
def mp4Reader( filePath ):
  """
  Read metadata from MP4 file and parse into 'internal' format

  Arguments:
    filePath (str): Path to MP4 file to extract metadata from

  Keyword arguments:
    None

  Returns:
    dict: Dictionary containing metadata if found, otherwise, emtpy dict

  """

  log  = logging.getLogger(__name__)
  obj  = mp4.MP4( filePath )                                                            # Create mutagen.mp4.MP4 object
  info = {}                                                                             # Initialize empty dictionary
  for mp4Key, val in obj.items():                                                       # Iterate over key/value pairs in MP4
    if isinstance(val, (tuple,list)):                                                   # If value is an iterable
      val = [v.decode() if isinstance(v, mp4.MP4FreeForm) else v for v in val]          # Decode every MP4FreeForm type element in the iterable
      if len(val) == 1:                                                                 # If only one (1) element in iterable
        val = val[0]                                                                    # Take just that value
    key = MP42COMMON.get(mp4Key, None)                                                  # Get common key if exists 

    if key:                                                                             # If common key found
      info[key] = val                                                                   # Update info dictionary
    else:
      log.debug('Invalid key : {}'.format(mp4Key))                                      # Debug info

  info = _mp4ExtractCover( info )

  return info
Esempio n. 3
0
    def _downloadAudio(self, video):
        filepath = os.environ.get(
            'HOME') + '/Downloads/YouTubePlayer/' + video.title + '[audio].m4a'
        audio = video.m4astreams[-1]
        try:
            audio.download(
                filepath, quiet=False, callback=self._setdownloadETA)
        except FileNotFoundError:
            os.makedirs(os.environ.get('HOME') + '/Downloads/YouTubePlayer')
            audio.getbestaudio(preftype="m4a").download(
                filepath, quiet=False, callback=self._setdownloadETA)

        metadata = self._getMetadata(video)
        if metadata is None:
            return

        self.infoLabel.set_text('Fixing metadata')
        audiofile = mp4.MP4(filepath)

        audiofile['\xa9nam'] = metadata['track_title']
        audiofile['\xa9ART'] = metadata['artist']
        audiofile['\xa9alb'] = metadata['album']
        audiofile['aART'] = metadata['artist']

        cover = metadata['album_art_url']
        fd = urllib.request.urlopen(cover)
        covr = mp4.MP4Cover(fd.read(),
                            getattr(mp4.MP4Cover, 'FORMAT_PNG' if
                                    cover.endswith('png') else 'FORMAT_JPEG'))
        fd.close()
        audiofile['covr'] = [covr]
        audiofile.save()
        self.infoLabel.set_text('Metadata fixed')
Esempio n. 4
0
def processm4a(filename):
    '''Reads metadata from MPEG-4 audio files'''

    f = easymp4.EasyMP4(filename)
    title, anime, role, rolequal = part(f.get('title', _blank)[0])
    artist = f.get('artist', _blank)[0]
    fn_artist = artist if artist else 'Unknown Artist'
    label = f.get('description', _blank)[0]
    album = f.get('album', _blank)[0]
    id = random_id()

    f = mp4.MP4(filename)
    composer = f.get('©wrt', _blank)[0]
    return [
        metadata(
            ID=id,
            OriginalFileName=filename,
            Filename=canonicalFileName(id, fn_artist, title, 'm4a'),
            Tracktitle=title,
            Album=album,
            Length=int(f.info.length * 1000),
            Anime=anime,
            Role=role,
            Rolequalifier=rolequal,
            Artist=artist,
            Composer=composer,
            Label=label,
            InMyriad="NO",
            Dateadded=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M"))
    ]
Esempio n. 5
0
def get_file(filename):
    extension = filename[filename.rfind('.'):len(filename)]

    if extension == '.mp3':
        return mp3.MP3(filename), 'mp3'
    if extension == '.mp4' or extension == '.m4a':
        return mp4.MP4(filename), 'mp4'
Esempio n. 6
0
File: meta.py Progetto: uri247/ot
    def do_one(self, number, fn):
        bn = os.path.basename(fn)
        dn = os.path.dirname(fn)
        result = re.match('(\d*);(.*?);(.*).(m4a)', bn)
        if not result:
            print u'bad file name format: {0}'.format(bn)
            return

        _, youtube_id, title, ext = result.groups()

        disk_number = number / self.tracks_per_disk + 1
        track_number = number % self.tracks_per_disk + 1

        # try to split title to artist - song name
        result = re.match('(.*) - (.*)', title)
        if result:
            artist, song_name = result.groups()
        else:
            artist, song_name = '', title

        f = mp4.MP4(fn)
        f.tags['trkn'] = [(track_number, self.tracks_per_disk)]
        f.tags['disk'] = [(disk_number, self.num_disks)]
        f.tags['cpil'] = True
        f.tags['\xa9alb'] = [
            self.album,
        ]
        f.tags['\xa9nam'] = [
            song_name,
        ]
        f.tags['\xa9ART'] = [
            artist,
        ]
        f.tags['\xa9gen'] = [
            self.genre,
        ]
        f.tags['\xa9cmt'] = [
            youtube_id,
        ]

        jpg_file = os.path.splitext(fn)[0] + '.jpg'
        with open(jpg_file, 'rb') as h:
            cover_data = h.read()
        cover = mp4.MP4Cover(cover_data, mp4.AtomDataType.JPEG)
        f.tags['covr'] = [
            cover,
        ]

        f.save()

        os.unlink(jpg_file)
        new_fn = os.path.join(dn, title + '.' + ext)
        try:
            os.rename(fn, new_fn)
        except WindowsError as e:
            print u'cannot rename {0}'.format(number)
            print u'error {0}: {1}'.format(e.args[0], e.args[1])

        self.xl.add_song(number, youtube_id, title, song_name, artist)
Esempio n. 7
0
    def __init__(self, path):
        super(Track, self).__init__(path)
        self.m4a = mp4.MP4(path)
        f = self.m4a

        self.title = l(f.get(b'\xa9nam'))
        self.discnumber = reader.number(l(f.get(b'disk')))
        self.tracknumber = reader.number(l(f.get(b'trkn')))
        self.length = int(f.info.length)
        self.bitrate = int(f.info.bitrate)

        self.musicbrainz_trackid = l(
            f.get(b'----:com.apple.iTunes:MusicBrainz Track Id'))
        self.genres = f.get(b'\xa9gen')

        artist = reader.Artist()
        artist.name = l(f.get(b'\xa9ART'))
        artist.sortname = l(f.get(b'soar'))
        artist.musicbrainz_artistid = [
            i.decode('utf-8')
            for i in f.get(b'----:com.apple.iTunes:MusicBrainz Artist Id', [])
        ]
        self.artist = artist

        for a, i in zip(
                f.get(b'----:com.apple.iTunes:ARTISTS', []),
                f.get(b'----:com.apple.iTunes:MusicBrainz Artist Id', []),
        ):
            artist = reader.Artist()
            artist.name = a
            artist.musicbrainz_artistid = i
            self.artists.append(artist)

        album = reader.Album()
        album.title = l(f.get(b'\xa9alb'))
        album.date = l(f.get(b'\xa9day'))
        album.country = l(
            f.get(b'----:com.apple.iTunes:MusicBrainz Album Release Country'))
        album.musicbrainz_albumid = l(
            f.get(b'----:com.apple.iTunes:MusicBrainz Album Id'))
        album.musicbrainz_releasegroupid = l(
            f.get(b'----:com.apple.iTunes:MusicBrainz Release Group Id'))
        album.labels = f.get(b'----:com.apple.iTunes:LABEL')
        album.albumtypes = f.get(
            b'----:com.apple.iTunes:MusicBrainz Album Type')
        album.albumstatus = f.get(
            b'----:com.apple.iTunes:MusicBrainz Album Status')

        albumartist = reader.Artist()
        albumartist.name = l(f.get(b'aART'))
        albumartist.sortname = l(f.get(b'soaa'))
        albumartist.musicbrainz_artistid = [
            i.decode('utf-8') for i in f.get(
                b'----:com.apple.iTunes:MusicBrainz Album Artist Id', [])
        ]
        album.artist = albumartist

        self.album = album
Esempio n. 8
0
def tags_mp4(name, infer=True):
    try:
        tags = mp4.MP4(name)
    except (mp4.MP4StreamInfoError, mutagen.mp4.error):
        if infer:
            uprint(
                'WARN: Corrupted or mistagged, inferring artist and album: %s ...'
                % name,
                end=' ')
            return infer_album_artist(name)
        return None
    if not tags:
        return None, None
    return \
        tags['\xa9ART'][0] if '\xa9ART' in tags.keys() else None,\
        tags['\xa9alb'][0] if '\xa9alb' in tags.keys() else None
Esempio n. 9
0
def test_mp4_episode_tagging():

    episode = tvdb.record_from_query('Friends', 1, 1)  #season 1, episode 1
    filename = pkg_resources.resource_filename(
        __name__, os.path.join('data', 'movie.mp4'))
    with tempfile.NamedTemporaryFile() as tmp:
        with open(filename, 'rb') as original:
            tmp.write(original.read())
        tmp.flush()
        tmp.seek(0)
        tvdb.retag(tmp.name, episode)
        rewritten = mp4.MP4(tmp)

        nose.tools.eq_(rewritten["tvsh"][0], episode.season.show.SeriesName)
        nose.tools.eq_(rewritten["\xa9nam"][0], episode.EpisodeName)
        nose.tools.eq_(rewritten["tven"][0], episode.EpisodeName)
        assert rewritten["desc"][0].startswith(episode.Overview)
        nose.tools.eq_(rewritten["ldes"][0], episode.Overview)
        nose.tools.eq_(rewritten["tvnn"][0], episode.season.show.Network)
        nose.tools.eq_(rewritten["\xa9day"][0],
                       episode.FirstAired.strftime('%Y-%m-%d'))
        nose.tools.eq_(rewritten["tvsn"], [episode.season.season_number])
        nose.tools.eq_(
            rewritten["disk"],
            [(episode.season.season_number, len(episode.season.show))])
        nose.tools.eq_(rewritten["\xa9alb"][0], '%s, Season %d' % \
            (episode.season.show.SeriesName, episode.season.season_number))
        nose.tools.eq_(rewritten["tves"], [episode.EpisodeNumber])
        nose.tools.eq_(rewritten["trkn"], \
            [(episode.EpisodeNumber, len(episode.season))])
        nose.tools.eq_(rewritten["stik"], [10])
        #nose.tools.eq_(rewritten["hdvd"], self.HD)
        nose.tools.eq_(rewritten["\xa9gen"], episode.season.show.Genre)

        us_cert = rewritten.tags["----:com.apple.iTunes:iTunEXTC"][0].decode()
        nose.tools.eq_(us_cert, 'us-tv|TV-14|500|')

        #ensures we can parse the embedded XML document
        plist = rewritten.tags['----:com.apple.iTunes:iTunMOVI'][0]
        xml = ElementTree.fromstring(plist)
        nose.tools.eq_(xml.tag, 'plist')

        #check cover is present
        covr = rewritten['covr']
        nose.tools.eq_(len(covr), 1)
        covr = covr[0]
        assert len(covr) != 0
Esempio n. 10
0
    def _downloadAudio(self, video):
        print("download audio")
        download_path = os.environ.get('HOME') + '/Downloads/YouTubePlayer/'
        if not os.path.exists(download_path):
            print(download_path, "does not exist")
            os.makedirs(download_path)
        filepath = download_path + video.title + '[audio].m4a'
        audio = video.m4astreams[-1]
        try:
            audio.download(filepath,
                           quiet=False,
                           callback=self._setdownloadETA)
        except DownloadError:
            GObject.idle_add(self.infoLabel.set_text,
                             "Can't download this video.")
            return

        metadata = self._getMetadata(video)
        if metadata is None:
            return

        self.infoLabel.set_text('Fixing metadata')
        audiofile = mp4.MP4(filepath)

        audiofile['\xa9nam'] = metadata['track_title']
        audiofile['\xa9ART'] = metadata['artist']
        audiofile['\xa9alb'] = metadata['album']
        audiofile['aART'] = metadata['artist']

        cover = metadata['album_art_url']
        fd = urllib.request.urlopen(cover)
        covr = mp4.MP4Cover(
            fd.read(),
            getattr(mp4.MP4Cover,
                    'FORMAT_PNG' if cover.endswith('png') else 'FORMAT_JPEG'))
        fd.close()
        audiofile['covr'] = [covr]
        audiofile.save()
        self.infoLabel.set_text('Metadata fixed')
Esempio n. 11
0
    def mp4info(self):
        """ MP4(m4a)の曲情報を取得 """

        self.tags = mp4.MP4(self.filepath)

        # 各項目取得
        self.title = self.tags.get('\xa9nam', ' ')[0].strip()
        self.album = self.tags.get('\xa9alb', ' ')[0].strip()
        self.artist = self.tags.get('\xa9ART', ' ')[0].strip()
        self.genre = self.tags.get('\xa9gen', ' ')[0].strip()
        self.comment = self.tags.get('\xa9cmt', ' ')[0].strip()

        # アートワーク取得
        artworks = self.tags.get('covr')    # list or None
        artwork = None
        if artworks:
            for artwork in artworks:    # 抽出(最後に登録されている画像のみ)
                pass

        # bytesへ変換
        if artwork:
            self.artwork = bytes(artwork)
        else:
            self.artwork = None
Esempio n. 12
0
def start(upload_file_path,taskid_re=False):
    def gene_params(apiname, taskid=None, slice_id=None):
        ts = str(int(time.time()))
        m2 = hashlib.md5()
        m2.update((appid + ts).encode('utf-8'))
        md5 = m2.hexdigest()
        # md5 = bytes(md5, encoding='utf-8')
        md5 = bytes(md5)
        # 以secret_key为key, 上面的md5为msg, 使用hashlib.sha1加密结果为signa
        signa = hmac.new(secret_key.encode('utf-8'), md5, hashlib.sha1).digest()
        signa = base64.b64encode(signa)
        # signa = str(signa, 'utf-8')
        signa = str(signa)
        if not taskid_re:
            file_len = os.path.getsize(upload_file_path)
            file_name = os.path.basename(upload_file_path)
        else:
            file_len=100
            file_name='get_result'
        param_dict = {}

        if apiname == api_prepare:
            # slice_num是指分片数量,如果您使用的音频都是较短音频也可以不分片,直接将slice_num指定为1即可
            slice_num = int(file_len / file_piece_sice) + (0 if (file_len % file_piece_sice == 0) else 1)
            param_dict['app_id'] = appid
            param_dict['signa'] = signa
            param_dict['ts'] = ts
            param_dict['file_len'] = str(file_len)
            param_dict['file_name'] = file_name
            param_dict['slice_num'] = str(slice_num)
        elif apiname == api_upload:
            param_dict['app_id'] = appid
            param_dict['signa'] = signa
            param_dict['ts'] = ts
            param_dict['task_id'] = taskid
            param_dict['slice_id'] = slice_id
        elif apiname == api_merge:
            param_dict['app_id'] = appid
            param_dict['signa'] = signa
            param_dict['ts'] = ts
            param_dict['task_id'] = taskid
            param_dict['file_name'] = file_name
        elif apiname == api_get_progress or apiname == api_get_result:
            param_dict['app_id'] = appid
            param_dict['signa'] = signa
            param_dict['ts'] = ts
            param_dict['task_id'] = taskid
        return param_dict

    # 请求和结果解析,结果中各个字段的含义可参考:https://doc.xfyun.cn/rest_api/%E8%AF%AD%E9%9F%B3%E8%BD%AC%E5%86%99.html
    def gene_request(apiname, data, files=None, headers=None):
        response = requests.post(lfasr_host + apiname, data=data, files=files, headers=headers)
        result = json.loads(response.text)
        if result["ok"] == 0:
            print("{} success:".format(apiname) + str(result))
            return result
        else:
            print("{} error:".format(apiname) + str(result))
            exit(0)
            return result

    # 预处理
    def prepare_request():
        return gene_request(apiname=api_prepare,
                                 data=gene_params(api_prepare, taskid=None, slice_id=None))

    # 上传
    def upload_request(taskid, upload_file_path):
        file_object = open(upload_file_path, 'rb')
        yield 'uploading file...plz wait DONT CLOSE YOUR BROWSER!!'
        try:
            index = 1
            sig = SliceIdGenerator()
            while True:
                yield 'uploading slice %s ' % str(index)
                content = file_object.read(file_piece_sice)
                if not content or len(content) == 0:
                    break
                files = {
                    "filename": gene_params(api_upload).get("slice_id"),
                    "content": content
                }
                response = gene_request(api_upload,
                                             data=gene_params(api_upload, taskid=taskid,
                                                                   slice_id=sig.getNextSliceId()),
                                             files=files)
                if response.get('ok') != 0:
                    # 上传分片失败
                    yield ('upload slice fail, response: ' + str(response))
                    # return False
                yield ('upload slice ' + str(index) + ' success')
                index += 1
        finally:
            'file index:' + str(file_object.tell())
            file_object.close()
        # return True


    # 合并
    def merge_request(taskid):
        return gene_request(api_merge, data=gene_params(api_merge, taskid=taskid))

    # 获取进度
    def get_progress_request(taskid):
        return gene_request(api_get_progress, data=gene_params(api_get_progress, taskid=taskid))

    # 获取结果
    def get_result_request(taskid):
        return gene_request(api_get_result, data=gene_params(api_get_result, taskid=taskid))

    if not taskid_re:
        yield '<h3>1. 预处理,请勿关闭或刷新浏览器</h3> '
        pre_result = prepare_request()
        from mutagen import mp3,mp4
        if 'mp3' in upload_file_path[-5:]:
            audio = mp3.MP3(upload_file_path)
            audiolength=audio.info.length
            yield 'filelength: '+str(audio.info.length)+' seconds'
        if 'm4a' in upload_file_path[-5:]:
            audio=mp4.MP4(upload_file_path)
            audiolength = audio.info.length
            yield 'filelength: '+str(audio.info.length)+' seconds'

        speed=15
        totaltime=float(speed*(float(audiolength)/60)/60)
        if totaltime == 0:totaltime = 1
        yield '预计完成时间: '+str(totaltime)+'分钟'

        taskid = pre_result["data"]
        yield '<h3> 2 . 分片上传至服务器,请耐心等待...预计需要:'+str(float(audiolength/10))+'秒</h3> '
        for msg in upload_request(taskid=taskid, upload_file_path=upload_file_path):
            yield msg
        yield '<h3> 3 . 文件合并</h3>'
        merge_request(taskid=taskid)
        yield '<h3> 4 . 获取任务进度 (每10秒刷新一次),taskid: %s </h3>' % str(taskid)
        def completed_per(pasttime):
            per=pasttime/(totaltime*60)*100
            if per > 99:
                per = 99.99999
            return str(per)+'%'

        pasttime=0
        while True:
            # 每隔10秒获取一次任务进度
            pasttime=pasttime+10
            progress = get_progress_request(taskid)
            progress_dic = progress
            if progress_dic['err_no'] != 0 and progress_dic['err_no'] != 26605:
                print('task error: ' + progress_dic['failed'])
                return
            else:
                data = progress_dic['data']
                task_status = json.loads(data)
                if task_status['status'] == 9:
                    # print ('task ' +taskid + ' finished')
                    break
                yield ('The task is in processing, task status: ' + data+'  completed: %s' % completed_per(pasttime))

            # yield '# 每次获取进度间隔10S'
            time.sleep(10)

        yield '<h3> 5 . 完成转写</h3>'
        yield get_result_request(taskid=taskid)
    else:
        yield get_result_request(taskid=taskid_re)
Esempio n. 13
0
def set_metadata_tags(args, audio_file, idx, track, ripper):
    # log completed file
    print(Fore.GREEN + Style.BRIGHT + os.path.basename(audio_file) +
          Style.NORMAL + "\t[ " + format_size(os.stat(audio_file)[ST_SIZE]) +
          " ]" + Fore.RESET)

    if args.output_type == "wav" or args.output_type == "pcm":
        print(Fore.YELLOW + "Skipping metadata tagging for " +
              args.output_type + " encoding...")
        return

    # ensure everything is loaded still
    if not track.is_loaded:
        track.load()
    if not track.album.is_loaded:
        track.album.load()
    album_browser = track.album.browse()
    album_browser.load()

    # calculate num of tracks on disc and num of dics
    num_discs = 0
    num_tracks = 0
    for track_browse in album_browser.tracks:
        if (track_browse.disc == track.disc and
                track_browse.index > track.index):
            num_tracks = track_browse.index
        if track_browse.disc > num_discs:
            num_discs = track_browse.disc

    # try to get genres from Spotify's Web API
    genres = None
    if args.genres is not None:
        genres = ripper.web.get_genres(args.genres[0], track)

    # use mutagen to update id3v2 tags and vorbis comments
    try:
        audio = None
        on_error = 'replace' if args.ascii_path_only else 'ignore'
        album = to_ascii(track.album.name, on_error)
        artist = to_ascii(track.artists[0].name, on_error)
        title = to_ascii(track.name, on_error)

        # the comment tag can be formatted
        if args.comment is not None:
            comment = \
                format_track_string(ripper, args.comment[0], idx, track)
            comment_ascii = to_ascii(comment, on_error)

        if args.grouping is not None:
            grouping = \
                format_track_string(ripper, args.grouping[0], idx, track)
            grouping_ascii = to_ascii(grouping, on_error)

        if genres is not None and genres:
            genres_ascii = [to_ascii(genre) for genre in genres]

        # cover art image
        def get_cover_image(image_link):
            image_link = 'http://i.scdn.co%s' % (
                image_link[len('spotify'):].replace(':', '/'))
            cover_file = urllib.request.urlretrieve(image_link)[0]

            with open(cover_file, "rb") as f:
                if f.mode == "rb":
                    return f.read()
                else:
                    return None

        image_link = str(track.album.cover(0).link)
        image = get_cover_image(image_link)
        
        def tag_to_ascii(_str, _str_ascii):
            return _str if args.ascii_path_only else _str_ascii

        def idx_of_total_str(_idx, _total):
            if _total > 0:
                return "%d/%d" % (_idx, _total)
            else:
                return "%d" % (_idx)

        def save_cover_image(embed_image_func):
            if image is not None:
                def write_image(file_name):
                    cover_path = os.path.dirname(audio_file)
                    cover_file = os.path.join(cover_path, file_name)
                    if not path_exists(cover_file):
                        with open(cover_file, "wb") as f:
                            f.write(image)

                if args.cover_file is not None:
                    write_image(args.cover_file[0])
                elif args.cover_file_and_embed is not None:
                    write_image(args.cover_file_and_embed[0])
                    embed_image_func()
                else:
                    embed_image_func()

        def set_id3_tags(audio):
            # add ID3 tag if it doesn't exist
            audio.add_tags()

            def embed_image():
                audio.tags.add(
                    id3.APIC(
                        encoding=3,
                        mime='image/jpeg',
                        type=3,
                        desc='Front Cover',
                        data=image
                    )
                )

            save_cover_image(embed_image)

            if album is not None:
                audio.tags.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            audio.tags.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)],
                         encoding=3))
            audio.tags.add(
                id3.TPE1(text=[tag_to_ascii(track.artists[0].name, artist)],
                         encoding=3))
            audio.tags.add(id3.TDRC(text=[str(track.album.year)],
                                    encoding=3))
            audio.tags.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            audio.tags.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                audio.tags.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                audio.tags.add(tcon_tag)

            if args.id3_v23:
                audio.tags.update_to_v23()
                audio.save(v2_version=3, v23_sep='/')
                audio.tags.version = (2, 3, 0)
            else:
                audio.save()

        # aac is not well supported
        def set_id3_tags_raw(audio, audio_file):
            try:
                id3_dict = id3.ID3(audio_file)
            except id3.ID3NoHeaderError:
                id3_dict = id3.ID3()

            def embed_image():
                id3_dict.add(
                    id3.APIC(
                        encoding=3,
                        mime='image/jpeg',
                        type=3,
                        desc='Front Cover',
                        data=image
                    )
                )

            save_cover_image(embed_image)

            if album is not None:
                id3_dict.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            id3_dict.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)],
                         encoding=3))
            id3_dict.add(
                id3.TPE1(text=[tag_to_ascii(track.artists[0].name, artist)],
                         encoding=3))
            id3_dict.add(id3.TDRC(text=[str(track.album.year)],
                                  encoding=3))
            id3_dict.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            id3_dict.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                id3_dict.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                id3_dict.add(tcon_tag)

            if args.id3_v23:
                id3_dict.update_to_v23()
                id3_dict.save(audio_file, v2_version=3, v23_sep='/')
                id3_dict.version = (2, 3, 0)
            else:
                id3_dict.save(audio_file)
            audio.tags = id3_dict

        def set_vorbis_comments(audio):
            # add Vorbis comment block if it doesn't exist
            if audio.tags is None:
                audio.add_tags()

            def embed_image():
                pic = flac.Picture()
                pic.type = 3
                pic.mime = "image/jpeg"
                pic.desc = "Front Cover"
                pic.data = image
                if args.output_type == "flac":
                    audio.add_picture(pic)
                else:
                    data = base64.b64encode(pic.write())
                    audio["METADATA_BLOCK_PICTURE"] = [data.decode("ascii")]

            save_cover_image(embed_image)

            if album is not None:
                audio.tags["ALBUM"] = tag_to_ascii(track.album.name, album)
            audio.tags["TITLE"] = tag_to_ascii(track.name, title)
            audio.tags["ARTIST"] = tag_to_ascii(track.artists[0].name, artist)
            audio.tags["DATE"] = str(track.album.year)
            audio.tags["YEAR"] = str(track.album.year)
            audio.tags["DISCNUMBER"] = str(track.disc)
            audio.tags["DISCTOTAL"] = str(num_discs)
            audio.tags["TRACKNUMBER"] = str(track.index)
            audio.tags["TRACKTOTAL"] = str(num_tracks)
            if args.comment is not None:
                audio.tags["COMMENT"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags["GROUPING"] = \
                    tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags["GENRE"] = ", ".join(_genres)

            audio.save()

        # only called by Python 3
        def set_mp4_tags(audio):
            # add MP4 tags if it doesn't exist
            if audio.tags is None:
                audio.add_tags()

            def embed_image():
                audio.tags["covr"] = mp4.MP4Cover(image)

            save_cover_image(embed_image)

            if album is not None:
                audio.tags["\xa9alb"] = tag_to_ascii(track.album.name, album)
            audio["\xa9nam"] = tag_to_ascii(track.name, title)
            audio.tags["\xa9ART"] = tag_to_ascii(track.artists[0].name, artist)
            audio.tags["\xa9day"] = str(track.album.year)
            audio.tags["disk"] = [(track.disc, num_discs)]
            audio.tags["trkn"] = [(track.index, num_tracks)]
            if args.comment is not None:
                audio.tags["\xa9cmt"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags["\xa9grp"] = tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags["\xa9gen"] = ", ".join(_genres)

            audio.save()

        def set_m4a_tags(audio):
            # add M4A tags if it doesn't exist
            audio.add_tags()

            def embed_image():
                audio.tags[str("covr")] = m4a.M4ACover(image)

            save_cover_image(embed_image)

            if album is not None:
                audio.tags[b"\xa9alb"] = tag_to_ascii(track.album.name, album)
            audio[b"\xa9nam"] = tag_to_ascii(track.name, title)
            audio.tags[b"\xa9ART"] = tag_to_ascii(
                track.artists[0].name, artist)
            audio.tags[b"\xa9day"] = str(track.album.year)
            audio.tags[str("disk")] = (track.disc, num_discs)
            audio.tags[str("trkn")] = (track.index, num_tracks)
            if args.comment is not None:
                audio.tags[b"\xa9cmt"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags[b"\xa9grp"] = tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags[b"\xa9gen"] = ", ".join(_genres)

            audio.save()

        if args.output_type == "flac":
            audio = flac.FLAC(audio_file)
            set_vorbis_comments(audio)
        elif args.output_type == "ogg":
            audio = oggvorbis.OggVorbis(audio_file)
            set_vorbis_comments(audio)
        elif args.output_type == "opus":
            audio = oggopus.OggOpus(audio_file)
            set_vorbis_comments(audio)
        elif args.output_type == "aac":
            audio = aac.AAC(audio_file)
            set_id3_tags_raw(audio, audio_file)
        elif args.output_type == "m4a":
            if sys.version_info >= (3, 0):
                from mutagen import mp4

                audio = mp4.MP4(audio_file)
                set_mp4_tags(audio)
            else:
                from mutagen import m4a, mp4

                audio = m4a.M4A(audio_file)
                set_m4a_tags(audio)
                audio = mp4.MP4(audio_file)
        elif args.output_type == "alac.m4a":
            if sys.version_info >= (3, 0):
                from mutagen import mp4

                audio = mp4.MP4(audio_file)
                set_mp4_tags(audio)
            else:
                from mutagen import m4a, mp4

                audio = m4a.M4A(audio_file)
                set_m4a_tags(audio)
                audio = mp4.MP4(audio_file)
        elif args.output_type == "mp3":
            audio = mp3.MP3(audio_file, ID3=id3.ID3)
            set_id3_tags(audio)

        def bit_rate_str(bit_rate):
            brs = "%d kb/s" % bit_rate
            if not args.cbr:
                brs = "~" + brs
            return brs

        def mode_str(mode):
            modes = ["Stereo", "Joint Stereo", "Dual Channel", "Mono"]
            if mode < len(modes):
                return modes[mode]
            else:
                return ""

        def channel_str(num):
            channels = ["", "Mono", "Stereo"]
            if num < len(channels):
                return channels[num]
            else:
                return ""

        # log id3 tags
        print("-" * 79)
        print(Fore.YELLOW + "Setting artist: " + artist + Fore.RESET)
        if album is not None:
            print(Fore.YELLOW + "Setting album: " + album + Fore.RESET)
        print(Fore.YELLOW + "Setting title: " + title + Fore.RESET)
        print(Fore.YELLOW + "Setting track info: (" +
              str(track.index) + ", " + str(num_tracks) + ")" + Fore.RESET)
        print(Fore.YELLOW + "Setting disc info: (" + str(track.disc) +
              ", " + str(num_discs) + ")" + Fore.RESET)
        print(Fore.YELLOW + "Setting release year: " +
              str(track.album.year) + Fore.RESET)
        if genres is not None and genres:
            print(Fore.YELLOW + "Setting genres: " +
                  " / ".join(genres_ascii) + Fore.RESET)
        if image is not None:
            print(Fore.YELLOW + "Adding cover image" + Fore.RESET)
        if args.comment is not None:
            print(Fore.YELLOW + "Adding comment: " + comment_ascii +
                  Fore.RESET)
        if args.grouping is not None:
            print(Fore.YELLOW + "Adding grouping: " + grouping_ascii +
                  Fore.RESET)
        if args.output_type == "flac":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tFree Lossless Audio Codec" +
                  "\t[ " + bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) +
                  " Hz - " + channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            print(Fore.YELLOW + "Writing Vorbis comments - " +
                  audio.tags.vendor + Fore.RESET)
            print("-" * 79)
        if args.output_type == "alac.m4a":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tApple Lossless" +
                  "\t[ " + bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) +
                  " Hz - " + channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            print(Fore.YELLOW + "Writing Apple iTunes metadata - " +
                  Fore.RESET)
            print("-" * 79)
        elif args.output_type == "ogg":
            print("Time: " + format_time(audio.info.length) +
                  "\tOgg Vorbis Codec" +
                  "\t[ " + bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) +
                  " Hz - " + channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            print(Fore.YELLOW + "Writing Vorbis comments - " +
                  audio.tags.vendor + Fore.RESET)
            print("-" * 79)
        elif args.output_type == "opus":
            print("Time: " + format_time(audio.info.length) + "\tOpus Codec" +
                  "\t[ " + channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            print(Fore.YELLOW + "Writing Vorbis comments - " +
                  audio.tags.vendor + Fore.RESET)
            print("-" * 79)
        elif args.output_type == "mp3":
            print("Time: " + format_time(audio.info.length) + "\tMPEG" +
                  str(audio.info.version) +
                  ", Layer " + ("I" * audio.info.layer) + "\t[ " +
                  bit_rate_str(audio.info.bitrate / 1000) +
                  " @ " + str(audio.info.sample_rate) + " Hz - " +
                  mode_str(audio.info.mode) + " ]")
            print("-" * 79)
            id3_version = "v%d.%d" % (
                audio.tags.version[0], audio.tags.version[1])
            print("ID3 " + id3_version + ": " +
                  str(len(audio.tags.values())) + " frames")
            print(
                Fore.YELLOW + "Writing ID3 version " +
                id3_version + Fore.RESET)
            print("-" * 79)
        elif args.output_type == "aac":
            print("Time: " + format_time(audio.info.length) +
                  "\tAdvanced Audio Coding" +
                  "\t[ " + bit_rate_str(audio.info.bitrate / 1000) +
                  " @ " + str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            id3_version = "v%d.%d" % (
                audio.tags.version[0], audio.tags.version[1])
            print("ID3 " + id3_version + ": " +
                  str(len(audio.tags.values())) + " frames")
            print(
                Fore.YELLOW + "Writing ID3 version " +
                id3_version + Fore.RESET)
            print("-" * 79)
        elif args.output_type == "m4a":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tMPEG-4 Part 14 Audio" +
                  "\t[ " + bit_rate_str(bit_rate / 1000) +
                  " @ " + str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print("-" * 79)
            print(Fore.YELLOW + "Writing Apple iTunes metadata - " +
                  str(audio.info.codec) + Fore.RESET)
            print("-" * 79)

    except id3.error:
        print(Fore.YELLOW + "Warning: exception while saving id3 tag: " +
              str(id3.error) + Fore.RESET)
Esempio n. 14
0
def setAlbumInfo(album):
    conn = sqlite3.connect('database/music.db')
    # print("Connection granted")
    cur = conn.cursor()
    conn_settings = sqlite3.connect('database/settings.db')
    # print("Connection granted")
    cur_settings = conn_settings.cursor()

    cur_settings.execute("SELECT music_directory_path FROM music_directory WHERE active='True'")
    result_directory = cur_settings.fetchone()

    cur.execute("SELECT album_artist FROM album WHERE album_spotify_id='%s'" % str(album))
    result_art_id = cur.fetchone()

    cur.execute("SELECT artist_name FROM artist WHERE artist_id='%s'" % str(result_art_id[0]))
    result_art_name = cur.fetchone()

    cur.execute("SELECT album_name FROM album WHERE album_spotify_id='%s'" % str(album))
    result_alb_name = cur.fetchone()

    cur.execute("SELECT album_id FROM album WHERE album_spotify_id='%s'" % str(album))
    result_alb_id = cur.fetchone()

    cur.execute("SELECT album_number_songs FROM album WHERE album_spotify_id='%s'" % str(album))
    result_alb_number_songs = cur.fetchone()

    print(result_alb_number_songs[0])

    cur.execute("SELECT album_coverart FROM album WHERE album_spotify_id='%s'" % str(album))
    result_alb_coverart = cur.fetchone()

    try:
        coverArtDirectory = "%s%s/cover_art" % (str(result_directory[0]), str(result_art_name[0]))
        coverArtName = "%s.jpg" % str(result_alb_name[0])
        if not os.path.exists(coverArtDirectory):
            os.makedirs(coverArtDirectory)

        if coverArtName not in coverArtDirectory:
            urllib.request.urlretrieve("%s" % str(result_alb_coverart[0]), "%s/%s" % (str(coverArtDirectory), str(coverArtName)))
            print("Cover Art Downloaded Fine!")

        directory = "%s%s/%s" % (str(result_directory[0]), str(result_art_name[0]), str(result_alb_name[0]))
        directoryResults = os.listdir(directory)
        for re in directoryResults:
            print("PreCleaned: " + re)
            cleanResult = re.replace('.m4a', '')
            print("Cleaned: " + cleanResult)

            cur.execute("SELECT song_name FROM song WHERE song_album='%s' AND song_name='%s'" % (str(result_alb_id[0]), str(cleanResult)))
            result_song_name = cur.fetchone()
            cur.execute("SELECT song_track_number FROM song WHERE song_album='%s' AND song_name='%s'" % (str(result_alb_id[0]), str(cleanResult)))
            result_song_track_number = cur.fetchone()
            cur.execute("SELECT song_spotify_id FROM song WHERE song_album='%s' AND song_name='%s'" % (str(result_alb_id[0]), str(cleanResult)))
            result_song_spotify_id = cur.fetchone()

            if (result_song_track_number != None):
                # print(str(result_song_track_number[0]))

                coverArtLocation = "%s/%s" % (str(coverArtDirectory), str(coverArtName))

                song = mutagen.MP4("%s/%s" % (str(directory), str(re)))

                song['\xa9nam'] = result_song_name[0]
                song['\xa9alb'] = result_alb_name[0]
                song['\xa9ART'] = result_art_name[0]
                song['trkn'] = [(int(result_song_track_number[0]), int(result_alb_number_songs[0]))]

                with open(coverArtLocation, "rb") as f:
                    song["covr"] = [mutagen.MP4Cover(f.read(), imageformat=mutagen.MP4Cover.FORMAT_JPEG)]

                song.save()

    except FileNotFoundError as err:
        print("Sorry File Not found: {0}".format(err))



    #
    # savedir = "/home/que/Music/MusicLibrary/Artists/%s/%s/" % (str(art_id[0]), str(alb_name[0]))
    # savename = "%s%s" % (fileName, audioType)

    conn.close()
    conn_settings.close()
Esempio n. 15
0
def get_tags(song_path):
        return mp4.MP4('/Users/ft3/Music/lofi/{}'.format(song_path))
Esempio n. 16
0
def mp4Tagger(file, metaData):
    '''
  Name:
    mp4Tags
  Purpose:
    A function to parse information from the IMDbPY API and
    write Tag data to MP4 files.
  Inputs:
    file     : Full path of file to write metadata to.
    metaData : Dictionary of meta data where keys are internal
                  metadata keys and values are metadata values
  Outputs:
    Returns following values based on completion.
       0 : Completed successfully.
       1 : Input was NOT and MP4
       2 : IMDb ID was not valid
       3 : Failed to download information from IMDb AND themoviedb.org
       4 : Writing tags is NOT possible
       5 :  Failed when trying to remove tags from file.
       6 : Failed when trying to write tags to file.
      10 : IMDbPY not installed AND getTMDb_Info failed to import
      11 : File is too large
  Keywords:
    None
  Dependencies:
    mutagen
  Author and History: 
    Kyle R. Wodzicki     Created 18 Feb. 2018
  '''
    log = logging.getLogger(__name__)  # Set up a logger
    log.debug('Testing file is MP4')  # Debugging information
    if not file.endswith('.mp4'):  # If the input file does NOT end in '.mp4'
        log.error('Input file is NOT an MP4!!!')
        return 1  # Print message and return code one (1)

    log.debug('Testing file too large')  # Debugging information
    if os.stat(
            file
    ).st_size > sys.maxsize:  # If the file size is larger than the supported maximum size
        log.error('Input file is too large!')
        return 11  # Print message and return code eleven (11)

    metaData = toMP4(metaData)

    if len(metaData) == 0:
        log.warning('No metadata, cannot write tags')
        return 3

    filedir, filebase = os.path.dirname(file), os.path.basename(
        file)  # Get the directory and baseanem of the file

    log.debug('Loading file using mutagen.mp4.MP4')  # Debugging information
    handle = mp4.MP4(file)  # Initialize mutagen MP4 handler
    log.debug('Attempting to add tag block to file')  # Debugging information
    try:  # Try to...
        handle.add_tags()  # Add new tags to the file
    except mp4.error as e:  # On exception, catch the error
        if 'already exists' in e.__str__(
        ):  # If the error is that the tag block already exists
            log.debug(
                'MP4 tags already exist in file.')  # Debugging information
            pass  # Pass
        else:  # Else, adding is not possible
            log.error('Could NOT add tags to file!')  # Log an error
            return 4  # Return code 4
    try:  # Try to...
        handle.delete()
        # Remove all old tags.
    except mp4.error as e:  # On exception, catch the error
        log.error(e.__str__())
        # Log the error
        return 5
        # Return code 5
    log.debug('Setting basic inforamtion')  # Debugging information
    for key, val in metaData.items():
        if key == 'covr' and val != '':
            log.debug('Attempting to get coverart')  # Debugging information
            fmt = mp4.AtomDataType.PNG if val.endswith(
                'png') else mp4.AtomDataType.JPEG  # Set format for the image
            data = download(val)
            if data is not None:
                val = [mp4.MP4Cover(data, fmt)]  # Add image to file
            else:
                continue
        try:
            handle[key] = val
        except:
            log.warning('Failed to write metadata for: {}'.format(key))

    log.debug('Saving tags to file')  # Debugging information
    try:  # Try to...
        handle.save()
        # Save the tags
    except:  # On exception
        log.error('Failed to save tags to file!')
        # Log an error
        return 6
    return 0
Esempio n. 17
0
    def __update_tag(self, download_dir, audio_file, image_file,
                     song_title=None, album_title=None, album_artist=None, album_composer=None,
                     track_number=-1, process_index=-1, process_total=-1):
        """
        The function that update audio metadata for each song.

        :param str download_dir: Download directory
        :param str audio_file: Path to audio file
        :param str image_file: Path to image file
        :param str song_title: Song title
        :param str album_title: Album title to be saved in metadata
        :param str album_artist: Album artist to be saved in metadata
        :param str album_composer: Album composer to be saved in metadata
        :param int track_number: track number to be saved in metadata
        :param int process_index: Current process index displayed in log message
        :param int process_total: Total number of process displayed in log message
        """

        if audio_file is None:
            logger.warning('[Process:{}/{}][Track:{}] Could not update metadata because there is no data found on the playlist. The video may be private or deleted.'.format(process_index, process_total, track_number))
            return

        if process_index > 0 and process_total > 0:

            if track_number > 0:
                log_prefix = '[Process:{}/{}][Track:{}]'.format(process_index, process_total, track_number)

            else:
                log_prefix = '[Process:{}/{}]'.format(process_index, process_total)

        else:
            log_prefix = ''

        audio_filename = os.path.basename(audio_file)

        try:
            # Validate audio data
            if not os.path.isfile(audio_file):
                raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), audio_file)

            audio_mime_type = mimetypes.guess_type(audio_file)

            if contains_at_least(audio_mime_type, ['audio/x-mp4', 'audio/x-m4a', 'audio/mp4a-latm']):

                # For more info about mp4 tag is available at
                # https://github.com/quodlibet/mutagen/blob/cf399dc58940fb1356f672809d763be9e2af0033/mutagen/mp4/__init__.py
                # http://atomicparsley.sourceforge.net/mpeg-4files.html
                mp4_data = mp4.MP4(audio_file)
                # Track Number
                if not self.no_track_number and track_number > 0:
                    mp4_data['trkn'] = [(track_number, 0)]
                # Cover image
                if not self.no_artwork:
                    image_data = self.__get_tag_image(image_file, audio_mime_type)
                    if image_data:
                        mp4_data['covr'] = [image_data]
                # Album title
                if not self.no_album_title and album_title is not None:
                    mp4_data['\xa9alb'] = album_title
                # Album artist
                if not self.no_album_artist and album_artist is not None:
                    mp4_data['aART'] = album_artist
                # Composer
                if not self.no_composer and album_composer is not None:
                    mp4_data['\xa9wrt'] = album_composer
                # Part of compilation
                if not self.no_compilation:
                    mp4_data['cpil'] = True
                # Save
                mp4_data.save()

            elif contains_at_least(audio_mime_type, ['audio/x-mp3', 'audio/mpeg']):

                # For more info about ID3v2 tag is available at
                # https://github.com/quodlibet/mutagen/blob/4a5d7d17f1a611280cc52d229aa70b77ca3c55dd/mutagen/id3/_frames.py
                # https://help.mp3tag.de/main_tags.html
                mp3_data = id3.ID3(audio_file)
                # Cover image
                if not self.no_artwork:
                    image_data = self.__get_tag_image(image_file, audio_mime_type)
                    if image_data:
                        mp3_data['APIC'] = image_data
                # Track number
                if not self.no_track_number and track_number > 0:
                    mp3_data.add(id3.TRCK(encoding=3, text=['{}/{}'.format(track_number, 0)]))
                # Album title
                if not self.no_album_title and album_title is not None:
                    mp3_data["TALB"] = id3.TALB(encoding=0, text=album_title)
                # Album artist
                if not self.no_album_artist and album_artist is not None:
                    mp3_data["TPE2"] = id3.TPE2(encoding=0, text=album_artist)
                # Composer
                if not self.no_composer and album_composer is not None:
                    mp3_data["TCOM"] = id3.TCOM(encoding=0, text=album_composer)
                # Part of compilation
                if not self.no_compilation:
                    mp3_data['TCMP'] = id3.TCMP(encoding=0, text=['1'])
                # Save
                mp3_data.save()

            elif contains_at_least(audio_mime_type, ['audio/x-aac']):

                # TODO: Add AAC support
                pass
                # image_data = __get_tag_image(image_file, audio_mime_type)
                # aac_data = aac.AAC(audio_file)
                # if not self.no_track_number:
                #     if track_number > 0 and track_total > 0:
                #         aac_data.add_tags(id3.TRCK(encoding=3, text=['{}/{}'.format(track_number, track_total)]))
                #         # mp3_data['TRCK'] = id3.TRCK(encoding=3, text=[str(track_number)])
                # if image_data:
                #     mp3_data['APIC'] = image_data
                #     aac_data.save()

            elif contains_at_least(audio_mime_type, ['audio/x-flac']):

                # https://github.com/quodlibet/mutagen/blob/a1db79ece62c4e86259f15825e360d1ce0986a22/mutagen/flac.py
                # https://github.com/quodlibet/mutagen/blob/4a5d7d17f1a611280cc52d229aa70b77ca3c55dd/tests/test_flac.py

                flac_data = flac.FLAC(audio_file)
                # Artwork
                if not self.no_artwork:
                    image_data = self.__get_tag_image(image_file, audio_mime_type)
                    if image_data:
                        flac_data.add_picture(image_data)
                # Save
                flac_data.save()

                flac_data = File(audio_file)
                # Track number
                if not self.no_track_number and track_number > 0:
                    flac_data.tags['tracknumber'] = str(track_number)
                # Album title
                if not self.no_album_title and album_title is not None:
                    flac_data.tags['album'] = album_title
                # Album artist
                if not self.no_album_artist and album_artist is not None:
                    flac_data.tags['albumartist'] = album_artist
                # Composer
                if not self.no_composer and album_composer is not None:
                    flac_data.tags['composer'] = album_composer
                # Part of compilation
                if not self.no_compilation:
                    pass
                # Save
                flac_data.save()
                # audio = File(audio_file, easy=True)

            else:
                raise InvalidMimeTypeException("Invalid audio format.", audio_mime_type)

            # Remove artwork if succeeded
            if os.path.exists(image_file):
                os.remove(image_file)

            # Rename filename from id to title
            dest_audio_file = os.path.join(download_dir, '{}.{}'.format(song_title, self.audio_codec))
            os.rename(audio_file, dest_audio_file)

            dest_audio_filename = os.path.basename(dest_audio_file)
            logger.info('{}[File:{}] Updated.'.format(log_prefix, dest_audio_filename))

        except FileNotFoundError:
            message = 'File not found. Skipped.'
            logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message))

        except InvalidDataException as e:
            message = e.message + ' Skipped.'
            logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message))

        except InvalidMimeTypeException as e:
            message = e.message + ' Skipped.'
            logger.warning('{}[File:{}] {}'.format(log_prefix, audio_filename, message))

        except Exception as e:
            message = 'Error {}: {} Skipped.'.format(type(e), str(e))
            logger.error('{}[File:{}] {}'.format(log_prefix, audio_filename, message))
Esempio n. 18
0
def set_metadata_tags(args, audio_file, idx, track, ripper):
    # log completed file
    print(Fore.GREEN + Style.BRIGHT + os.path.basename(audio_file) +
          Style.NORMAL + "\t[ " +
          format_size(os.stat(enc_str(audio_file))[ST_SIZE]) + " ]" +
          Fore.RESET)

    if args.output_type == "wav" or args.output_type == "pcm":
        print_yellow("Skipping metadata tagging for " + args.output_type +
                     " encoding...")
        return

    # ensure everything is loaded still
    if not track.is_loaded:
        track.load(args.timeout)
    if not track.album.is_loaded:
        track.album.load(args.timeout)

    # use mutagen to update audio file tags
    try:
        audio = None
        tags = None

        if args.output_type == "flac":
            audio = flac.FLAC(audio_file)
            tags = VorbisTags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        elif args.output_type == "aiff":
            audio = aiff.AIFF(audio_file)
            tags = Id3Tags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        elif args.output_type == "ogg":
            audio = oggvorbis.OggVorbis(audio_file)
            tags = VorbisTags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        elif args.output_type == "opus":
            audio = oggopus.OggOpus(audio_file)
            tags = VorbisTags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        elif args.output_type == "aac":
            audio = aac.AAC(audio_file)
            tags = RawId3Tags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        elif args.output_type == "m4a" or args.output_type == "alac.m4a":
            if sys.version_info >= (3, 0):
                audio = mp4.MP4(audio_file)
                tags = MP4Tags(args, audio_file, idx, track, ripper)
                tags.set_tags(audio)
            else:
                audio = m4a.M4A(audio_file)
                tags = M4ATags(args, audio_file, idx, track, ripper)
                tags.set_tags(audio)
                audio = mp4.MP4(audio_file)

        elif args.output_type == "mp3":
            audio = mp3.MP3(audio_file, ID3=id3.ID3)
            tags = Id3Tags(args, audio_file, idx, track, ripper)
            tags.set_tags(audio)

        # utility functions
        def bit_rate_str(bit_rate):
            brs = "%d kb/s" % bit_rate
            if not args.cbr:
                brs = "~" + brs
            return brs

        def mode_str(mode):
            modes = ["Stereo", "Joint Stereo", "Dual Channel", "Mono"]
            return modes[mode] if mode < len(modes) else ""

        def channel_str(num):
            channels = ["", "Mono", "Stereo"]
            return channels[num] if num < len(channels) else ""

        def print_line():
            print("-" * 79)

        # log tags
        print_line()
        print_yellow("Setting artist: " + tags.artists())

        if tags.album() is not None:
            print_yellow("Setting album: " + tags.album())

        if tags.album_artist() is not None:
            print_yellow("Setting album artist: " + tags.album_artist())

        print_yellow("Setting title: " + tags.title())
        print_yellow("Setting track info: (" + tags.track_idx() + ", " +
                     tags.num_tracks() + ")")
        print_yellow("Setting disc info: (" + tags.disc_idx() + ", " +
                     tags.num_discs() + ")")
        print_yellow("Setting release year: " + tags.year())

        if tags.genres() is not None:
            print_yellow("Setting genres: " + " / ".join(tags.genres()))

        if tags.image is not None:
            print_yellow("Adding cover image")

        if tags.comment() is not None:
            print_yellow("Adding comment: " + tags.comment())

        if tags.grouping() is not None:
            print_yellow("Adding grouping: " + tags.grouping())

        # log bitrate info
        if args.output_type == "flac":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tFree Lossless Audio Codec" + "\t[ " +
                  bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            print_yellow("Writing Vorbis comments - " + audio.tags.vendor)
            print_line()

        elif args.output_type == "aiff":
            print("Time: " + format_time(audio.info.length) +
                  "\tAudio Interchange File Format" + "\t[ " +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print("ID3 " + id3_version + ": " + str(len(audio.tags.values())) +
                  " frames")
            print(Fore.YELLOW + "Writing ID3 version " + id3_version)
            print_line()

        elif args.output_type == "alac.m4a":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tApple Lossless" + "\t[ " + bit_rate_str(bit_rate / 1000) +
                  " @ " + str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            print_yellow("Writing Apple iTunes metadata")
            print_line()

        elif args.output_type == "ogg":
            print("Time: " + format_time(audio.info.length) +
                  "\tOgg Vorbis Codec" + "\t[ " +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            print_yellow("Writing Vorbis comments - " + audio.tags.vendor)
            print_line()

        elif args.output_type == "opus":
            print("Time: " + format_time(audio.info.length) + "\tOpus Codec" +
                  "\t[ " + channel_str(audio.info.channels) + " ]")
            print_line()
            print_yellow("Writing Vorbis comments - " + audio.tags.vendor)
            print_line()

        elif args.output_type == "mp3":
            print("Time: " + format_time(audio.info.length) + "\tMPEG" +
                  str(audio.info.version) + ", Layer " +
                  ("I" * audio.info.layer) + "\t[ " +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  mode_str(audio.info.mode) + " ]")
            print_line()
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print("ID3 " + id3_version + ": " + str(len(audio.tags.values())) +
                  " frames")
            print_yellow("Writing ID3 version " + id3_version)
            print_line()

        elif args.output_type == "aac":
            print("Time: " + format_time(audio.info.length) +
                  "\tAdvanced Audio Coding" + "\t[ " +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print("ID3 " + id3_version + ": " + str(len(audio.tags.values())) +
                  " frames")
            print("Writing ID3 version " + id3_version)
            print_line()

        elif args.output_type == "m4a":
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print("Time: " + format_time(audio.info.length) +
                  "\tMPEG-4 Part 14 Audio" + "\t[ " +
                  bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels) + " ]")
            print_line()
            print_yellow("Writing Apple iTunes metadata - " +
                         str(audio.info.codec))
            print_line()

    except id3.error:
        print_yellow("Warning: exception while saving id3 tag: " +
                     str(id3.error))
Esempio n. 19
0
def mp4Tagger( file, metaData ):
  """
  Parse information from the IMDbPY API and write Tag data to MP4 files.

  Arguments:
    file (str): Full path of file to write metadata to.
    metaData (dict): Dictionary of meta data where keys are internal
                  metadata keys and values are metadata values

  Keyword arguments:
    None

  Returns:
    int: Returns following values based on completion.
      -  0 : Completed successfully.
      -  1 : Input was NOT and MP4
      -  2 : IMDb ID was not valid
      -  3 : Failed to download information from IMDb AND themoviedb.org
      -  4 : Writing tags is NOT possible
      -  5 :  Failed when trying to remove tags from file.
      -  6 : Failed when trying to write tags to file.
      - 10 : IMDbPY not installed AND getTMDb_Info failed to import
      - 11 : File is too large

  """

  log = logging.getLogger(__name__)                                                     # Set up a logger
  log.debug( 'Testing file is MP4' )                                                    # Debugging information
  if not file.endswith('.mp4'):                                                         # If the input file does NOT end in '.mp4'
    log.error('Input file is NOT an MP4!!!')
    return 1                                                                            # Print message and return code one (1)

  log.debug( 'Testing file too large' )                                                 # Debugging information
  if os.stat(file).st_size > sys.maxsize:                                               # If the file size is larger than the supported maximum size
    log.error('Input file is too large!')
    return 11                                                                           # Print message and return code eleven (11)

  version = metaData.pop('version', '')
  comment = _updateComment( metaData.get('comment',   '') )
  metaData.update( {'comment' : comment } )
  metaData  = toMP4(metaData)
  if len(metaData) == 0:
    log.warning('No metadata, cannot write tags')
    return 3

  filedir, filebase = os.path.dirname( file ), os.path.basename( file )                 # Get the directory and baseanem of the file

  log.debug('Loading file using mutagen.mp4.MP4')                                       # Debugging information
  handle = mp4.MP4(file)                                                                # Initialize mutagen MP4 handler
  log.debug('Attempting to add tag block to file')                                      # Debugging information
  try:                                                                                  # Try to...
    handle.add_tags()                                                                   # Add new tags to the file
  except mp4.error as e:                                                                # On exception, catch the error
    if 'already exists' in e.__str__():                                                 # If the error is that the tag block already exists
      log.debug('MP4 tags already exist in file.')                                      # Debugging information
      pass                                                                              # Pass
    else:                                                                               # Else, adding is not possible
      log.error('Could NOT add tags to file!')                                          # Log an error
      return 4                                                                          # Return code 4
  try:                                                                                  # Try to...
    handle.delete();                                                                    # Remove all old tags.
  except mp4.error as e:                                                                # On exception, catch the error
    log.error( e.__str__() );                                                           # Log the error
    return 5;                                                                           # Return code 5
  log.debug('Setting basic inforamtion')                                                # Debugging information
  for key, val in metaData.items():
    if key == 'covr' and val != '':
      fmt  = mp4.AtomDataType.PNG if val.endswith('png') else mp4.AtomDataType.JPEG     # Set format for the image
      if os.path.isfile( val ):                                                         # If value is local file
        with open(val, 'rb') as fid: data = fid.read()                                  # Read in data from file
      else:
        log.debug('Attempting to get coverart')                                         # Debugging information
        _, data = downloadCover( file, val, text = version )                            # Download the data
      if data is not None:
        val = [ mp4.MP4Cover( data, fmt ) ]                                             # Add image to file
      else:
        continue
    try:
      handle[key] = val
    except:
      log.warning( 'Failed to write metadata for: {}'.format(key) )

  log.debug('Saving tags to file')                                                      # Debugging information
  try:                                                                                  # Try to...
    handle.save();                                                                      # Save the tags
  except:                                                                               # On exception
    log.error('Failed to save tags to file!');                                          # Log an error
    return 6
  return 0
Esempio n. 20
0
def set_metadata_tags(args, audio_file, idx, track, ripper):
    if args.output_type == "wav" or args.output_type == "pcm":
        print(Fore.YELLOW + "Skipping metadata tagging for " +
              args.output_type + " encoding..." + Fore.RESET)
        return

    # ensure everything is loaded still
    if not track.is_loaded:
        track.load(args.timeout)
    if not track.album.is_loaded:
        track.album.load(args.timeout)
    album_browser = track.album.browse()
    album_browser.load(args.timeout)

    # calculate num of tracks on disc and num of dics
    num_discs = 0
    num_tracks = 0
    for track_browse in album_browser.tracks:
        if (track_browse.disc == track.disc
                and track_browse.index > track.index):
            num_tracks = track_browse.index
        if track_browse.disc > num_discs:
            num_discs = track_browse.disc

    # try to get genres from Spotify's Web API
    genres = None
    if args.genres is not None:
        genres = ripper.web.get_genres(args.genres, track)

    # use mutagen to update id3v2 tags and vorbis comments
    try:
        audio = None
        on_error = 'replace' if args.ascii_path_only else 'ignore'
        album = to_ascii(track.album.name, on_error)
        artists = ", ".join([artist.name for artist in track.artists]) \
            if args.all_artists else track.artists[0].name
        artists_ascii = to_ascii(artists, on_error)
        album_artist = to_ascii(track.album.artist.name, on_error)
        title = to_ascii(track.name, on_error)

        # the comment tag can be formatted
        if args.comment is not None:
            comment = \
                format_track_string(ripper, args.comment, idx, track)
            comment_ascii = to_ascii(comment, on_error)

        if args.grouping is not None:
            grouping = \
                format_track_string(ripper, args.grouping, idx, track)
            grouping_ascii = to_ascii(grouping, on_error)

        if genres is not None and genres:
            genres_ascii = [to_ascii(genre) for genre in genres]

        # cover art image
        image = None
        if args.large_cover_art:
            image = ripper.web.get_large_coverart(track.link.uri)

        # if we fail, use regular cover size
        if image is None:
            image = track.album.cover()
            if image is not None:
                image.load(args.timeout)
                image = image.data

        def tag_to_ascii(_str, _str_ascii):
            return _str if args.ascii_path_only else _str_ascii

        def idx_of_total_str(_idx, _total):
            if _total > 0:
                return "%d/%d" % (_idx, _total)
            else:
                return "%d" % (_idx)

        def save_cover_image(embed_image_func):
            if image is not None:

                def write_image(file_name):
                    cover_path = os.path.dirname(audio_file)
                    cover_file = os.path.join(cover_path, file_name)
                    if not path_exists(cover_file):
                        with open(enc_str(cover_file), "wb") as f:
                            f.write(image)

                if args.cover_file is not None:
                    write_image(args.cover_file)
                elif args.cover_file_and_embed is not None:
                    write_image(args.cover_file_and_embed)
                    embed_image_func(image)
                else:
                    embed_image_func(image)

        def set_id3_tags(audio):
            # add ID3 tag if it doesn't exist
            audio.add_tags()

            def embed_image(data):
                audio.tags.add(
                    id3.APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             desc='Front Cover',
                             data=data))

            save_cover_image(embed_image)

            if album is not None:
                audio.tags.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            audio.tags.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)], encoding=3))
            audio.tags.add(
                id3.TPE1(text=[tag_to_ascii(artists, artists_ascii)],
                         encoding=3))
            if album_artist is not None:
                audio.tags.add(
                    id3.TPE2(text=[
                        tag_to_ascii(track.album.artist.name, album_artist)
                    ],
                             encoding=3))
            audio.tags.add(id3.TDRC(text=[str(track.album.year)], encoding=3))
            audio.tags.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            audio.tags.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                audio.tags.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                audio.tags.add(tcon_tag)

            if args.id3_v23:
                audio.tags.update_to_v23()
                audio.save(v2_version=3, v23_sep='/')
                audio.tags.version = (2, 3, 0)
            else:
                audio.save()

        # aac is not well supported
        def set_id3_tags_raw(audio, audio_file):
            try:
                id3_dict = id3.ID3(audio_file)
            except id3.ID3NoHeaderError:
                id3_dict = id3.ID3()

            def embed_image(data):
                id3_dict.add(
                    id3.APIC(encoding=3,
                             mime='image/jpeg',
                             type=3,
                             desc='Front Cover',
                             data=data))

            save_cover_image(embed_image)

            if album is not None:
                id3_dict.add(
                    id3.TALB(text=[tag_to_ascii(track.album.name, album)],
                             encoding=3))
            id3_dict.add(
                id3.TIT2(text=[tag_to_ascii(track.name, title)], encoding=3))
            id3_dict.add(
                id3.TPE1(text=[tag_to_ascii(artists, artists_ascii)],
                         encoding=3))
            if album_artist is not None:
                id3_dict.add(
                    id3.TPE2(text=[
                        tag_to_ascii(track.album.artist.name, album_artist)
                    ],
                             encoding=3))
            id3_dict.add(id3.TDRC(text=[str(track.album.year)], encoding=3))
            id3_dict.add(
                id3.TPOS(text=[idx_of_total_str(track.disc, num_discs)],
                         encoding=3))
            id3_dict.add(
                id3.TRCK(text=[idx_of_total_str(track.index, num_tracks)],
                         encoding=3))
            if args.comment is not None:
                id3_dict.add(
                    id3.COMM(text=[tag_to_ascii(comment, comment_ascii)],
                             encoding=3))
            if args.grouping is not None:
                audio.tags.add(
                    id3.TIT1(text=[tag_to_ascii(grouping, grouping_ascii)],
                             encoding=3))
            if genres is not None and genres:
                tcon_tag = id3.TCON(encoding=3)
                tcon_tag.genres = genres if args.ascii_path_only \
                    else genres_ascii
                id3_dict.add(tcon_tag)

            if args.id3_v23:
                id3_dict.update_to_v23()
                id3_dict.save(audio_file, v2_version=3, v23_sep='/')
                id3_dict.version = (2, 3, 0)
            else:
                id3_dict.save(audio_file)
            audio.tags = id3_dict

        def set_vorbis_comments(audio):
            # add Vorbis comment block if it doesn't exist
            if audio.tags is None:
                audio.add_tags()

            def embed_image(data):
                pic = flac.Picture()
                pic.type = 3
                pic.mime = "image/jpeg"
                pic.desc = "Front Cover"
                pic.data = data
                if args.output_type == "flac":
                    audio.add_picture(pic)
                else:
                    data = base64.b64encode(pic.write())
                    audio["METADATA_BLOCK_PICTURE"] = [data.decode("ascii")]

            save_cover_image(embed_image)

            if album is not None:
                audio.tags["ALBUM"] = tag_to_ascii(track.album.name, album)
            audio.tags["TITLE"] = tag_to_ascii(track.name, title)
            audio.tags["ARTIST"] = tag_to_ascii(artists, artists_ascii)
            if album_artist is not None:
                audio.tags["ALBUMARTIST"] = \
                    tag_to_ascii(track.album.artist.name, album_artist)
            audio.tags["DATE"] = str(track.album.year)
            audio.tags["YEAR"] = str(track.album.year)
            audio.tags["DISCNUMBER"] = str(track.disc)
            audio.tags["DISCTOTAL"] = str(num_discs)
            audio.tags["TRACKNUMBER"] = str(track.index)
            audio.tags["TRACKTOTAL"] = str(num_tracks)
            if args.comment is not None:
                audio.tags["COMMENT"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags["GROUPING"] = \
                    tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags["GENRE"] = ", ".join(_genres)

            audio.save()

        # only called by Python 3
        def set_mp4_tags(audio):
            # add MP4 tags if it doesn't exist
            if audio.tags is None:
                audio.add_tags()

            def embed_image(data):
                audio.tags["covr"] = mp4.MP4Cover(data)

            save_cover_image(embed_image)

            if album is not None:
                audio.tags["\xa9alb"] = tag_to_ascii(track.album.name, album)
            audio["\xa9nam"] = tag_to_ascii(track.name, title)
            audio.tags["\xa9ART"] = tag_to_ascii(artists, artists_ascii)
            if album_artist is not None:
                audio.tags["aART"] = \
                    tag_to_ascii(track.album.artist.name, album_artist)
            audio.tags["\xa9day"] = str(track.album.year)
            audio.tags["disk"] = [(track.disc, num_discs)]
            audio.tags["trkn"] = [(track.index, num_tracks)]
            if args.comment is not None:
                audio.tags["\xa9cmt"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags["\xa9grp"] = tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags["\xa9gen"] = ", ".join(_genres)

            audio.save()

        def set_m4a_tags(audio):
            # add M4A tags if it doesn't exist
            audio.add_tags()

            def embed_image(data):
                audio.tags[str("covr")] = m4a.M4ACover(data)

            save_cover_image(embed_image)

            if album is not None:
                audio.tags[b"\xa9alb"] = tag_to_ascii(track.album.name, album)
            audio[b"\xa9nam"] = tag_to_ascii(track.name, title)
            audio.tags[b"\xa9ART"] = tag_to_ascii(artists, artists_ascii)
            if album_artist is not None:
                audio.tags[str("aART")] = tag_to_ascii(track.album.artist.name,
                                                       album_artist)
            audio.tags[b"\xa9day"] = str(track.album.year)
            audio.tags[str("disk")] = (track.disc, num_discs)
            audio.tags[str("trkn")] = (track.index, num_tracks)
            if args.comment is not None:
                audio.tags[b"\xa9cmt"] = tag_to_ascii(comment, comment_ascii)
            if args.grouping is not None:
                audio.tags[b"\xa9grp"] = tag_to_ascii(grouping, grouping_ascii)

            if genres is not None and genres:
                _genres = genres if args.ascii_path_only else genres_ascii
                audio.tags[b"\xa9gen"] = ", ".join(_genres)

            audio.save()

        if args.output_type == "flac":
            audio = flac.FLAC(audio_file)
            set_vorbis_comments(audio)
        if args.output_type == "aiff":
            audio = aiff.AIFF(audio_file)
            set_id3_tags(audio)
        elif args.output_type == "ogg":
            audio = oggvorbis.OggVorbis(audio_file)
            set_vorbis_comments(audio)
        elif args.output_type == "opus":
            audio = oggopus.OggOpus(audio_file)
            set_vorbis_comments(audio)
        elif args.output_type == "aac":
            audio = aac.AAC(audio_file)
            set_id3_tags_raw(audio, audio_file)
        elif args.output_type == "m4a":
            audio = mp4.MP4(audio_file)
            set_mp4_tags(audio)
        elif args.output_type == "alac.m4a":
            audio = mp4.MP4(audio_file)
            set_mp4_tags(audio)
        elif args.output_type == "mp3":
            audio = mp3.MP3(audio_file, ID3=id3.ID3)
            set_id3_tags(audio)

        def bit_rate_str(bit_rate):
            brs = "%d kb/s" % bit_rate
            if not args.cbr:
                brs = "~" + brs
            return brs

        def mode_str(mode):
            modes = ["Stereo", "Joint Stereo", "Dual Channel", "Mono"]
            if mode < len(modes):
                return modes[mode]
            else:
                return ""

        def channel_str(num):
            channels = ["", "Mono", "Stereo"]
            if num < len(channels):
                return channels[num]
            else:
                return ""

        # log id3 tags
        print(Fore.YELLOW + "  Artist:\t" + Fore.RESET + artists_ascii)
        if album is not None:
            print(Fore.YELLOW + "  Album:\t" + Fore.RESET + album)
        if album_artist is not None:
            print(Fore.YELLOW + "  Album artist:\t" + Fore.RESET +
                  album_artist)
        print(Fore.YELLOW + "  Title:\t" + Fore.RESET + title)
        print(Fore.YELLOW + "  Track number:\t" + Fore.RESET +
              str(track.index) + "/" + str(num_tracks))
        print(Fore.YELLOW + "  Disc number:\t" + Fore.RESET + str(track.disc) +
              "/" + str(num_discs))
        print(Fore.YELLOW + "  Year:\t\t" + Fore.RESET + str(track.album.year))
        if genres is not None and genres:
            print(Fore.YELLOW + "  Genres:\t" + Fore.RESET +
                  " / ".join(genres_ascii))
        if image is not None:
            print(Fore.YELLOW + "  Cover image:\t" + Fore.RESET + "Yes")
        if args.comment is not None:
            print(Fore.YELLOW + "  Comment:\t" + Fore.RESET + comment_ascii)
        if args.grouping is not None:
            print(Fore.YELLOW + "  Grouping:\t" + Fore.RESET + grouping_ascii)
        print(Fore.YELLOW + "  Time:\t\t" + Fore.RESET +
              format_time(audio.info.length))
        print(Fore.YELLOW + "  Size:\t\t" + Fore.RESET +
              format_size(os.stat(enc_str(audio_file))[ST_SIZE]))

        if args.output_type == "flac":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET +
                  "Free Lossless Audio Codec")
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            print(Fore.YELLOW + "  Comment:\t" + Fore.RESET +
                  audio.tags.vendor)

        if args.output_type == "aiff":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET +
                  "Audio Interchange File Format")
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print(Fore.YELLOW + "  ID3:\t\t" + Fore.RESET + id3_version +
                  " - " + str(len(audio.tags.values())) + " frames")

        elif args.output_type == "alac.m4a":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET + "Apple Lossless")
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            print(Fore.YELLOW + "  iTunes:\t" + Fore.RESET +
                  str(audio.info.codec))

        elif args.output_type == "ogg":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET +
                  "Ogg Vorbis Codec")
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            print(Fore.YELLOW + "  Comment:\t" + Fore.RESET +
                  audio.tags.vendor)

        elif args.output_type == "opus":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET + "Opus Codec")
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  channel_str(audio.info.channels))
            print(Fore.YELLOW + "  Comment:\t" + Fore.RESET +
                  audio.tags.vendor)

        elif args.output_type == "mp3":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET + "MPEG" +
                  str(audio.info.version) + ", Layer " +
                  ("I" * audio.info.layer))
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  mode_str(audio.info.mode))
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print(Fore.YELLOW + "  ID3:\t\t" + Fore.RESET + id3_version +
                  " - " + str(len(audio.tags.values())) + " frames")

        elif args.output_type == "aac":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET +
                  "Advanced Audio Coding")
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(audio.info.bitrate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            id3_version = "v%d.%d" % (audio.tags.version[0],
                                      audio.tags.version[1])
            print(Fore.YELLOW + "  ID3:\t\t" + Fore.RESET + id3_version +
                  " - " + str(len(audio.tags.values())) + " frames")

        elif args.output_type == "m4a":
            print(Fore.YELLOW + "  Format:\t" + Fore.RESET +
                  "MPEG-4 Part 14 Audio")
            bit_rate = ((audio.info.bits_per_sample * audio.info.sample_rate) *
                        audio.info.channels)
            print(Fore.YELLOW + "  Quality:\t" + Fore.RESET +
                  bit_rate_str(bit_rate / 1000) + " @ " +
                  str(audio.info.sample_rate) + " Hz - " +
                  channel_str(audio.info.channels))
            print(Fore.YELLOW + "  iTunes:\t" + Fore.RESET +
                  str(audio.info.codec))

    except id3.error:
        print(Fore.YELLOW + "Warning: exception while saving id3 tag: " +
              str(id3.error) + Fore.RESET)