Exemplo n.º 1
0
    def test_hash(self):
        frame = COMM(encoding=0, lang="foo", desc="d")
        self.assertEqual(frame.HashKey, "COMM:d:foo")
        frame._pprint()

        self.assertEquals(COMM(text="a").HashKey, COMM(text="b").HashKey)
        self.assertNotEquals(COMM(desc="a").HashKey, COMM(desc="b").HashKey)
        self.assertNotEquals(
            COMM(lang="abc").HashKey, COMM(lang="def").HashKey)
Exemplo n.º 2
0
def copyTagsToTranscodedFileMp3(losslessFile, lossyFile):
    #
    # Copy the tags from the losslessFile (.flac) to the lossyFile.
    # All previous tags from the lossyFile will be deleted before the
    # tags from the losslessFile are copied.
    #
    from mutagen.flac import FLAC
    from mutagen.id3 import ID3

    # Read all tags from the flac file
    flacFile = FLAC(losslessFile)
    flacFileTags = flacFile.tags  # Returns a dictionary containing the flac tags

    # Only mp3 files with ID3 headers can be openend.
    # So be sure to add some tags during encoding .wav. to mp3

    # Mapping from Vorbis comments field recommendations to id3v2_4_0
    # For more information about vorbis field recommendations: http://reactor-core.org/ogg-tagging.html
    # For more information about id3v2_4_0 frames: http://www.id3.org/id3v2.4.0-frames
    #
    # Single value tags:
    #  ALBUM              -> TALB
    #  ARTIST             -> TPE1
    #  PUBLISHER          -> TPUB
    #  COPYRIGHT          -> WCOP
    #  DISCNUMBER         -> TPOS
    #  ISRC               -> TSRC
    #  EAN/UPN
    #  LABEL
    #  LABELNO
    #  LICENSE             -> TOWN
    #  OPUS                -> TIT3
    #  SOURCEMEDIA         -> TMED
    #  TITLE               -> TIT2
    #  TRACKNUMBER         -> TRCK
    #  VERSION
    #  ENCODED-BY          -> TENC
    #  ENCODING
    # Multiple value tags:
    #  COMPOSER            -> TCOM
    #  ARRANGER
    #  LYRICIST            -> TEXT
    #  AUTHOR              -> TEXT
    #  CONDUCTOR           -> TPE3
    #  PERFORMER           ->
    #  ENSEMBLE            -> TPE2
    #  PART                -> TIT1
    #  PARTNUMBER          -> TIT1
    #  GENRE               -> TCON
    #  DATE                -> TDRC
    #  LOCATION
    #  COMMENT             -> COMM
    # Other vorbis tags are mapped to TXXX tags

    mp3File = ID3(lossyFile)
    mp3File.delete()

    for key, value in flacFileTags.items():
        if key == 'title':
            # Map to TIT2 frame
            from mutagen.id3 import TIT2
            mp3File.add(TIT2(encoding=3, text=value))
        elif key == 'album':
            # Map to TALB frame
            from mutagen.id3 import TALB
            mp3File.add(TALB(encoding=3, text=value))
        elif key == 'artist':
            # Map to TPE1 frame
            from mutagen.id3 import TPE1
            mp3File.add(TPE1(encoding=3, text=value))
        elif key == 'tracknumber':
            # Map to TRCK frame
            from mutagen.id3 import TRCK
            mp3File.add(TRCK(encoding=3, text=value))
        elif key == 'date':
            # Map to TDRC frame
            from mutagen.id3 import TDRC
            mp3File.add(TDRC(encoding=3, text=value))
        elif key == 'genre':
            # Map to TCON frame
            from mutagen.id3 import TCON
            mp3File.add(TCON(encoding=3, text=value))
        elif key == 'discnumber':
            # Map to TPOS frame
            from mutagen.id3 import TPOS
            mp3File.add(TPOS(encoding=3, text=value))
        elif key == 'composer':
            # Map to TCOM frame
            from mutagen.id3 import TCOM
            mp3File.add(TCOM(encoding=3, text=value))
        elif key == 'conductor':
            # Map to TPE3 frame
            from mutagen.id3 import TPE3
            mp3File.add(TPE3(encoding=3, text=value))
        elif key == 'ensemble':
            # Map to TPE2 frame
            from mutagen.id3 import TPE2
            mp3File.add(TPE2(encoding=3, text=value))
        elif key == 'comment':
            # Map to COMM frame
            from mutagen.id3 import COMM
            mp3File.add(COMM(encoding=3, text=value))
        elif key == 'publisher':
            # Map to TPUB frame
            from mutagen.id3 import TPUB
            mp3File.add(TPUB(encoding=3, text=value))
        elif key == 'opus':
            # Map to TIT3 frame
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        elif key == 'sourcemedia':
            # Map to TMED frame
            from mutagen.id3 import TMED
            mp3File.add(TMED(encoding=3, text=value))
        elif key == 'isrc':
            # Map to TSRC frame
            from mutagen.id3 import TSRC
            mp3File.add(TSRC(encoding=3, text=value))
        elif key == 'license':
            # Map to TOWN frame
            from mutagen.id3 import TOWN
            mp3File.add(TOWN(encoding=3, text=value))
        elif key == 'copyright':
            # Map to WCOP frame
            from mutagen.id3 import WCOP
            mp3File.add(WCOP(encoding=3, text=value))
        elif key == 'encoded-by':
            # Map to TENC frame
            from mutagen.id3 import TENC
            mp3File.add(TENC(encoding=3, text=value))
        elif (key == 'part' or key == 'partnumber'):
            # Map to TIT3 frame
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        elif (key == 'lyricist' or key == 'textwriter'):
            # Map to TEXT frame
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        else:  #all other tags are mapped to TXXX frames
            # Map to TXXX frame
            from mutagen.id3 import TXXX
            mp3File.add(TXXX(encoding=3, text=value, desc=key))

        mp3File.update_to_v24()
        mp3File.save()

    return
Exemplo n.º 3
0
 def test_make_from_empty(self):
     empty = b'TAG' + b'\x00' * 124 + b'\xff'
     self.assertEquals(MakeID3v1({}), empty)
     self.assertEquals(MakeID3v1({'TCON': TCON()}), empty)
     self.assertEquals(MakeID3v1({'COMM': COMM(encoding=0, text="")}),
                       empty)
Exemplo n.º 4
0
 def test_multi_COMM(self):
     from mutagen.id3 import COMM
     self.assertEquals(COMM(encoding=0, text="a").HashKey, COMM(encoding=0, text="b").HashKey)
     self.assertNotEquals(COMM(encoding=0, desc="a").HashKey, COMM(encoding=0, desc="b").HashKey)
     self.assertNotEquals(
         COMM(lang="abc").HashKey, COMM(lang="def").HashKey)
Exemplo n.º 5
0
class MediaHelper:
    config = None
    tagSep = None
    maxTags = None
    overwriteFields = None
    forceOverwriteFields = None
    id3v1Handling = None
    useBothArtistFields = False
    artistFieldPref = []

    formatFieldMap = dict(id3=dict(genre='TCON',
                                   grouping='TIT1',
                                   comment="COMM::'eng'",
                                   artist='TPE1',
                                   albumartist='TPE2',
                                   album='TALB',
                                   track='TIT2'),
                          mp4=dict(genre='\xa9gen',
                                   grouping='\xa9grp',
                                   comment='\xa9cmt',
                                   artist='\xa9ART',
                                   albumartist='aART',
                                   album='\xa9alb',
                                   track='\xa9nam'),
                          oggvorbis=dict(genre='genre',
                                         grouping='grouping',
                                         comment='comment',
                                         artist='artist',
                                         albumartist='album artist',
                                         album='album',
                                         track='title'),
                          flac=dict(genre='genre',
                                    grouping='grouping',
                                    comment='comment',
                                    artist='artist',
                                    albumartist='album artist',
                                    album='album',
                                    track='title'))

    id3FuncMap = dict(
        genre=lambda val: TCON(encoding=3, text=val),
        grouping=lambda val: TIT1(encoding=3, text=val),
        comment=lambda val: COMM(encoding=3, lang='eng', desc='', text=val),
        artist=lambda val: TPE1(encoding=3, text=val),
        albumartist=lambda val: TPE2(encoding=3, text=val),
        album=lambda val: TALB(encoding=3, text=val),
        track=lambda val: TIT2(encoding=3, text=val))

    meaninglessArtists = frozenset([
        'various artists', 'soundtrack', 'soundtracks', 'original soundtrack',
        'ost', 'compilation'
    ])

    def __init__(self, config):
        self.config = config
        self.tagSep = self.config.get('tagSep')
        if (len(self.tagSep.strip()) > 0):
            self.tagSep += ' '
        self.maxTags = dict(genre=self.config.getint('genreMaxTags'),
                            grouping=self.config.getint('groupingMaxTags'),
                            comment=self.config.getint('commentMaxTags'))
        self.overwriteFields = set(
            map(string.strip,
                self.config.get('overwriteFields').lower().split(',')))
        self.forceOverwriteFields = set(
            map(string.strip,
                self.config.get('forceOverwriteFields').lower().split(',')))
        self.id3v1Handling = self.config.getint('id3v1Handling')

        self.artistFieldPref = ['albumartist', 'artist']
        if (self.config.get('artistField').lower() == 'both'):
            self.useBothArtistFields = True
        elif (self.config.get('artistField').lower() == 'artist'):
            self.artistFieldPref.reverse()

    def getMediawrapper(self, filename):
        root, ext = os.path.splitext(filename.lower())
        if (ext == '.mp3'): mediawrapper = ID3(filename)
        elif (ext == '.m4a'): mediawrapper = MP4(filename)
        elif (ext == '.ogg'): mediawrapper = OggVorbis(filename)
        elif (ext == '.flac'): mediawrapper = FLAC(filename)
        else: mediawrapper = mutagen.File(filename)
        return mediawrapper

    def extractMetadata(self, filename):
        try:
            mediawrapper = self.getMediawrapper(filename)

            if (isinstance(mediawrapper, ID3)):
                return self.extractMetadataHelper(mediawrapper,
                                                  self.formatFieldMap['id3'],
                                                  filename)
            elif (isinstance(mediawrapper, MP4)):
                return self.extractMetadataHelper(mediawrapper,
                                                  self.formatFieldMap['mp4'],
                                                  filename)
            elif (isinstance(mediawrapper, OggVorbis)):
                return self.extractMetadataHelper(
                    mediawrapper, self.formatFieldMap['oggvorbis'], filename)
            elif (isinstance(mediawrapper, FLAC)):
                return self.extractMetadataHelper(mediawrapper,
                                                  self.formatFieldMap['flac'],
                                                  filename)
            else:
                if (self.config.getboolean('verbose')):
                    common.safeStdout(
                        '\tSkipping unknown/incompatible media file type [' +
                        filename + ']')
        except Exception, err:
            common.safeStderr('Error seen during media reading: ' + str(err))
        return None
Exemplo n.º 6
0
    def download(self):
        """
        Starts the download process for this track. Also writes the file and
        applies ID3 tags if specified. Requires the track to have been prepared
        by the prepare method beforehand.
        """

        if not self.album:
            safe_print('\nWriting file to {}'.format(self.output))

        # Clean up the main title.
        if not self.short:
            clean_title = format_information(self.title, self.artist,
                                             self.album, self.index)

        else:
            clean_title = short_information(self.title, self.index)

        # Download the file.
        status = download_file(self.mp3_url,
                               self.output,
                               clean_title + ".mp3",
                               verbose=self.verbose,
                               silent=self.silent,
                               sleep=self.sleep)

        # Abort further processes if we receive an error status code.
        if not status or status > 2:
            if not self.silent:
                print('\nFailed to download the file. Error code {}'.format(
                    status))

            return status

        # Write ID3 tags if the id3_enabled is true.
        if self.id3_enabled:
            # Fix ID3 tags. Create ID3 tags if not present.
            try:
                tags = ID3(
                    os.path.join(self.output,
                                 safe_filename(clean_title + ".mp3")))

            except ID3NoHeaderError:
                tags = ID3()

            # Title and artist tags. Split the title if it contains the artist tag.
            if " - " in self.title:
                split_title = str(self.title).split(" - ", 1)

                tags["TPE1"] = TPE1(encoding=3, text=str(split_title[0]))
                tags["TIT2"] = TIT2(encoding=3, text=str(split_title[1]))

            else:
                tags["TIT2"] = TIT2(encoding=3, text=str(self.title))

                tags["TPE1"] = TPE1(encoding=3, text=str(self.artist))

            # Album tag. Make sure we have it.
            if self.album:
                tags["TALB"] = TALB(encoding=3, text=str(self.album))

            # Track index tag.
            if self.index:
                tags["TRCK"] = TRCK(encoding=3, text=str(self.index))

            # Track date.
            if self.date:
                tags["TDRC"] = TDRC(encoding=3, text=str(self.date))

            # Album artist
            if not self.album_artist:
                self.album_artist = self.artist

            tags["TPE2"] = TPE2(encoding=3, text=str(self.album_artist))

            # Retrieve the base page URL.
            base_url = "{}//{}".format(
                str(self.url).split("/")[0],
                str(self.url).split("/")[2])

            # Add the Bandcamp base comment in the ID3 comment tag.
            tags["COMM"] = COMM(encoding=3,
                                lang='XXX',
                                desc=u'',
                                text=u'Visit {}'.format(base_url))

            # Save all tags to the track.
            tags.save(
                os.path.join(self.output, safe_filename(clean_title + ".mp3")))

        # Download artwork if it is enabled.
        if self.art_enabled:
            status = download_file(self.art_url, self.output,
                                   clean_title + self.art_url[-4:])

            if status == 1:
                if self.verbose:
                    safe_print('\nSaved track art to {}{}{}'.format(
                        self.output, clean_title, self.art_url[-4:]))

            elif status == 2:
                if self.verbose:
                    print('\nArtwork already found.')

            elif not self.silent:
                print('\nFailed to download the artwork. Error code {}'.format(
                    status))
Exemplo n.º 7
0
    def test_COMM(self):
        from mutagen.id3 import COMM

        frame = COMM(encoding=0, lang="foo", desc="d")
        self.assertEqual(frame.HashKey, "COMM:d:foo")
        frame._pprint()
Exemplo n.º 8
0
 def set_comment(filename, comment):
     audio = ID3(filename)
     audio.add(COMM(encoding=3, text=comment))
     audio.save()
Exemplo n.º 9
0
def download(broadcast, targetDir, reliveUrlTemplate):
    broadcastStartDT = parse(broadcast['start'])
    broadcastEndDT = parse(broadcast['end'])

    # build filename from channel, show title and broadcast datetime, while escaping "bad" characters
    filename = os.path.join(
        targetDir,
        re.sub(
            '[^\w\s\-\.\[\]]', '_',
            broadcast['trackingInfos']['pageVars']['broadcast_service'] + ' ' +
            broadcastStartDT.astimezone(
                pytz.timezone('Europe/Berlin')).strftime("%Y-%m-%d %H:%M") +
            ' ' + broadcast['trackingInfos']['pageVars']['topline']) + ".mp3")

    # skip broadcast if file is already exists
    if os.path.isfile(filename) and os.path.getsize(filename) > 0:
        print("%s already exists, skipping." % filename, flush=True)
        return

    # get links to all audio segments of this broadcast
    segmentUrls = getSegmentUrls(broadcastStartDT, broadcastEndDT,
                                 reliveUrlTemplate)
    if segmentUrls is None:
        # skip broadcast if no segments available
        print("Skipping %s, not yet in relive" % filename)
        return

    # dowload all ts segments, and convert them to mp3
    print("Downloading %s ..." % filename, end=" ", flush=True)

    try:
        sound = AudioSegment.empty()
        for i in segmentUrls:
            sound += AudioSegment.from_file(BytesIO(urlopen(i).read()))
        sound.export(filename, format="mp3")
    except:
        print("failed.", flush=True)
        return
    else:
        print("done.", flush=True)

    # ID3: remove all tags
    try:
        tags = ID3(filename)
        tags.delete()
    except ID3NoHeaderError:
        tags = ID3()

    # ID3: save as much information as possible in the ID3 tags
    tags.add(
        TRSN(
            text=[broadcast['trackingInfos']['pageVars']['broadcast_service']
                  ]))
    tags.add(
        TPE1(
            text=[broadcast['trackingInfos']['pageVars']['broadcast_service']
                  ]))
    tags.add(
        TALB(text=[
            " - ".join(
                list(
                    dict.fromkeys([
                        broadcast['trackingInfos']['pageVars']['topline'],
                        broadcast['trackingInfos']['pageVars']['title']
                    ])))
        ]))
    tags.add(TRCK(text=['1/1']))
    #tags.add(TIT2(text=[broadcastStartDT.astimezone(pytz.timezone('Europe/Berlin')).strftime("%Y-%m-%d %H:%M")]))
    tags.add(TIT2(text=[broadcast['publicationOf']['title']]))
    tags.add(
        COMM(lang="deu",
             desc="desc",
             text=[broadcast['publicationOf']['description']]))
    tags.add(
        TYER(text=[
            broadcastStartDT.astimezone(pytz.timezone(
                'Europe/Berlin')).strftime("%Y")
        ]))
    tags.add(
        TDAT(text=[
            broadcastStartDT.astimezone(pytz.timezone(
                'Europe/Berlin')).strftime("%d%m")
        ]))
    tags.add(
        TIME(text=[
            broadcastStartDT.astimezone(pytz.timezone(
                'Europe/Berlin')).strftime("%H%M")
        ]))
    tags.add(
        TLEN(text=[
            int((broadcastEndDT - broadcastStartDT).total_seconds() * 1000)
        ]))
    tags.add(WOAS(url=broadcast['publicationOf']['canonicalUrl']))
    tags.add(WORS(url="https://www.br.de/radio/"))

    # ID3: chapters
    chapterNr = 0
    for chapter in broadcast['items']:
        chapterStartDT = parse(chapter['start'])

        if 'duration' in chapter and chapter['duration'] is not None:
            chapterEndDT = chapterStartDT + timedelta(
                seconds=chapter['duration'])
        else:
            chapterEndDT = broadcastEndDT

        artists = []
        for i in ['performer', 'author']:
            if i in chapter and chapter[i] is not None and len(chapter[i]) > 0:
                artists.append(chapter[i])

        titles = []
        for i in ['title']:
            if i in chapter and chapter[i] is not None and len(chapter[i]) > 0:
                titles.append(chapter[i])

        tags.add(
            CHAP(element_id=chapterNr,
                 start_time=floor(
                     (chapterStartDT - broadcastStartDT).total_seconds() *
                     1000),
                 end_time=ceil(
                     (chapterEndDT - broadcastStartDT).total_seconds() * 1000),
                 sub_frames=[
                     TIT2(text=[
                         " - ".join([" ".join(artists), " ".join(titles)])
                     ])
                 ]))
        chapterNr += 1

    tocList = ",".join([str(i) for i in range(0, chapterNr)])

    tags.add(
        CTOC(element_id="toc",
             flags=CTOCFlags.TOP_LEVEL | CTOCFlags.ORDERED,
             child_element_ids=[tocList],
             sub_frames=[TIT2(text=["Table Of Contents"])]))

    # ID3: cover image
    response = requests.get(
        broadcast['publicationOf']['defaultTeaserImage']['url'], timeout=5)
    if response.status_code == 200:
        tags.add(
            APIC(mime=response.headers['content-type'],
                 desc="Front Cover",
                 data=response.content))

    # save ID3 tags
    tags.save(filename, v2_version=3)
Exemplo n.º 10
0
has_tdrc = False
for k, v in id3_tags.items():
    if 'COMM' in k:
        has_comm = True
        comm = v
    if 'TDRC' in k:
        has_tdrc = True
        tdrc = v
# for

# conv comment encoding
if has_comm:
    comm_list = comm.text
    comm_text = comm_list[0]
    comm_decode = comm_text.encode('latin1').decode('cp932')
    print('comm converted: ',  comm_decode)
    id3_tags[COMM] = COMM(encoding=3, text = comm_decode)
# if

# conv TRDC to TYER for v2.3.0
if has_tdrc:
    tdrc_list = tdrc.text
    tdrc_text = tdrc_list[0]
    id3_tags.add( TYER(encoding=3, text = str(tdrc_text)) )
# if

id3_tags.save(v2_version=3)

print( 'converted: %s ' % filepath_utf16)

Exemplo n.º 11
0
def CopyTagsToTranscodedFileMp3(losslessFile, lossyFile):  
    # Because the input flac file is decoded to wav, all metadata is lost. We have to extract this metadata from 
    # the flac file and put it directly into the generated mp3 file.
    from mutagen.flac import FLAC
    from mutagen.id3 import ID3   
    
    flacFile = FLAC(losslessFile)
    flacFileTags = flacFile.tags 
          
    mp3File = ID3(lossyFile)    
    mp3File.delete()    
        
    for key,value in flacFileTags.items():
        if key == 'title': 
            from mutagen.id3 import TIT2
            mp3File.add(TIT2(encoding=3, text=value)) 
        elif key == 'album': 
            from mutagen.id3 import TALB
            mp3File.add(TALB(encoding=3, text=value))
        elif key == 'artist': 
            from mutagen.id3 import TPE1
            mp3File.add(TPE1(encoding=3, text=value)) 
        elif key == 'tracknumber': 
            from mutagen.id3 import TRCK
            mp3File.add(TRCK(encoding=3, text=value))
        elif key == 'date': 
            from mutagen.id3 import TDRC
            mp3File.add(TDRC(encoding=3, text=value))
        elif key == 'genre': 
            from mutagen.id3 import TCON
            mp3File.add(TCON(encoding=3, text=value))
        elif key == 'discnumber': 
            from mutagen.id3 import TPOS
            mp3File.add(TPOS(encoding=3, text=value))
        elif key == 'composer': 
            from mutagen.id3 import TCOM
            mp3File.add(TCOM(encoding=3, text=value))
        elif key == 'conductor': 
            from mutagen.id3 import TPE3
            mp3File.add(TPE3(encoding=3, text=value))
        elif key == 'ensemble': 
            from mutagen.id3 import TPE2
            mp3File.add(TPE2(encoding=3, text=value))      
        elif key == 'comment': 
            from mutagen.id3 import COMM
            mp3File.add(COMM(encoding=3, text=value))
        elif key == 'publisher': 
            from mutagen.id3 import TPUB
            mp3File.add(TPUB(encoding=3, text=value))
        elif key == 'opus': 
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        elif key == 'sourcemedia': 
            from mutagen.id3 import TMED
            mp3File.add(TMED(encoding=3, text=value))
        elif key == 'isrc': 
            from mutagen.id3 import TSRC
            mp3File.add(TSRC(encoding=3, text=value))
        elif key == 'license': 
            from mutagen.id3 import TOWN
            mp3File.add(TOWN(encoding=3, text=value))
        elif key == 'copyright': 
            from mutagen.id3 import WCOP
            mp3File.add(WCOP(encoding=3, text=value))
        elif key == 'encoded-by': 
            from mutagen.id3 import TENC
            mp3File.add(TENC(encoding=3, text=value))
        elif (key == 'part' or key == 'partnumber'): 
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        elif (key == 'lyricist' or key == 'textwriter'): 
            from mutagen.id3 import TIT3
            mp3File.add(TIT3(encoding=3, text=value))
        else: 
            from mutagen.id3 import TXXX
            mp3File.add(TXXX(encoding=3, text=value, desc=key))        
      
        mp3File.update_to_v24()
        mp3File.save() 
      
    return
Exemplo n.º 12
0
     list = (json_obj['album'][0])
     comment = list['strDescriptionEN']
 except:
     try:
         comment = wikipedia.summary(query, sentences=4)
         comment = comment + "Wikipedia"
     except wikipedia.exceptions.PageError:
         print(colored('NO', 'red') + ',Album Description. Please Check')
         comment = "Please Add Information"
 for name in os.listdir(paths):
     if name.endswith(filetypes):
         filename = os.path.join(paths, name)
         if filename.endswith('.mp3'):
             #Write Comment To MP3
             audio = ID3(filename)
             audio.add(COMM(encoding=3, text=comment))
             audio.save()
             Easy3 = EMP3(filename)
             tag = TinyTag.get(filename)
             albumid = Easy3["musicbrainz_releasegroupid"][0]
             mbztrackids = Easy3["musicbrainz_trackid"][0]
             title = Easy3["title"][0]
             tpos = tag.track
             duration = tag.duration
             duration = math.ceil(duration)
             duration = time.strftime("%M:%S", time.gmtime(duration))
             mbztrackid.append(mbztrackids)
             tracknames.append(title)
             trackpositions.append(tpos)
             trackdurations.append(duration)
         elif filename.endswith('.flac'):
Exemplo n.º 13
0
    # write ID3 Tag
    for partNo in range(len(showInfo['parts'])):
        # set ID3 tags
        try:
            tags = ID3(showInfo['parts'][partNo]['filepath'] + ".part")
            tags.delete()
        except ID3NoHeaderError:
            tags = ID3()

        tags.add(TRSN(text=[stationInfo['name']]))
        tags.add(TPE1(text=[stationInfo['name']]))
        tags.add(TALB(text=[showInfo['name']]))
        tags.add(
            TRCK(text=[str(partNo + 1) + "/" + str(len(showInfo['parts']))]))
        tags.add(TIT2(text=[showInfo['parts'][partNo]['title']]))
        tags.add(COMM(lang="deu", desc="desc", text=[showInfo['description']]))
        tags.add(TYER(text=[showInfo['start_dt'].strftime("%Y")]))
        tags.add(TDAT(text=[showInfo['start_dt'].strftime("%d%m")]))
        tags.add(TIME(text=[showInfo['start_dt'].strftime("%H%M")]))
        tags.add(TLEN(text=[showInfo['parts'][partNo]['duration_ms']]))
        tags.add(WOAS(url=showInfo['website']))
        tags.add(WORS(url=stationInfo['website']))

        for chapter in showInfo['parts'][partNo]['chapters']:
            tags.add(
                CHAP(element_id=chapter["id"],
                     start_time=chapter["start_ms"],
                     end_time=chapter["end_ms"],
                     sub_frames=[TIT2(text=[chapter["title"]])]))

        tocList = ",".join([
Exemplo n.º 14
0
                                     lang=u'eng',
                                     desc=u'desc',
                                     text=lyrics))
        print 'Added USLT frame to', fn

        # set title from filename; adjust to your needs
        if SET_OTHER_ID3_TAGS:
            title = unicode(os.path.splitext(os.path.split(fn)[-1])[0])
            print title,
            print fname
            # title
            tags["TIT2"] = TIT2(encoding=3, text=title)
            tags["TALB"] = TALB(encoding=3, text=u'mutagen Album Name')
            tags["TPE2"] = TPE2(encoding=3, text=u'mutagen Band')
            tags["COMM"] = COMM(encoding=3,
                                lang=u'eng',
                                desc='desc',
                                text=u'mutagen comment')
            # artist
            tags["TPE1"] = TPE1(encoding=3, text=u'mutagen Artist')
            # composer
            tags["TCOM"] = TCOM(encoding=3, text=u'mutagen Composer')
            # genre
            tags["TCON"] = TCON(encoding=3, text=u'mutagen Genre')
            tags["TDRC"] = TDRC(encoding=3, text=u'2010')
            #use to set track number
            #tags["TRCK"] = COMM(encoding=3, text=track_number)
        tags.save(fname)

print 'Done'
Exemplo n.º 15
0
    def MP3Tagger(self):
        Caminho = self.lineEdit.text()
        if Caminho == "":
            QMessageBox.about(self, "ERRO", "Nenhum caminho especificado.")
        else:
            File = glob.glob(Caminho + "/*.mp3")
            for i in File:  # Caminho completo do arquivo com extensão
                self.label_10.setText(_translate("MainWindow", ""))
                QtWidgets.QApplication.processEvents()
                Path = os.path.dirname(i)  # Caninho completo da pasta do arquivo
                Name1 = os.path.basename(i)  # Nome do arquivo completo com extensão
                Name2 = os.path.splitext(Name1)  # Nome do arquivo dividido na extensão
                Name3 = Name2[0].split(" - ")  # Nome do arquivo divido no artista e musica
                Name4 = ''.join(i for i in Name3[0] if not i.isdigit())  # Nome da música sem os números
                print("Caminho: " + i)
                print("Name1: " + str(Name1))
                print("Name2: " + str(Name2))
                print("Name3: " + str(Name3))
                print("Name4: " + str(Name4))
                self.label_10.setText(_translate("MainWindow", "Renomeando arquivo..."))
                QtWidgets.QApplication.processEvents()
                os.rename(i, Path + "\\" + Name3[1] + " -" + Name4 + ".mp3")  # Renomeia o arquivo
                self.label_10.setText(_translate("MainWindow", "Buscando metadados no banco de dados do Spotify..."))
                QtWidgets.QApplication.processEvents()
                meta_tags = spotify_tools.generate_metadata(Name3[1] + " -" + Name4)  # Gera as tags do mp3
                if meta_tags == None:
                    continue
                else:
                    self.label_6.setText(_translate("MainWindow", str(meta_tags['artists'][0]['name'])))
                    self.label_7.setText(_translate("MainWindow", str(meta_tags['name'])))
                    self.label_8.setText(_translate("MainWindow", str(meta_tags['album']['name'])))
                    self.label_10.setText(_translate("MainWindow", "Aplicando tags..."))
                    ScriptFolder = os.path.dirname(os.path.realpath(sys.argv[0]))
                    IMG = open(ScriptFolder + "\\" + 'cover2.jpg', 'wb')
                    IMG.write(urllib.request.urlopen(meta_tags['album']['images'][0]['url']).read())
                    IMG.close()
                    time.sleep(1)
                    self.GenericCover3 = QPixmap('cover2.jpg')
                    self.GenericCover4 = self.GenericCover3.scaled(141, 141)
                    self.graphicsView.setPixmap(self.GenericCover4)
                    QtWidgets.QApplication.processEvents()
                    audiofile = MP3(Path + "\\" + Name3[1] + " -" + Name4 + ".mp3")
                    audiofile.tags = None  # Exclui qualquer tag antes de aplicar as novas (previne erro)
                    audiofile.add_tags(ID3=EasyID3)
                    audiofile['artist'] = meta_tags['artists'][0]['name']
                    audiofile['albumartist'] = meta_tags['artists'][0]['name']
                    audiofile['album'] = meta_tags['album']['name']
                    audiofile['title'] = meta_tags['name']
                    audiofile['tracknumber'] = [meta_tags['track_number'],
                                                meta_tags['total_tracks']]
                    audiofile['discnumber'] = [meta_tags['disc_number'], 0]
                    audiofile['date'] = meta_tags['release_date']
                    audiofile['originaldate'] = meta_tags['release_date']
                    audiofile['media'] = meta_tags['type']
                    audiofile['author'] = meta_tags['artists'][0]['name']
                    audiofile['lyricist'] = meta_tags['artists'][0]['name']
                    audiofile['arranger'] = meta_tags['artists'][0]['name']
                    audiofile['performer'] = meta_tags['artists'][0]['name']
                    audiofile['website'] = meta_tags['external_urls']['spotify']
                    audiofile['length'] = str(meta_tags['duration_ms'] / 1000.0)
                    if meta_tags['publisher']:
                        audiofile['encodedby'] = meta_tags['publisher']
                    if meta_tags['genre']:
                        audiofile['genre'] = meta_tags['genre']
                    if meta_tags['copyright']:
                        audiofile['copyright'] = meta_tags['copyright']
                    if meta_tags['external_ids']['isrc']:
                        audiofile['isrc'] = meta_tags['external_ids']['isrc']
                    audiofile.save(v2_version=3)

                    # For supported id3 tags:
                    # https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py
                    # Each class represents an id3 tag
                    audiofile = ID3(Path + "\\" + Name3[1] + " -" + Name4 + ".mp3")
                    year, *_ = meta_tags['release_date'].split('-')
                    audiofile['TORY'] = TORY(encoding=3, text=year)
                    audiofile['TYER'] = TYER(encoding=3, text=year)
                    audiofile['TPUB'] = TPUB(encoding=3, text=meta_tags['publisher'])
                    audiofile['COMM'] = COMM(encoding=3, text=meta_tags['external_urls']['spotify'])
                    if meta_tags['lyrics']:
                        audiofile['USLT'] = USLT(encoding=3, desc=u'Lyrics', text=meta_tags['lyrics'])
                    try:
                        albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url'])
                        audiofile['APIC'] = APIC(encoding=3, mime='image/jpeg', type=3,
                                                 desc=u'Cover', data=albumart.read())
                        albumart.close()
                    except IndexError:
                        pass
                    audiofile.save(v2_version=3)
                    self.label_10.setText(_translate("MainWindow", "Concluído."))
                    QtWidgets.QApplication.processEvents()
                    time.sleep(2)  # pausa dramática
                    # Some com os textos:
                    self.label_10.setText(_translate("MainWindow", ""))
                    QtWidgets.QApplication.processEvents()
            QMessageBox.about(self, "Concluído", "Operação concluída.")
Exemplo n.º 16
0
 def comments(self, comments: str):
     self.file['COMM'] = COMM(encoding=3, text=comments)
     return self
Exemplo n.º 17
0
#!/usr/bin/python3
from mutagen.id3 import ID3NoHeaderError
from mutagen.id3 import ID3, COMM
filename = input("Path to file: ")
aid = input("Artist ID: ")

try:
    tags = ID3(filename)
except ID3NoHeaderError:
    tags = ID3()

tags["COMM"] = COMM(encoding=3, lang=u'eng', desc='desc', text="a" + str(aid))

# TODO: Insert function to check sanity of user against database

tags.save(filename)
Exemplo n.º 18
0
def run():
    parser = argparse.ArgumentParser(
        prog='odmpy',
        description='Download/return an Overdrive loan audiobook',
        epilog=
        'Version {version}. [Python {py_major}.{py_minor}.{py_micro}-{platform}] '
        'Source at https://github.com/ping/odmpy/'.format(
            version=__version__,
            py_major=sys.version_info.major,
            py_minor=sys.version_info.minor,
            py_micro=sys.version_info.micro,
            platform=sys.platform,
        ))
    parser.add_argument('-v',
                        '--verbose',
                        dest='verbose',
                        action='store_true',
                        help='Enable more verbose messages for debugging')
    parser.add_argument(
        '-t',
        '--timeout',
        dest='timeout',
        type=int,
        default=10,
        help='Timeout (seconds) for network requests. Default 10.')

    subparsers = parser.add_subparsers(
        title='Available commands',
        dest='subparser_name',
        help='To get more help, use the -h option with the command.')
    parser_info = subparsers.add_parser(
        'info',
        description='Get information about a loan file.',
        help='Get information about a loan file')
    parser_info.add_argument('odm_file', type=str, help='ODM file path')

    parser_dl = subparsers.add_parser('dl',
                                      description='Download from a loan file.',
                                      help='Download from a loan file')
    parser_dl.add_argument('-d',
                           '--downloaddir',
                           dest='download_dir',
                           default='.',
                           help='Download folder path')
    parser_dl.add_argument('-c',
                           '--chapters',
                           dest='add_chapters',
                           action='store_true',
                           help='Add chapter marks (experimental)')
    parser_dl.add_argument(
        '-m',
        '--merge',
        dest='merge_output',
        action='store_true',
        help='Merge into 1 file (experimental, requires ffmpeg)')
    parser_dl.add_argument(
        '--mergeformat',
        dest='merge_format',
        choices=['mp3', 'm4b'],
        default='mp3',
        help='Merged file format (m4b is slow, experimental, requires ffmpeg)')
    parser_dl.add_argument(
        '-k',
        '--keepcover',
        dest='always_keep_cover',
        action='store_true',
        help='Always generate the cover image file (cover.jpg)')
    parser_dl.add_argument('-f',
                           '--keepmp3',
                           dest='keep_mp3',
                           action='store_true',
                           help='Keep downloaded mp3 files (after merging)')
    parser_dl.add_argument('-j',
                           '--writejson',
                           dest='write_json',
                           action='store_true',
                           help='Generate a meta json file (for debugging)')
    parser_dl.add_argument('odm_file', type=str, help='ODM file path')

    parser_ret = subparsers.add_parser('ret',
                                       description='Return a loan file.',
                                       help='Return a loan file.')

    parser_ret.add_argument('odm_file', type=str, help='ODM file path')

    args = parser.parse_args()
    if args.verbose:
        logger.setLevel(logging.DEBUG)

    try:
        # test for odm file
        args.odm_file
    except AttributeError:
        parser.print_help()
        exit(0)

    xml_doc = xml.etree.ElementTree.parse(args.odm_file)
    root = xml_doc.getroot()

    # Return Book
    if args.subparser_name == 'ret':
        logger.info('Returning {} ...'.format(args.odm_file))
        early_return_url = root.find('EarlyReturnURL').text
        try:
            early_return_res = requests.get(early_return_url,
                                            headers={'User-Agent': UA_LONG},
                                            timeout=10)
            early_return_res.raise_for_status()
            logger.info('Loan returned successfully: {}'.format(args.odm_file))
        except HTTPError as he:
            if he.response.status_code == 403:
                logger.warning('Loan is probably already returned.')
                sys.exit()
            logger.error(
                'Unexpected HTTPError while trying to return loan {}'.format(
                    args.odm_file))
            logger.error('HTTPError: {}'.format(str(he)))
            logger.debug(he.response.content)
            sys.exit(1)
        except ConnectionError as ce:
            logger.error('ConnectionError: {}'.format(str(ce)))
            sys.exit(1)

        sys.exit()

    metadata = None
    for t in root.itertext():
        if not t.startswith('<Metadata>'):
            continue
        metadata = xml.etree.ElementTree.fromstring(
            # remove invalid & char
            re.sub(r'\s&\s', ' &amp; ', t))
        break

    debug_meta = {}

    title = metadata.find('Title').text
    cover_url = metadata.find(
        'CoverUrl').text if metadata.find('CoverUrl') != None else ''
    authors = [
        unescape_html(c.text) for c in metadata.find('Creators')
        if 'Author' in c.attrib.get('role', '')
    ]
    if not authors:
        authors = [
            unescape_html(c.text) for c in metadata.find('Creators')
            if 'Editor' in c.attrib.get('role', '')
        ]
    if not authors:
        authors = [unescape_html(c.text) for c in metadata.find('Creators')]
    publisher = metadata.find('Publisher').text
    description = metadata.find('Description').text if metadata.find(
        'Description') is not None else ''

    debug_meta['meta'] = {
        'title': title,
        'coverUrl': cover_url,
        'authors': authors,
        'publisher': publisher,
        'description': description,
    }

    # View Book Info
    if args.subparser_name == 'info':
        logger.info(u'{:10} {}'.format('Title:', colored.blue(title)))
        logger.info(u'{:10} {}'.format(
            'Creators:',
            colored.blue(u', '.join([
                u'{} ({})'.format(c.text, c.attrib['role'])
                for c in metadata.find('Creators')
            ]))))
        logger.info(u'{:10} {}'.format('Publisher:',
                                       metadata.find('Publisher').text))
        logger.info(u'{:10} {}'.format(
            'Subjects:',
            u', '.join([c.text for c in metadata.find('Subjects')])))
        logger.info(u'{:10} {}'.format(
            'Languages:',
            u', '.join([c.text for c in metadata.find('Languages')])))
        logger.info(u'{:10} \n{}'.format('Description:',
                                         metadata.find('Description').text))

        for formats in root.findall('Formats'):
            for f in formats:
                logger.info(u'\n{:10} {}'.format('Format:', f.attrib['name']))
                parts = f.find('Parts')
                for p in parts:
                    logger.info('* {} - {} ({:,.0f}kB)'.format(
                        p.attrib['name'], p.attrib['duration'],
                        math.ceil(1.0 * int(p.attrib['filesize']) / 1024)))
        sys.exit()

    # Download Book
    download_baseurl = ''
    download_parts = []
    for formats in root.findall('Formats'):
        for f in formats:
            protocols = f.find('Protocols')
            for p in protocols:
                if p.attrib.get('method', '') != 'download':
                    continue
                download_baseurl = p.attrib['baseurl']
                break
            parts = f.find('Parts')
            for p in parts:
                download_parts.append(p.attrib)
    debug_meta['download_parts'] = download_parts

    logger.info('Downloading "{}" by "{}" in {} parts...'.format(
        colored.blue(title, bold=True), colored.blue(', '.join(authors)),
        len(download_parts)))

    # declare book folder/file names here together so we can catch problems from too long names
    book_folder = os.path.join(
        args.download_dir,
        u'{} - {}'.format(title.replace(os.sep, '-'),
                          u', '.join(authors).replace(os.sep, '-')))

    # for merged mp3
    book_filename = os.path.join(
        book_folder,
        u'{} - {}.mp3'.format(title.replace(os.sep, '-'),
                              u', '.join(authors).replace(os.sep, '-')))
    # for merged m4b
    book_m4b_filename = os.path.join(
        book_folder,
        u'{} - {}.m4b'.format(title.replace(os.sep, '-'),
                              u', '.join(authors).replace(os.sep, '-')))

    if not os.path.exists(book_folder):
        try:
            os.makedirs(book_folder)
        except OSError as exc:
            if exc.errno not in (
                    36, 63):  # ref http://www.ioplex.com/~miallen/errcmpp.html
                raise

            # Ref OSError: [Errno 36] File name too long https://github.com/ping/odmpy/issues/5
            # create book folder, file with just the title
            book_folder = os.path.join(
                args.download_dir, u'{}'.format(title.replace(os.sep, '-')))
            os.makedirs(book_folder)

            book_filename = os.path.join(
                book_folder, u'{}.mp3'.format(title.replace(os.sep, '-')))
            book_m4b_filename = os.path.join(
                book_folder, u'{}.m4b'.format(title.replace(os.sep, '-')))

    cover_filename = os.path.join(book_folder, 'cover.jpg')
    debug_filename = os.path.join(book_folder, 'debug.json')
    if not os.path.isfile(cover_filename) and cover_url:
        cover_res = requests.get(cover_url, headers={'User-Agent': UA})
        cover_res.raise_for_status()
        with open(cover_filename, 'wb') as outfile:
            outfile.write(cover_res.content)

    acquisition_url = root.find('License').find('AcquisitionUrl').text
    media_id = root.attrib['id']

    client_id = str(uuid.uuid1()).upper()
    raw_hash = '{client_id}|{omc}|{os}|ELOSNOC*AIDEM*EVIRDREVO'.format(
        client_id=client_id, omc=OMC, os=OS)
    m = hashlib.sha1(raw_hash.encode('utf-16-le'))
    license_hash = base64.b64encode(m.digest())

    # Extract license
    # License file is downloadable only once per odm
    # so we keep it in case downloads fail
    _, odm_filename = os.path.split(args.odm_file)
    license_file = os.path.join(args.download_dir,
                                odm_filename.replace('.odm', '.license'))

    if os.path.isfile(license_file):
        logger.warning(
            'Already downloaded license file: {}'.format(license_file))
    else:
        # download license file
        params = OrderedDict([('MediaID', media_id), ('ClientID', client_id),
                              ('OMC', OMC), ('OS', OS),
                              ('Hash', license_hash)])

        license_res = requests.get(acquisition_url,
                                   params=params,
                                   headers={'User-Agent': UA},
                                   timeout=args.timeout,
                                   stream=True)
        try:
            license_res.raise_for_status()
            with open(license_file, 'wb') as outfile:
                for chunk in license_res.iter_content(1024):
                    outfile.write(chunk)
            logger.debug('Saved license file {}'.format(license_file))

        except HTTPError as he:
            if he.response.status_code == 404:
                # odm file has expired
                logger.error('The loan file "{}" has expired.'
                             'Please download again.'.format(args.odm_file))
            else:
                logger.error(he.response.content)
            sys.exit(1)
        except ConnectionError as ce:
            logger.error('ConnectionError: {}'.format(str(ce)))
            sys.exit(1)

    license_xml_doc = xml.etree.ElementTree.parse(license_file)
    license_root = license_xml_doc.getroot()

    ns = '{http://license.overdrive.com/2008/03/License.xsd}'

    license_client = license_root.find('{}SignedInfo'.format(ns)).find(
        '{}ClientID'.format(ns))
    license_client_id = license_client.text

    lic_file_contents = ''
    with open(license_file, 'r') as lic_file:
        lic_file_contents = lic_file.read()

    cover_bytes = None
    if os.path.isfile(cover_filename):
        with open(cover_filename, 'rb') as f:
            cover_bytes = f.read()

    track_count = 0
    file_tracks = []
    keep_cover = args.always_keep_cover
    audio_lengths_ms = []
    for p in download_parts:
        part_number = int(p['number'])
        part_filename = os.path.join(
            book_folder, u'{}.mp3'.format(
                slugify(u'{} - Part {:02d}'.format(title, part_number),
                        allow_unicode=True)))
        part_tmp_filename = u'{}.part'.format(part_filename)
        part_file_size = int(p['filesize'])
        part_url_filename = p['filename']
        part_download_url = '{}/{}'.format(download_baseurl, part_url_filename)
        part_markers = []

        if os.path.isfile(part_filename):
            logger.warning('Already saved {}'.format(
                colored.magenta(part_filename)))
        else:
            try:
                part_download_res = requests.get(part_download_url,
                                                 headers={
                                                     'User-Agent': UA,
                                                     'ClientID':
                                                     license_client_id,
                                                     'License':
                                                     lic_file_contents
                                                 },
                                                 timeout=args.timeout,
                                                 stream=True)

                part_download_res.raise_for_status()

                chunk_size = 1024 * 1024
                expected_chunk_count = math.ceil(1.0 * part_file_size /
                                                 chunk_size)
                with open(part_tmp_filename, 'wb') as outfile:
                    for chunk in progress.bar(
                            part_download_res.iter_content(
                                chunk_size=chunk_size),
                            label='Part {}'.format(part_number),
                            expected_size=expected_chunk_count):
                        if chunk:
                            outfile.write(chunk)

                # try to remux file to remove mp3 lame tag errors
                cmd = [
                    'ffmpeg', '-y', '-nostdin', '-hide_banner', '-loglevel',
                    'info' if logger.level == logging.DEBUG else 'error', '-i',
                    part_tmp_filename, '-c:a', 'copy', '-c:v', 'copy',
                    part_filename
                ]
                try:
                    exit_code = subprocess.call(cmd)
                    if exit_code:
                        logger.warning(
                            'ffmpeg exited with the code: {0!s}'.format(
                                exit_code))
                        logger.warning('Command: {0!s}'.format(' '.join(cmd)))
                        os.rename(part_tmp_filename, part_filename)
                    else:
                        os.remove(part_tmp_filename)
                except Exception as ffmpeg_ex:
                    logger.warning('Error executing ffmpeg: {}'.format(
                        str(ffmpeg_ex)))
                    os.rename(part_tmp_filename, part_filename)

            except HTTPError as he:
                logger.error('HTTPError: {}'.format(str(he)))
                logger.debug(he.response.content)
                sys.exit(1)

            except ConnectionError as ce:
                logger.error('ConnectionError: {}'.format(str(ce)))
                sys.exit(1)

        try:
            # Fill id3 info for mp3 part
            mutagen_audio = MP3(part_filename, ID3=ID3)
            if not mutagen_audio.tags:
                mutagen_audio.tags = ID3()
            if 'TIT2' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TIT2(encoding=3, text=u'{}'.format(title)))
            if 'TIT3' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TIT3(encoding=3, text=u'{}'.format(description)))
            if 'TALB' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TALB(encoding=3, text=u'{}'.format(title)))
            if 'TPE1' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TPE1(encoding=3, text=u'{}'.format(authors[0])))
            if 'TPE2' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TPE2(encoding=3, text=u'{}'.format(authors[0])))
            if 'TRCK' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TRCK(encoding=3, text=u'{}'.format(part_number)))
            if 'TPUB' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    TPUB(encoding=3, text=u'{}'.format(publisher)))
            if 'COMM' not in mutagen_audio.tags:
                mutagen_audio.tags.add(
                    COMM(encoding=3,
                         desc=u'Description',
                         text=u'{}'.format(description)))
            if cover_bytes:
                mutagen_audio.tags.add(
                    APIC(encoding=3,
                         mime=u'image/jpeg',
                         type=3,
                         desc=u'Cover',
                         data=cover_bytes))
            mutagen_audio.save()

            audio_lengths_ms.append(
                int(round(mutagen_audio.info.length * 1000)))

            # Extract OD chapter info from mp3s for use in merged file
            if 'TXXX:OverDrive MediaMarkers' in mutagen_audio.tags \
                    and mutagen_audio.tags['TXXX:OverDrive MediaMarkers'].text:
                marker_text = mutagen_audio.tags[
                    'TXXX:OverDrive MediaMarkers'].text[0]
                try:
                    tree = xml.etree.ElementTree.fromstring(marker_text)
                except UnicodeEncodeError:
                    tree = xml.etree.ElementTree.fromstring(
                        marker_text.encode('ascii', 'ignore').decode('ascii'))

                for m in tree.iter('Marker'):
                    marker_name = m.find('Name').text.strip()
                    marker_timestamp = m.find('Time').text
                    timestamp = None
                    ts_mark = 0
                    # 2 timestamp formats found
                    for r in ('%M:%S.%f', '%H:%M:%S.%f'):
                        try:
                            timestamp = time.strptime(marker_timestamp, r)
                            ts = datetime.timedelta(hours=timestamp.tm_hour,
                                                    minutes=timestamp.tm_min,
                                                    seconds=timestamp.tm_sec)
                            ts_mark = int(1000 * ts.total_seconds())
                            break
                        except ValueError:
                            pass

                    if not timestamp:
                        # some invalid timestamp string, e.g. 60:15.00
                        mobj = re.match(MARKER_TIMESTAMP_HHMMSS,
                                        marker_timestamp)
                        if mobj:
                            ts_mark = int(mobj.group('hr')) * 60 * 60 * 1000 + \
                                      int(mobj.group('min')) * 60 * 1000 + \
                                      int(mobj.group('sec')) * 1000 + \
                                      int(mobj.group('ms'))
                        else:
                            mobj = re.match(MARKER_TIMESTAMP_MMSS,
                                            marker_timestamp)
                            if mobj:
                                ts_mark = int(mobj.group('min')) * 60 * 1000 + \
                                          int(mobj.group('sec')) * 1000 + \
                                          int(mobj.group('ms'))
                            else:
                                raise ValueError(
                                    'Invalid marker timestamp: {}'.format(
                                        marker_timestamp))

                    track_count += 1
                    part_markers.append((u'ch{:02d}'.format(track_count),
                                         marker_name, ts_mark))

            if args.add_chapters and not args.merge_output:
                # set the chapter marks
                generated_markers = []
                for j, file_marker in enumerate(part_markers):
                    generated_markers.append({
                        'id':
                        file_marker[0],
                        'text':
                        file_marker[1],
                        'start_time':
                        int(file_marker[2]),
                        'end_time':
                        int(
                            round(mutagen_audio.info.length *
                                  1000) if j == (len(part_markers) -
                                                 1) else part_markers[j +
                                                                      1][2]),
                    })

                mutagen_audio.tags.add(
                    CTOC(element_id=u'toc',
                         flags=CTOCFlags.TOP_LEVEL | CTOCFlags.ORDERED,
                         child_element_ids=[
                             m['id'].encode('ascii') for m in generated_markers
                         ],
                         sub_frames=[TIT2(text=[u'Table of Contents'])]))
                for i, m in enumerate(generated_markers):
                    mutagen_audio.tags.add(
                        CHAP(element_id=m['id'].encode('ascii'),
                             start_time=m['start_time'],
                             end_time=m['end_time'],
                             sub_frames=[TIT2(text=[u'{}'.format(m['text'])])
                                         ]))
                    start_time = datetime.timedelta(
                        milliseconds=m['start_time'])
                    end_time = datetime.timedelta(milliseconds=m['end_time'])
                    logger.debug(
                        u'Added chap tag => {}: {}-{} "{}" to "{}"'.format(
                            colored.cyan(m['id']), start_time, end_time,
                            colored.cyan(m['text']),
                            colored.blue(part_filename)))

                if len(generated_markers) == 1:
                    # Weird player problem on voice where title is shown instead of chapter title
                    mutagen_audio.tags.add(
                        TIT2(encoding=3, text=u'{}'.format(title)))

                mutagen_audio.save()

        except Exception as e:
            logger.warning('Error saving ID3: {}'.format(
                colored.red(str(e), bold=True)))
            keep_cover = True

        logger.info('Saved "{}"'.format(colored.magenta(part_filename)))

        file_tracks.append({
            'file': part_filename,
            'markers': part_markers,
        })
    # end loop: for p in download_parts:

    debug_meta['audio_lengths_ms'] = audio_lengths_ms
    debug_meta['file_tracks'] = file_tracks

    if args.merge_output:
        if os.path.isfile(book_filename if args.merge_format ==
                          'mp3' else book_m4b_filename):
            logger.warning('Already saved "{}"'.format(
                colored.magenta(book_filename if args.merge_format ==
                                'mp3' else book_m4b_filename)))
            sys.exit(0)

        logger.info('Generating "{}"...'.format(
            colored.magenta(book_filename if args.merge_format ==
                            'mp3' else book_m4b_filename)))

        # We can't directly generate a m4b here even if specified because eyed3 doesn't support m4b/mp4
        temp_book_filename = '{}.part'.format(book_filename)
        cmd = [
            'ffmpeg',
            '-y',
            '-nostdin',
            '-hide_banner',
            '-loglevel',
            'info' if logger.level == logging.DEBUG else 'error',
            '-stats',
            '-i',
            'concat:{}'.format('|'.join([ft['file'] for ft in file_tracks])),
            '-acodec',
            'copy',
            '-b:a',
            '64k',  # explicitly set audio bitrate
            '-f',
            'mp3',
            temp_book_filename
        ]
        exit_code = subprocess.call(cmd)

        if exit_code:
            logger.error(
                'ffmpeg exited with the code: {0!s}'.format(exit_code))
            logger.error('Command: {0!s}'.format(' '.join(cmd)))
            exit(exit_code)
        os.rename(temp_book_filename, book_filename)

        mutagen_audio = MP3(book_filename, ID3=ID3)
        if not mutagen_audio.tags:
            mutagen_audio.tags = ID3()
        # Overwrite title since it gets picked up from the first track by default
        mutagen_audio.tags.add(TIT2(encoding=3, text=u'{}'.format(title)))
        if 'TALB' not in mutagen_audio.tags:
            mutagen_audio.tags.add(TALB(encoding=3, text=u'{}'.format(title)))
        if 'TPE1' not in mutagen_audio.tags:
            mutagen_audio.tags.add(
                TPE1(encoding=3, text=u'{}'.format(authors[0])))
        if 'TPE2' not in mutagen_audio.tags:
            mutagen_audio.tags.add(
                TPE2(encoding=3, text=u'{}'.format(authors[0])))
        if 'TPUB' not in mutagen_audio.tags:
            mutagen_audio.tags.add(
                TPUB(encoding=3, text=u'{}'.format(publisher)))
        if 'COMM' not in mutagen_audio.tags:
            mutagen_audio.tags.add(
                COMM(encoding=3,
                     desc=u'Description',
                     text=u'{}'.format(description)))
        mutagen_audio.save()

        if args.add_chapters:
            merged_markers = []
            for i, f in enumerate(file_tracks):
                prev_tracks_len_ms = 0 if i == 0 else reduce(
                    lambda x, y: x + y, audio_lengths_ms[0:i])
                this_track_endtime_ms = int(
                    reduce(lambda x, y: x + y, audio_lengths_ms[0:i + 1]))
                file_markers = f['markers']
                for j, file_marker in enumerate(file_markers):
                    merged_markers.append({
                        'id':
                        file_marker[0],
                        'text':
                        u'{}'.format(file_marker[1]),
                        'start_time':
                        int(file_marker[2]) + prev_tracks_len_ms,
                        'end_time':
                        int(this_track_endtime_ms if j == (
                            len(file_markers) -
                            1) else file_markers[j + 1][2] +
                            prev_tracks_len_ms),
                    })
            debug_meta['merged_markers'] = merged_markers

            mutagen_audio.tags.add(
                CTOC(element_id=u'toc',
                     flags=CTOCFlags.TOP_LEVEL | CTOCFlags.ORDERED,
                     child_element_ids=[
                         m['id'].encode('ascii') for m in merged_markers
                     ],
                     sub_frames=[TIT2(text=[u"Table of Contents"])]))
            for i, m in enumerate(merged_markers):
                mutagen_audio.tags.add(
                    CHAP(element_id=m['id'].encode('ascii'),
                         start_time=m['start_time'],
                         end_time=m['end_time'],
                         sub_frames=[TIT2(text=[u'{}'.format(m['text'])])]))
                start_time = datetime.timedelta(milliseconds=m['start_time'])
                end_time = datetime.timedelta(milliseconds=m['end_time'])
                logger.debug(
                    u'Added chap tag => {}: {}-{} "{}" to "{}"'.format(
                        colored.cyan(m['id']), start_time, end_time,
                        colored.cyan(m['text']), colored.blue(book_filename)))

        mutagen_audio.save()

        if args.merge_format == 'mp3':
            logger.info('Merged files into "{}"'.format(
                colored.magenta(book_filename if args.merge_format ==
                                'mp3' else book_m4b_filename)))

        if args.merge_format == 'm4b':
            temp_book_m4b_filename = '{}.part'.format(book_m4b_filename)
            cmd = [
                'ffmpeg',
                '-y',
                '-nostdin',
                '-hide_banner',
                '-loglevel',
                'info' if logger.level == logging.DEBUG else 'error',
                '-stats',
                '-i',
                book_filename,
            ]
            if os.path.isfile(cover_filename):
                cmd.extend(['-i', cover_filename])

            cmd.extend([
                '-map',
                '0:a',
                '-c:a',
                'aac',
                '-b:a',
                '64k',  # explicitly set audio bitrate
            ])
            if os.path.isfile(cover_filename):
                cmd.extend([
                    '-map',
                    '1:v',
                    '-c:v',
                    'copy',
                    '-disposition:v:0',
                    'attached_pic',
                ])

            cmd.extend(['-f', 'mp4', temp_book_m4b_filename])
            exit_code = subprocess.call(cmd)

            if exit_code:
                logger.error(
                    'ffmpeg exited with the code: {0!s}'.format(exit_code))
                logger.error('Command: {0!s}'.format(' '.join(cmd)))
                exit(exit_code)

            os.rename(temp_book_m4b_filename, book_m4b_filename)
            logger.info('Merged files into "{}"'.format(
                colored.magenta(book_m4b_filename)))
            try:
                os.remove(book_filename)
            except Exception as e:
                logger.warning('Error deleting "{}": {}'.format(
                    book_filename, str(e)))

        if not args.keep_mp3:
            for f in file_tracks:
                try:
                    os.remove(f['file'])
                except Exception as e:
                    logger.warning('Error deleting "{}": {}'.format(
                        f['file'], str(e)))

    if not keep_cover:
        try:
            os.remove(cover_filename)
        except Exception as e:
            logger.warning('Error deleting "{}": {}'.format(
                cover_filename, str(e)))

    if args.write_json:
        with open(debug_filename, 'w') as outfile:
            json.dump(debug_meta, outfile, indent=2)
Exemplo n.º 19
0
def get_song(show, download, name):
    begin, end = [int(value) for value in get_num(download)]
    url_base = "http://zaycev.net"
    url_add = ''
    ua = UserAgent()
    header = {'User-Agent': ua.random}
    param = None
    if name:
        url_add = "search.html"
        query = '+'.join(name)
        param = {"query_search": query}
    http = get(f'{url_base}/{url_add}', headers=header, params=param)
    response = html.fromstring(http.text)
    links = response.xpath('//div[@data-rbt-content-id]/@data-url')
    artists = response.xpath('//*[@itemprop="byArtist"]/a/text()')
    songs = response.xpath('//*[@itemprop="name"]/a/text()')
    begin = max(begin, 1)
    if end == -1 or end > len(songs):
        end = len(songs)
    begin = min(begin, end)
    if show:
        print('Доступные композиции:')
    if links:
        i = 1
        shift = 0
        while True:
            url = get(f'{url_base}{links[i - 1]}').json()['url']
            presence = head(url)
            if presence.status_code != 200 and presence.headers.get(
                    'Content-Type') != 'audio/mpeg':
                i += 1
                continue
            shift += 1
            if shift >= begin:
                title = f'{artists[i-1].strip()} – {songs[i-1].strip()}.mp3'
                size = round(
                    int(presence.headers.get('Content-Length', '0')) / 1048576,
                    1)
                if show:
                    print(f'{shift}. {title} ({size} Мб)')
                else:
                    title = title.replace(':', ' ').replace('\\', ' ').replace(
                        '*', ' ').replace('  ', ' ')
                    while exists(title):
                        title = '_' + title
                    number = '' if begin == end else f"{shift}."
                    print(f"Загружается: {number}{title}", end='', flush=True)
                    song = get(url, stream=True)
                    save_song_to_file(title, song, number)
                    try:
                        audio = ID3(title)
                    except ID3NoHeaderError:
                        pass
                    else:
                        song_name = audio['TIT2'][0][:-13]
                        audio.add(TIT2(text=song_name))
                        audio.add(TALB(text=''))
                        audio.add(COMM(lang='eng', text=''))
                        audio.save()

                    print(f"\rЗагружено: {number}{title}     ")
            if shift >= end or i >= len(links):
                break
            i += 1
Exemplo n.º 20
0
    def save_mp3_metadata(self, file_path, data):
        """ Saves the given metadata for an MP3 file.

		Metadata.save_mp3_metadata(str, dict)
		"""
        audio = ID3(file_path)  # Writing MP3 tags needs ID3
        mp3_audio, format = self.get_mutagen_parser(file_path)

        if data.get("title"):
            audio.add(TIT2(encoding=3, text=unicode(data['title'])))
        if data.get("artist"):
            audio.add(TPE1(encoding=3, text=unicode(data['artist'])))
        if data.get("album"):
            audio.add(TALB(encoding=3, text=unicode(data['album'])))
        if data.get("genre"):
            audio.add(TCON(encoding=3, text=unicode(data['genre'])))
        if data.get("year"):
            audio.add(TDRC(encoding=3, text=unicode(data['year'])))
        if data.get("album_artist"):
            audio.add(TPE2(encoding=3, text=unicode(data['album_artist'])))

        if data.get("track_number", False) and data.get("total_tracks", False):
            audio.add(
                TRCK(encoding=3,
                     text=unicode(data['track_number'] + '/' +
                                  data['total_tracks'])))
        elif data.get("track_number"):
            total_tracks = self.get_total_tracks(mp3_audio, format)
            if total_tracks == None:
                audio.add(TRCK(encoding=3, text=unicode(data['track_number'])))
            else:
                audio.add(
                    TRCK(encoding=3,
                         text=unicode(data['track_number'] + '/' +
                                      total_tracks)))
        elif data.get("total_tracks"):
            t_no = self.get_track_number(mp3_audio, format)
            if t_no == None:
                audio.add(
                    TRCK(encoding=3,
                         text=unicode(" /" + data["total_tracks"])))
            else:
                audio.add(
                    TRCK(encoding=3,
                         text=unicode(t_no + '/' + data["total_tracks"])))

        if data.get("disc_number", False) and data.get("total_discs", False):
            audio.add(
                TPOS(encoding=3,
                     text=unicode(data['disc_number'] + '/' +
                                  data['total_discs'])))
        elif data.get("disc_number"):
            total_discs = self.get_total_discs(mp3_audio, format)
            if total_discs == None:
                audio.add(TPOS(encoding=3, text=unicode(data['disc_number'])))
            else:
                audio.add(
                    TPOS(encoding=3,
                         text=unicode(data['disc_number'] + '/' +
                                      total_discs)))
        elif data.get("total_discs"):
            d_no = self.get_disc_number(mp3_audio, format)
            if d_no == None:
                audio.add(
                    TPOS(encoding=3, text=unicode(" /" + data["total_discs"])))
            else:
                audio.add(
                    TPOS(encoding=3,
                         text=unicode(d_no + '/' + data["total_discs"])))

        if data.get("composer"):
            audio.add(TCOM(encoding=3, text=unicode(data['composer'])))
        if data.get("publisher"):
            audio.add(TPUB(encoding=3, text=unicode(data['publisher'])))
        if data.get("comments"):
            audio.add(
                COMM(encoding=3,
                     lang="eng",
                     desc="",
                     text=unicode(data['comments'])))
        audio.save()
Exemplo n.º 21
0
fname = os.path.splitext(basename)[0]
filepath_change = fname + "_change.mp3"

shutil.copyfile(filepath, filepath_change)

# find comment tag
id3_tags = ID3(filepath_change)

# find comment tag
has_comm = False
for k, v in id3_tags.items():
    if "COMM" in k:
        has_comm = True
        comm = v
# for

if not has_comm:
    print('no comment')
    exit()
# if

comm_text_list = comm.text
comm_text = = comm_text_list[0]

# change comment
id3_tags[COMM] = COMM(encoding=3, text='new comment')
id3_tags.save()

print( 'changed: %s ' % filepath_change)

Exemplo n.º 22
0
    def test_COMM(self):
        from mutagen.id3 import COMM

        frame = COMM(encoding=0, lang="foo", desc="d")
        self.assertEqual(frame.HashKey, "COMM:d:foo")
        frame._pprint()
Exemplo n.º 23
0
    def as_mp3(self):
        """ Embed metadata to MP3 files. """
        music_file = self.music_file
        meta_tags = self.meta_tags
        # EasyID3 is fun to use ;)
        # For supported easyid3 tags:
        # https://github.com/quodlibet/mutagen/blob/master/mutagen/easyid3.py
        # Check out somewhere at end of above linked file
        audiofile = EasyID3(music_file)
        audiofile['artist'] = meta_tags['artists'][0]['name']
        audiofile['albumartist'] = meta_tags['artists'][0]['name']
        audiofile['album'] = meta_tags['album']['name']
        audiofile['title'] = meta_tags['name']
        audiofile['tracknumber'] = [
            meta_tags['track_number'], meta_tags['total_tracks']
        ]
        audiofile['discnumber'] = [meta_tags['disc_number'], 0]
        audiofile['date'] = meta_tags['release_date']
        audiofile['originaldate'] = meta_tags['release_date']
        audiofile['media'] = meta_tags['type']
        audiofile['author'] = meta_tags['artists'][0]['name']
        audiofile['lyricist'] = meta_tags['artists'][0]['name']
        audiofile['arranger'] = meta_tags['artists'][0]['name']
        audiofile['performer'] = meta_tags['artists'][0]['name']
        audiofile['website'] = meta_tags['external_urls']['spotify']
        audiofile['length'] = str(meta_tags['duration'])
        if meta_tags['publisher']:
            audiofile['encodedby'] = meta_tags['publisher']
        if meta_tags['genre']:
            audiofile['genre'] = meta_tags['genre']
        if meta_tags['copyright']:
            audiofile['copyright'] = meta_tags['copyright']
        if meta_tags['external_ids']['isrc']:
            audiofile['isrc'] = meta_tags['external_ids']['isrc']
        audiofile.save(v2_version=3)

        # For supported id3 tags:
        # https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py
        # Each class represents an id3 tag
        audiofile = ID3(music_file)
        audiofile['TORY'] = TORY(encoding=3, text=meta_tags['year'])
        audiofile['TYER'] = TYER(encoding=3, text=meta_tags['year'])
        audiofile['TPUB'] = TPUB(encoding=3, text=meta_tags['publisher'])
        audiofile['COMM'] = COMM(encoding=3,
                                 text=meta_tags['external_urls']['spotify'])
        if meta_tags['lyrics']:
            audiofile['USLT'] = USLT(encoding=3,
                                     desc=u'Lyrics',
                                     text=meta_tags['lyrics'])
        try:
            albumart = urllib.request.urlopen(
                meta_tags['album']['images'][0]['url'])
            audiofile['APIC'] = APIC(encoding=3,
                                     mime='image/jpeg',
                                     type=3,
                                     desc=u'Cover',
                                     data=albumart.read())
            albumart.close()
        except IndexError:
            pass

        audiofile.save(v2_version=3)
        return True
Exemplo n.º 24
0
    def as_mp3(self, path, metadata, cached_albumart=None):
        """
        Apply metadata on MP3 media files.

        Parameters
        ----------
        path: `str`
            Path to the media file.

        metadata: `dict`
            Metadata (standardized) to apply to the media file.

        cached_albumart: `bool`
            An albumart image binary. If passed, the albumart URL
            present in the ``metadata`` won't be downloaded or used.
        """
        logger.debug('Writing MP3 metadata to "{path}".'.format(path=path))
        # EasyID3 is fun to use ;)
        # For supported easyid3 tags:
        # https://github.com/quodlibet/mutagen/blob/master/mutagen/easyid3.py
        # Check out somewhere at end of above linked file
        audiofile = EasyID3(path)
        self._embed_basic_metadata(audiofile,
                                   metadata,
                                   "mp3",
                                   preset=TAG_PRESET)
        audiofile["media"] = metadata["type"]
        audiofile["author"] = metadata["artists"][0]["name"]
        audiofile["lyricist"] = metadata["artists"][0]["name"]
        audiofile["arranger"] = metadata["artists"][0]["name"]
        audiofile["performer"] = metadata["artists"][0]["name"]
        provider = metadata["provider"]
        audiofile["website"] = metadata["external_urls"][provider]
        audiofile["length"] = str(metadata["duration"])
        if metadata["publisher"]:
            audiofile["encodedby"] = metadata["publisher"]
        if metadata["external_ids"]["isrc"]:
            audiofile["isrc"] = metadata["external_ids"]["isrc"]
        audiofile.save(v2_version=3)

        # For supported id3 tags:
        # https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py
        # Each class in the linked source file represents an id3 tag
        audiofile = ID3(path)
        if metadata["year"]:
            audiofile["TORY"] = TORY(encoding=3, text=metadata["year"])
            audiofile["TYER"] = TYER(encoding=3, text=metadata["year"])
        if metadata["publisher"]:
            audiofile["TPUB"] = TPUB(encoding=3, text=metadata["publisher"])
        provider = metadata["provider"]
        audiofile["COMM"] = COMM(encoding=3,
                                 text=metadata["external_urls"][provider])
        if metadata["lyrics"]:
            audiofile["USLT"] = USLT(encoding=3,
                                     desc=u"Lyrics",
                                     text=metadata["lyrics"])
        if cached_albumart is None:
            cached_albumart = urllib.request.urlopen(
                metadata["album"]["images"][0]["url"]).read()
        try:
            audiofile["APIC"] = APIC(
                encoding=3,
                mime="image/jpeg",
                type=3,
                desc=u"Cover",
                data=cached_albumart,
            )
        except IndexError:
            pass

        audiofile.save(v2_version=3)