This is my first real program in python (and my first real program) and I would like to have input by some more advanced programmers on the code, on the writing style and on the amount of comments (is it clear enough?).
new_name(scheme)
does seems a bit bloated to me, but I can't figure out how to write it in a simpler way.
The program is made to run on Debian GNU/Linux exclusively (if I get a good feedback I'll port it to other distros)
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
rename-flac takes the information from FLAC metadata to batch rename
the files according to a filenaming scheme.
Usage:
rename-flac.py <scheme> <directory>
rename-flac.py -o
rename-flac.py (-h | --help)
rename-flac.py --version
Arguments:
<scheme> The filenaming scheme. Has to be between quotation marks
<directory> The path to the directory containing the album
Options:
-o Displays filenaming scheme options
-h --help Shows the help screen
--version Outputs version information
"""
from docopt import docopt # Creating command-line interface
import sys
import subprocess
from py.path import local
#TODO: write to work with Python 3
# Dependency check
programlist = ["flac", "python-py", "python-docopt"]
for program in programlist:
pipe = subprocess.Popen(
["dpkg", "-l", program], stdout=subprocess.PIPE)
dependency, error = pipe.communicate()
if pipe.returncode:
print """
%s is not installed: this program won't run correctly.
To instal %s, run: aptitude install %s
""" % (program, program, program)
sys.exit()
else:
pass
# Defining the function that fetches metadata and formats it
def metadata(filename):
filename = str(filename).decode("utf-8") # Since filename is a LocalPath
# Tracknumber
pipe = subprocess.Popen(
["metaflac", "--show-tag=tracknumber", filename],
stdout=subprocess.PIPE)
tracknumber, error = pipe.communicate()
tracknumber = tracknumber.replace("tracknumber=", "")
tracknumber = tracknumber.replace("TRACKNUMBER=", "")
tracknumber = tracknumber.replace("TrackNumber=", "")
tracknumber = tracknumber.rstrip() # Remove whitespaces
if int(tracknumber) < 10: # To achieve 01 instead of 1
if "0" in tracknumber:
pass
else:
tracknumber = "0" + tracknumber
else:
pass
# Title
pipe = subprocess.Popen(
["metaflac", "--show-tag=title", filename],
stdout=subprocess.PIPE)
title, error = pipe.communicate()
title = title.replace("TITLE=", "")
title = title.replace("title=", "")
title = title.rstrip()
# Artist
pipe = subprocess.Popen(
["metaflac", "--show-tag=artist", filename],
stdout=subprocess.PIPE)
artist, error = pipe.communicate()
artist = artist.replace("ARTIST=", "")
artist = artist.replace("artist=", "")
artist = artist.rstrip()
# Date
pipe = subprocess.Popen(
["metaflac", "--show-tag=date", filename],
stdout=subprocess.PIPE)
date, error = pipe.communicate()
date = date.replace("DATE=", "")
date = date.replace("date=", "")
date = date.rstrip()
# Album
pipe = subprocess.Popen(
["metaflac", "--show-tag=album", filename],
stdout=subprocess.PIPE)
album, error = pipe.communicate()
album = album.replace("ALBUM=", "")
album = album.replace("album=", "")
album = album.rstrip()
# Genre
pipe = subprocess.Popen(
["metaflac", "--show-tag=genre", filename],
stdout=subprocess.PIPE)
genre, error = pipe.communicate()
genre = genre.replace("GENRE=", "")
genre = genre.replace("genre=", "")
genre = genre.rstrip()
return tracknumber, title, artist, date, album, genre
# Defining the function that puts together the new name
def new_name(scheme):
scheme_list = permanent_scheme.rsplit("%")
formatter = {}
for option in scheme_list:
if "a" in option:
scheme = scheme.replace("%a", "%(artist)s")
formatter["artist"] = artist
elif "b" in option:
scheme = scheme.replace("%b", "%(album)s")
formatter["album"] = album
elif "d" in option:
scheme = scheme.replace("%d", "%(date)s")
formatter["date"] = date
elif "g" in option:
scheme = scheme.replace("%g", "%(genre)s")
formatter["genre"] = genre
elif "n" in option:
scheme = scheme.replace("%n", "%(tracknumber)s")
formatter["tracknumber"] = tracknumber
elif "t" in option:
scheme = scheme.replace("%t", "%(title)s")
formatter["title"] = title
else:
pass
return scheme, formatter
# Defining function that renames the files
def rename(root):
if "t" not in permanent_scheme: # To have different file names
print " %t has to be present in <scheme>"
sys.exit()
new_filename = scheme % formatter
if new_filename == filename.purebasename:
print "%s is already named correctly\n" % (title)
else:
filename.rename(filename.new(purebasename=new_filename))
# Importing command line arguments
args = docopt(__doc__, version="rename-flac 1.0")
if args["<directory>"] != "None":
root = local(args["<directory>"])
scheme = args["<scheme>"]
permanent_scheme = args["<scheme>"]
else:
pass
# Filenaming scheme options
if args["-o"] == True:
print"""
Theses are the options you can use to define the filenaming scheme:
%a = Artist | %b = Album | %d = Date
%g = Genre | %n = Tracknumber | %t = Title
"""
sys.exit()
# Renaming files
if args["<scheme>"] != "None":
for filename in root.visit(fil="*.flac", rec=True):
tracknumber, title, artist, date, album, genre = metadata(filename)
scheme, formatter = new_name(permanent_scheme)
rename(root)
print "Files renamed"
else:
pass
After a few days trying to understand the awesome reviews I got, here is the revised code. Even though it might look a bit like Gareth Rees' reviewed code, I rewrote everything (no cheats!).
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
rename-flac takes the information from FLAC metadata to batch rename
the files according to a filenaming scheme.
Usage:
rename-flac.py [--verbose] <scheme> <directory>
rename-flac.py (-h | --help)
rename-flac.py --version
Arguments:
<scheme> The filenaming scheme. Has to be between quotation marks
<directory> The path to the directory containing the album
Options:
-h --help Shows the help screen
--version Outputs version information
--verbose Runs the program as verbose mode
These are the options you can use to define the filenaming scheme:
%a = Artist | %b = Album | %d = Date
%g = Genre | %n = Tracknumber | %t = Title
"""
try:
from docopt import docopt # Creating command-line interface
except ImportError:
sys.stderr.write("""
%s is not installed: this program won't run correctly.
To install %s, run: aptitude install %s
""" % ("python-docopt", "python-docopt", "python-docopt"))
import sys
import subprocess
import re
import os
#TODO: write to work with Python 3
TAGS = dict(a='artist', b='album', d='date',
g='genre', n='tracknumber', t='title')
# Defining the function that fetches metadata and formats it
def metadata(filepath):
args = ["--show-tag=%s" % tag for tag in TAGS.values()]
tags = ["%s=" % tag for tag in TAGS.values()]
formatter = dict()
pipe = subprocess.Popen(["metaflac"] + args + [filepath],
stdout=subprocess.PIPE)
output, error = pipe.communicate()
if pipe.returncode:
raise IOError("metaflac failed: %s" % error)
for tag in tags:
x = re.compile(re.escape(tag), re.IGNORECASE)
output = x.sub("", output)
output = output.splitlines()
formatter["artist"] = output[0]
formatter["album"] = output[1]
formatter["date"] = output[2]
formatter["genre"] = output[3]
formatter["title"] = output[4]
formatter["tracknumber"] = output[5].zfill(2)
return formatter
# Defining function that renames the files
def rename(scheme, dirname, filename, args):
filepath = os.path.join(dirname, filename)
new_filename = scheme % metadata(filepath) + ".flac"
if new_filename == filename:
print("%s is already named correctly") % (filename)
else:
new_filepath = os.path.join(dirname, new_filename)
os.rename(filepath, new_filepath)
if args["--verbose"] == True:
print("%s >> %s") % (filename, new_filename)
# Defining main function
def main():
args = docopt(__doc__, version="rename-flac 1.0")
scheme = args["<scheme>"]
if not re.search("%[tn]", scheme): # To have a unique filename
sys.stderr.write("%t or %n has to be present in <scheme>\n")
return
scheme = re.sub('%%([%s])' % ''.join(TAGS.keys()),
lambda m: '%%(%s)s' % TAGS[m.group(1)],
scheme)
for dirname, _, filenames in os.walk(
args["<directory>"],
topdown=False):
for filename in filenames:
if os.path.splitext(filename)[1] == ".flac":
try:
rename(scheme, dirname, filename, args)
except KeyboardInterrupt:
raise
print("Files renamed")
# Calling main function
if __name__ == "__main__":
main()