Source code for mp3fm
#!/usr/bin/env python
from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MP3
import musicbrainzngs as mbz
from shutil import move
from glob import glob
import os
[docs]class PackSongs(object):
"""
It pack songs into folders according to property choosen by the user
making easy to manage songs and keep them into folders
"""
def __init__(self, input_folder, tag=''):
""" Initializing info to use for creating folders """
self.tag = tag
self.folder = input_folder
self.change_cwd()
self.list_mp3files()
[docs] def find_info(self, song_name):
""" Finding song info """
info = dict()
# Loading Song
try:
self.mp3file = MP3(song_name, ID3=EasyID3)
# Storing all details of song into "info" dictionary
try:
info['title'] = str(self.mp3file['title'][0])
except:
info['title'] = ''
try:
info['artist'] = str(self.mp3file['artist'][0])
except:
info['artist'] = ''
try:
info['album'] = str(self.mp3file['album'][0])
except:
info['album'] = ''
try:
info['year'] = str(self.mp3file['date'][0])
except:
info['year'] = ''
try:
info['duration'] = int(self.mp3file.info.length * 1000)
except:
info['duration'] = ''
except:
info = {}
self.song_info = info
[docs] def change_cwd(self):
"""
Changing current working directory to user input folder to access songs
"""
os.chdir(self.folder)
[docs] def check_folder(self, folder_name):
""" Checking if required folder exists otherwise create a new one """
if not os.path.exists(folder_name):
os.makedirs(folder_name)
[docs] def list_mp3files(self, folder=''):
""" Storing list of all .mp3 files """
# All posibilities of writing mp3 considered ;)
if folder == '':
self.songs = glob('*.??3')
else:
return glob(folder+'/*.??3')
[docs] def move_song(self, song, folder_name):
""" Moving song to specific folder """
try:
move(song, folder_name)
except:
pass
[docs] def generate_log(self):
"""
Generate a log file stating the songs inside each newly created
directory
"""
# Listing all newly made folders
new_folders = [i for i in os.listdir('.') if(os.path.isdir(i))]
# Writing nfo data into log file
fh = open('PackLog.txt', 'w')
for folder in new_folders:
text = 'Folder : ' + folder + '\n' + "-"*(12+len(folder)) + \
'\n\n' + 'Track Listing' + '\n' + "-"*len('Track Listing') + '\n'
try:
songs = os.listdir(folder)
for index,name in enumerate(songs):
text += str(index+1) + '. ' + name + '\n'
except:
continue
fh.write(text+'\n\n\n')
fh.close()
[docs] def put_songs(self):
""" Putting all songs in folders according to user choice """
# Traversing all songs
for song in self.songs:
# Calling function to find song info
self.find_info(song)
info = self.song_info
if info != {}:
folder_name = info[self.tag]
if folder_name in [None, '', 'Unknown']:
folder_name = 'Random'
self.check_folder(folder_name)
self.move_song(song, folder_name)
[docs]class UpdateSongInfo(PackSongs):
"""
It updates all the songs information using online Music Brainz database.
"""
[docs] def authenticate(self):
""" Authenticate the client to query the Music Brainz Server """
mbz.set_useragent('mp3fm', 1.0, 'akshit.jiit@gmail.com')
[docs] def search_musicbrainz(self, song_name):
""" Searching music brainz db for particular song """
info = self.song_info
if info['title'] == '' or 'Track' in info['title']:
# Converting song_name into lower case so that we don't have to
# search for all .MP3 combination like(mp3,mP3,Mp3,MP3)
song_name = song_name.lower()
if '.mp3' in song_name:
pos = song_name.find('.mp3')
info['title'] = song_name[:pos]
# Finding song information in DB of music brainz and taking only 1
# result in response by using limit=1
self.data = mbz.search_recordings(query=info['title'], limit=1,
artist=info['artist'], release=info['album'],
date=str(info['year']), qdur=str(info['duration'])
)
[docs] def convert_to_unicode(self, string):
""" Converting string into unicode string using UTF-8 format """
return string.decode('utf-8')
[docs] def save_info(self, song_name):
""" Saving new song info """
# Loading new changed song info
info = self.song_info
# Loading Song
try:
# Updating title of song
try:
self.mp3file['title'] = self.convert_to_unicode(info['title'])
except:
pass
# Updating artist of song
try:
self.mp3file['artist'] = self.convert_to_unicode(info['artist'])
except:
pass
# Updating song album
try:
self.mp3file['album'] = self.convert_to_unicode(info['album'])
except:
pass
# Updating release_date of song
try:
self.mp3file['date'] = self.convert_to_unicode(info['year'])
except:
pass
except:
pass
try:
# Saving new info back to song properties
self.mp3file.save()
except:
pass
# Updating song name to title of song
try:
if info['title'] != '':
os.rename(song_name, info['title']+'.mp3')
except:
pass
[docs] def update_id3(self):
""" Function calling all other functions to update song info """
self.authenticate()
for song in self.songs:
self.find_info(song)
if self.song_info != {}:
self.search_musicbrainz(song)
self.extract_info()
self.save_info(song)
[docs]class UnpackFolders(PackSongs):
"""
It unpacks all the folders so that all songs comes out from folders and
reside at one place
"""
[docs] def list_folders(self):
""" Listing all folders """
self.folders_to_unpack = [i for i in os.listdir('.')
if(os.path.isdir(i))]
[docs] def move_song(self, folder_name, song):
""" Redefining the function which is Moving song to specific folder """
folder = folder_name
try:
# Moving a song from folder to current directory
move(song, '.')
# Storing songs list for creating log file
try:
self.moved_songs[folder].append(song)
except:
self.moved_songs[folder] = [song]
except:
# Storing songs list for creating log file
try:
self.unmoved_songs[folder].append(song)
except:
self.unmoved_songs[folder] = [song]
[docs] def generate_log(self):
"""
Redefining the function which is Generating log according to
different types
"""
prev_songs = self.existing_songs
moved_songs = self.moved_songs
unmoved_songs = self.unmoved_songs
# Opening a log file for
fh = open('UnpackLog.txt', 'w')
# Writing previously existing songs in log file
fh.write('*** Previously existing Songs before unpacking ***\n\n')
for index,song in enumerate(prev_songs):
fh.write(str(index)+'. '+str(song)+'\n')
# Writing moved songs in log file
fh.write('\n\n\n*** Songs MOVED while unpacking ***\n')
for folder in moved_songs:
fh.write('\n\n'+str(folder)+':\n')
for index,song in enumerate(moved_songs[folder]):
fh.write(str(index)+'. '+str(song)+'\n')
# Writing unmoved songs in log file
fh.write('\n\n\n*** Songs UNMOVED while unpacking ***\n')
for folder in unmoved_songs:
fh.write('\n\n'+str(folder)+':\n')
for index,song in enumerate(unmoved_songs[folder]):
fh.write(str(index)+'. '+str(song)+'\n')
[docs] def unpack(self):
""" Running all other functions to unpack folders """
# Making folders to create log file
self.existing_songs = self.songs
self.moved_songs = {}
self.unmoved_songs = {}
# Listing all folders inside given folder
self.list_folders()
# Traversing all folder to unpack them
for folder in self.folders_to_unpack:
# Listing all songs under a folder
songs = self.list_mp3files(folder)
# Traversing all songs and moving them
for song in songs:
self.move_song(folder, song)
[docs]def main():
"""
Main function which is asking user for choices and making appropriate
class objects and calling appropriate functions corresponding to them
"""
# User Choice Menu
options = "1. Pack Songs into Folders.\n2. Unpack all Songs from Folders\n\
3. Update Properties(ID3) of songs.\nEnter Choice:"
menu = "1. TITLE\n2. ARTIST\n3. ALBUM\n4. YEAR\n5. DURATION\n6. COMMENT"
tags = {1: 'title', 2: 'artist', 3: 'album', 4: 'year', 5: 'duration',
6: 'comment'}
print(options)
choice = input()
# Input folder would be the folder which contains all songs.
input_folder = os.getcwd()
if choice == 1:
print("'mp3fm' gives you the choice to move songs into folders \
according to given 6 categories:")
print(menu)
print "Enter Number corresponding to above given choices: ",
tag = tags[input()]
mp3fm = PackSongs(input_folder, tag)
mp3fm.put_songs()
mp3fm.generate_log()
print("*** Folders made with a LOG file containing Songs Info. \
present in every folder ***")
elif choice == 2:
mp3fm = UnpackFolders(input_folder)
mp3fm.unpack()
mp3fm.generate_log()
elif choice == 3:
mp3fm = UpdateSongInfo(input_folder)
mp3fm.update_id3()
if __name__ == '__main__':
main()