Skip to content

Commit 57c07b2

Browse files
committed
music-organizer: Add argparse descriptions and stub a global mode.
1 parent cf8d505 commit 57c07b2

1 file changed

Lines changed: 107 additions & 64 deletions

File tree

python2.7/music-organizer.py

Lines changed: 107 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,132 @@
11
#!/usr/bin/env python2.7
2+
#
3+
# music-organizer.py
4+
# Brandon Amos <http://bamos.io/>
5+
# 2014.4.19
26

7+
import argparse
38
import os
49
import re
510
import shutil
611
import sys
712
from mutagen.easyid3 import EasyID3
813

9-
emptyChars = re.compile(r"[(),.'\\\?\#]")
14+
parser = argparse.ArgumentParser(
15+
description='''Organizes a music collection using tag information.
16+
The directory format is that the music collection consists of
17+
artist subdirectories, and there are 2 modes to operate on
18+
the entire collection or a single artist.
19+
All names are made lowercase and separated by dashes for easier
20+
navigation in a Linux filesystem.'''
21+
)
22+
parser.add_argument('--delete-conflicts', action='store_true',
23+
dest='delete_conflicts',
24+
help='''If an artist has duplicate tracks with the same name,
25+
delete them. Note this might always be best in case an artist
26+
has multiple versions. To keep multiple versions,
27+
fix the tag information.''')
28+
parser.add_argument('--collection', action='store_true',
29+
help='TODO')
30+
parser.add_argument('--artist', action='store_true',
31+
help='TODO')
32+
args = parser.parse_args()
33+
34+
if args.collection and args.artist:
35+
print("Error: Only provide 1 of '--collection' or '--artist'.")
36+
sys.exit(-1)
37+
elif not (args.collection or args.artist):
38+
print("Error: Mode '--collection' or '--artist' not provided.")
39+
sys.exit(-1)
40+
41+
# Maps a string such as 'The Beatles' to 'the-beatles'.
1042
def toNeat(s):
11-
s = s.lower().replace(" ","-").replace("&","and").replace("*","-")
12-
s = emptyChars.sub("", s)
43+
s = s.lower().replace("&","and")
44+
s = re.sub(r"[()\[\],.'\\\?\#/\!]", "", s)
45+
s = re.sub(r"[ \*\_]", "-", s)
46+
s = re.sub("-+", "-", s)
1347
search = re.search("[^0-9a-z\-]", s)
1448
if search:
1549
print("Error: Unrecognized character in '" + s + "'")
1650
sys.exit(-42)
1751
return s
1852

19-
artists = set()
20-
valid = {"yes":True, "y":True, "no":False, "n":False}
21-
for dirname, dirnames, filenames in os.walk('.'):
22-
# Make sure there aren't a lot of different artists
23-
# in case this was called from the wrong directory.
24-
for filename in filenames:
25-
try:
26-
audio = EasyID3(os.path.join(dirname, filename))
27-
artist = audio['artist'][0].decode()
28-
artists.add(artist)
29-
except:
30-
pass
31-
32-
if len(artists) > 2:
33-
while True:
34-
print("Warning: More than 2 artists found.")
35-
print("This will move all songs to the current directory.")
36-
print("Continue? yes/no")
37-
choice = raw_input().lower()
38-
if choice in valid:
39-
if valid[choice]: break
40-
else:
41-
print("Exiting.")
42-
sys.exit(-1)
43-
44-
delete_dirs = []
45-
for dirname, dirnames, filenames in os.walk('.'):
46-
# Move all the files to the root directory.
47-
for filename in filenames:
48-
ext = os.path.splitext(filename)[1]
49-
if ext == ".mp3":
50-
fullPath = os.path.join(dirname, filename)
51-
print("file: " + str(fullPath))
52-
53+
def artist():
54+
artists = set()
55+
valid = {"yes":True, "y":True, "no":False, "n":False}
56+
for dirname, dirnames, filenames in os.walk('.'):
57+
# Make sure there aren't a lot of different artists
58+
# in case this was called from the wrong directory.
59+
for filename in filenames:
5360
try:
54-
audio = EasyID3(fullPath)
55-
title = audio['title'][0].decode()
56-
print(" title: " + title)
57-
except: title = None
61+
audio = EasyID3(os.path.join(dirname, filename))
62+
artist = audio['artist'][0].decode()
63+
artists.add(artist)
64+
except:
65+
pass
5866

59-
if not title:
60-
print("Error: title not found for '" + filename + "'")
61-
sys.exit(-42)
67+
if len(artists) > 2:
68+
while True:
69+
print("Warning: More than 2 artists found.")
70+
print("This will move all songs to the current directory.")
71+
print("Continue? yes/no")
72+
choice = raw_input().lower()
73+
if choice in valid:
74+
if valid[choice]: break
75+
else:
76+
print("Exiting.")
77+
sys.exit(-1)
6278

63-
neatTitle = toNeat(title)
64-
print(" neat-title: " + neatTitle)
79+
delete_dirs = []
80+
for dirname, dirnames, filenames in os.walk('.'):
81+
# Move all the files to the root directory.
82+
for filename in filenames:
83+
ext = os.path.splitext(filename)[1]
84+
if ext == ".mp3":
85+
fullPath = os.path.join(dirname, filename)
86+
print("file: " + str(fullPath))
6587

66-
newFullPath = os.path.join(".", neatTitle + ext) # Remove subdirectories.
67-
print(" newFullPath: " + newFullPath)
88+
try:
89+
audio = EasyID3(fullPath)
90+
title = audio['title'][0].decode()
91+
print(" title: " + title)
92+
except: title = None
6893

69-
if newFullPath != fullPath:
70-
if os.path.isfile(newFullPath):
71-
print("Error: File exists: '" + newFullPath + "'")
94+
if not title:
95+
print("Error: title not found for '" + filename + "'")
7296
sys.exit(-42)
7397

74-
os.rename(fullPath, newFullPath)
75-
os.chmod(newFullPath, 0644)
76-
elif ext == ".pdf":
77-
pass
78-
else:
79-
print("Error: Unrecognized file extension in '" + filename + "'")
80-
sys.exit(-42)
98+
neatTitle = toNeat(title)
99+
print(" neat-title: " + neatTitle)
100+
101+
newFullPath = os.path.join(".", neatTitle + ext)
102+
print(" newFullPath: " + newFullPath)
103+
104+
if newFullPath != fullPath:
105+
if os.path.isfile(newFullPath):
106+
if args.delete_conflicts:
107+
os.remove(fullPath)
108+
print("File exists: '" + newFullPath + "'")
109+
print("Deleted: '" + fullPath + "'")
110+
else:
111+
print("Error: File exists: '" + newFullPath + "'")
112+
sys.exit(-42)
113+
else:
114+
os.rename(fullPath, newFullPath)
115+
os.chmod(newFullPath, 0644)
116+
elif ext == ".pdf":
117+
pass
118+
else:
119+
print("Error: Unrecognized file extension in '" + filename + "'")
120+
sys.exit(-42)
121+
122+
# Delete all subdirectories.
123+
for subdirname in dirnames:
124+
delete_dirs.append(subdirname)
81125

82-
# Delete all subdirectories.
83-
for subdirname in dirnames:
84-
delete_dirs.append(subdirname)
126+
for d in delete_dirs:
127+
shutil.rmtree(d,ignore_errors=True)
85128

86-
for d in delete_dirs:
87-
shutil.rmtree(d,ignore_errors=True)
129+
print("\nComplete!")
88130

89-
print("\nComplete!")
131+
if args.artist:
132+
artist()

0 commit comments

Comments
 (0)