#!/usr/bin/python import os.path import re import os import sys import copy helptext = """ Tool for encoding tags of mp3 files in the russian 1-byte charsets to unicode Usage: tag2utf [DIRECTORIES] (By default files will be searched in the current dirrectory) Modes: --restore : programm will try to restore tags, that was broken by not right user choise --help, --version, --usage - view this text Version 0.12 Author: Kopats Andrei hlamer@tut.by Bugfix: Yarmak Vladislav chayt@smtp.ru This program is distributed under the terms of the GPL License. TODO: undo changes, Charsets will be in the config file or command line, for encoding not only from cp1251 and koi8-r GUI If you need to encode tags from different charset using this version, you can modify script, it's very easy to do. """ charsets = {'cp1251':'c','koi8-r':'k' } #modify it if you want decode tags from other encodings """ Default mode - just to convert tags, that have 1-byte encoding now Backup mode used for restore wrongly converted tags (and optionaly decode to right charset. """ restoreMode = False try: import eyeD3 except: print 'You need to install python-eyed3 package.' sys.exit() mp3FileName = re.compile ('.*(mp3|MP3)$') def recodingNeed (strs): """recoding needed if tags have symbols with 256>ords >127 """ # needed = False for string in strs: for i in range (len(string)): if 256>ord (string[i])>127: return True #nonunicode nonascii return False def passDir (rootdir): tags = [] songs = [] titles = [] artists = [] albums = [] for song in os.listdir(rootdir) : if ( os.path.isfile(os.path.join(rootdir,song)) and mp3FileName.match (song)): filename = os.path.join(rootdir,song) tag = eyeD3.Tag() try: if not tag.link(filename): continue #somthing wrong whith this file except: print '\n',filename,':error, may be tag is corrupted.\n ' continue if recodingNeed ([tag.getTitle(),tag.getArtist(),tag.getAlbum()]) or restoreMode: if not os.access(filename,os.W_OK): print 'Warning! Have not access for writing file '\ ,filename , ' Sciped!' continue tags.append (tag) songs.append (song) if not restoreMode : #normal mode titles.append (getTagStr (tag.getTitle())) artists.append (getTagStr (tag.getArtist())) albums.append (getTagStr (tag.getAlbum())) else: #restore mode titles.append (tag.getTitle()) artists.append (tag.getArtist()) albums.append (tag.getAlbum()) if len (tags) > 0: print len(tags),' file(s) finded in the ',rootdir askUser (tags,songs,titles,artists,albums) def getTagStr (tagUnicStr): #gets the 1byte 8bits string, as writed in the tag, from the unicode, returned by tag.get* ls = [] for i in range (0,len(tagUnicStr)): if (ord (tagUnicStr[i]) in range(256)): ls.append (chr (ord (tagUnicStr[i]))) else: ls.append(tagUnicStr[i]) Str8 = ''.join(ls) return Str8 def updateTags (tags,titles,artists,albums,charset, wrongCharset = ''): for i in range (len(tags)): tags[i].setVersion (eyeD3.ID3_V2_4) tags[i].setTextEncoding (eyeD3.UTF_8_ENCODING) if not ( restoreMode and wrongCharset != ''): #normal mode if (recodingNeed(artists[i])): tags[i].setArtist(artists[i].decode(charset)) else: tags[i].setArtist(artists[i]) if (recodingNeed(albums[i])): tags[i].setAlbum(albums[i].decode(charset)) else: tags[i].setAlbum(albums[i]) if (recodingNeed(titles[i])): tags[i].setTitle(titles[i].decode(charset)) else: tags[i].setTitle(titles[i]) else: #restore mode tags[i].setArtist(artists[i].encode(wrongCharset).decode(charset)) tags[i].setAlbum (albums[i].encode(wrongCharset).decode(charset)) tags[i].setTitle (titles[i].encode(wrongCharset).decode(charset)) tags[i].update() def askUser (tags,songs, titles,artists,albums): charsetListStr = '' if not restoreMode: #normal mode for charset in charsets.keys(): print '\n'+'['+charsets[charset]+']'+' If charset of tags is '+ charset+ ':' for i in range (len(songs)): sys.stdout.write(songs[i]) outlst = [' '] if (recodingNeed(titles[i])): outlst.append(titles[i].decode(charset)) else: outlst.append(titles[i]) outlst.append(' ') if (recodingNeed(artists[i])): outlst.append(artists[i].decode(charset)) else: outlst.append(artists[i]) outlst.append(' ') if (recodingNeed(albums[i])): outlst.append(albums[i].decode(charset)) else: outlst.append(albums[i]) print "".join(outlst) else: #backup mode for wrongCharset in charsets.keys(): #charset, to which file was wrongly converted for tagCharset in charsets.keys(): #right charset of tag if wrongCharset == tagCharset: continue print '\n'+'['+charsets[wrongCharset]+charsets[tagCharset]+']', \ ' If charset is '+ tagCharset+' (wrongly converted to '+ wrongCharset+ ')',':' for i in range (len(songs)): try: print songs[i],' ',\ titles[i].encode(wrongCharset).decode(tagCharset),' ',\ artists[i].encode(wrongCharset).decode(tagCharset),' ',\ albums[i].encode(wrongCharset).decode(tagCharset) except: print "ERROR:Can't encode tags of "+songs[i] charsetListStr += " '"+charsets[wrongCharset]+charsets[tagCharset]+"' - "+tagCharset+' converted to '+wrongCharset+'\n' print '\n',"Select charset:\n",charsetListStr, "'s' - skip this file(s)" if len(tags) >1: print "'m' - manual for every file" while 1: #get user choise end update the tags choise = raw_input() if not restoreMode : #normal mode if choise in charsets.values(): charset = charsets.keys()[charsets.values().index(choise)] updateTags(tags,titles,artists,albums,charset) break else: #restore mode if (len (choise) == 2) and \ (choise[0] in charsets.values()) and \ (choise[1] in charsets.values()): charset = charsets.keys()[charsets.values().index(choise[1])] wrongCharset = charsets.keys()[charsets.values().index(choise[0])] updateTags(tags,titles,artists,albums,charset,wrongCharset) break if choise == 's': return if choise == 'm' and len(tags) >1: for i in range(len(tags)): askUser ([tags[i]],[songs[i]],[titles[i]],[artists[i]],[albums[i]]) return #will be executed if no break or return before print 'What?' argsFailed = False ; rootdirs = [] argv = copy.copy ( sys.argv) argv.__delitem__(0) for arg in argv: if ( arg == '--usage' or arg == '--help' or arg == '--version'): print helptext sys.exit() elif arg == '--restore': restoreMode = True elif os.path.isdir(arg): rootdirs.append(arg) #this need because paths may have start in the working dir or in the root dir elif os.path.isdir (os.path.join (os.getcwd(),arg)): rootdirs.append (os.path.join (os.getcwd(),arg)) else: print "Not right argument '",sys.argv[i],"' It's not a directory.\n Try ",sys.argv[0], " --usage" argsFailed = True; if argsFailed: sys.exit() if rootdirs == []: rootdirs = [os.getcwd()] print 'Starting search in the ',os.getcwd() for rootdir in rootdirs: for root, dirs, files in os.walk(rootdir): passDir (root)