diff --git a/.gitignore b/.gitignore index bee8a64..8d35cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__ +*.pyc diff --git a/python2.7/audioFunction.py b/python2.7/audioFunction.py new file mode 100755 index 0000000..95effbc --- /dev/null +++ b/python2.7/audioFunction.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python2.7 + +from mutagen.easyid3 import EasyID3 +from mutagen.oggvorbis import OggVorbis +import os + +def returnAudio(path): + ext = os.path.splitext(path)[1] + if ext == ".mp3": + audio = EasyID3(path) + elif ext == ".ogg": + audio = OggVorbis(path) + return audio diff --git a/python2.7/fix-music-tags.py b/python2.7/fix-music-tags.py index 8e07eb2..8872499 100755 --- a/python2.7/fix-music-tags.py +++ b/python2.7/fix-music-tags.py @@ -8,13 +8,16 @@ """ from mutagen.easyid3 import EasyID3 +from mutagen.oggvorbis import OggVorbis +import os import argparse import glob - +import re +import tracknumber +import audioFunction def fixTags(fname, keep): - audio = EasyID3(fname) - + audio = audioFunction.returnAudio(fname) delKeys = [] for k, v in audio.items(): if k not in keep: @@ -28,10 +31,21 @@ def fixTags(fname, keep): parser = argparse.ArgumentParser() parser.add_argument('directory', help='Directory with mp3 files to fix.') parser.add_argument('--keep', default=['title', 'artist', 'album', 'genre'], - type=str, nargs='+', metavar='TAG', - help="Tags to keep. Default: title, artist, album, genre") + type=str, nargs='+', metavar='TAG', + help="Tags to keep. Default: title, artist, album, genre") + parser.add_argument('--fixnumber', action='store_true', + help="Trying to fix song number.") args = parser.parse_args() - for fname in glob.glob("{}/*.mp3".format(args.directory)): + types = ('*.mp3', '*.ogg') + files_grabbed = [] + for files in types: + files_grabbed.extend( + glob.glob("{}/*{}".format(args.directory, files)) + ) + + for fname in files_grabbed: print("Fixing tags for {}".format(fname)) fixTags(fname, args.keep) + if args.fixnumber: + tracknumber.formatNumber(fname) diff --git a/python2.7/music-autoplaylists.py b/python2.7/music-autoplaylists.py index 15759bf..85a0943 100755 --- a/python2.7/music-autoplaylists.py +++ b/python2.7/music-autoplaylists.py @@ -15,15 +15,19 @@ import sys from mutagen.easyid3 import EasyID3 from collections import defaultdict - +import toNeat def main(): parser = argparse.ArgumentParser() parser.add_argument('--musicDir', type=str, default='.') parser.add_argument('--playlistDir', type=str, default='./playlists/auto') + parser.add_argument('-a','--album', action='store_true', help='''Album mode''') + parser.add_argument('-g','--genre', action='store_true', help='''Genre mode''') + parser.add_argument('-C','--capital', action='store_true', help='''Changes names to capital''') args = parser.parse_args() - genres = defaultdict(list) + titleList = defaultdict(list) + for dpath, dnames, fnames in os.walk(args.musicDir): if '.git' in dpath: continue @@ -32,48 +36,31 @@ def main(): continue p = os.path.abspath(os.path.join(dpath, fname)) audio = EasyID3(p) - if 'genre' in audio: - assert(len(audio['genre']) == 1) - genre = toNeat(str(audio['genre'][0])) - else: - genre = 'Unknown' - genres[genre].append(p) + if args.genre: + if 'genre' in audio: + assert(len(audio['genre']) == 1) + title = toNeat.toNeat(str(audio['genre'][0]), args) + else: + title = 'Unknown' + titleList[title].append(p) + elif args.album: + if 'album' in audio: + assert(len(audio['album']) == 1) + title = toNeat.toNeat(str(audio['album'][0]), args) + else: + title = 'Unknown' + titleList[title].append(p) if os.path.exists(args.playlistDir): shutil.rmtree(args.playlistDir) os.makedirs(args.playlistDir) - for genre, songs in genres.items(): - p = os.path.join(args.playlistDir, genre + '.m3u') + for titleList, songs in titleList.items(): + p = os.path.join(args.playlistDir, title + '.m3u') print("Creating playlist: {}".format(p)) with open(p, 'w') as f: f.write("#EXTM3U\n") f.write("\n".join(sorted(songs)) + "\n") -# Maps a string such as 'The Beatles' to 'the-beatles'. - - -def toNeat(s): - s = s.lower().replace("&", "and") - - # Put spaces between and remove blank characters. - blankCharsPad = r"()\[\],.\\\?\#/\!\$\:\;" - blankCharsNoPad = r"'\"" - s = re.sub(r"([" + blankCharsPad + r"])([^ ])", "\\1 \\2", s) - s = re.sub("[" + blankCharsPad + blankCharsNoPad + "]", "", s) - - # Replace spaces with a single dash. - s = re.sub(r"[ \*\_]+", "-", s) - s = re.sub("-+", "-", s) - s = re.sub("^-*", "", s) - s = re.sub("-*$", "", s) - - # Ensure the string is only alphanumeric with '-', '+', and '='. - search = re.search("[^0-9a-z\-\+\=]", s) - if search: - print("Error: Unrecognized character in '" + s + "'") - sys.exit(-42) - return s - if __name__ == '__main__': main() diff --git a/python2.7/music-organizer.py b/python2.7/music-organizer.py index 1bed95e..ce4c933 100755 --- a/python2.7/music-organizer.py +++ b/python2.7/music-organizer.py @@ -20,190 +20,142 @@ import re import shutil import sys +import toNeat from mutagen.easyid3 import EasyID3 +from mutagen.oggvorbis import OggVorbis +import tracknumber +import audioFunction parser = argparse.ArgumentParser( - description='''Organizes a music collection using tag information. + description='''Organizes a music collection using tag information. The directory format is that the music collection consists of artist subdirectories, and there are 2 modes to operate on the entire collection or a single artist. All names are made lowercase and separated by dashes for easier navigation in a Linux filesystem.''' -) -parser.add_argument('--delete-conflicts', action='store_true', - dest='delete_conflicts', - help='''If an artist has duplicate tracks with the same name, + ) +parser.add_argument('-d','--delete-conflicts', action='store_true', + dest='delete_conflicts', + help='''If an artist has duplicate tracks with the same name, delete them. Note this might always be best in case an artist has multiple versions. To keep multiple versions, fix the tag information.''') -parser.add_argument('--ignore-multiple-artists', action='store_true', - dest='ignore_multiple_artists', - help='''This script will prompt for confirmation if an artist - directory has songs with more than 2 different tags. - This flag disables the confirmation and won't perform - this check.''') -parser.add_argument('--collection', action='store_true', - help='''Operate in 'collection' mode and run 'artist' mode - on every subdirectory.''') -parser.add_argument('--artist', action='store_true', - help='''Operate in 'artist' mode and copy all songs to the - root of the directory and cleanly format the names to - be easily typed and navigated in a shell.''') -parser.add_argument('--delete-unrecognized-extensions', action='store_true', - dest='delete_unrecognized') +parser.add_argument('-e','--delete-unrecognized-extensions', action='store_true', + dest='delete_unrecognized') +parser.add_argument('-A','--album', action='store_true', + dest='album', + help='''Adds album folder inside the artist folder to sort out + albums''') +parser.add_argument('-n','--numbering', action='store_true', + dest='numbering', + help='''Adds numbering in front of sorted songs''') +parser.add_argument('-C','--capital', action='store_true', + dest='capital', + help='''Makes the first letter of a song capital''') args = parser.parse_args() -if args.collection and args.artist: - print("Error: Only provide 1 of '--collection' or '--artist'.") - sys.exit(-1) -elif not (args.collection or args.artist): - print("Error: Mode '--collection' or '--artist' not provided.") - sys.exit(-1) - - -# Maps a string such as 'The Beatles' to 'the-beatles'. -def toNeat(s): - s = s.lower().replace("&", "and") - - # Put spaces between and remove blank characters. - blankCharsPad = r"()\[\],.\\\?\#/\!\$\:\;" - blankCharsNoPad = r"'\"" - s = re.sub(r"([" + blankCharsPad + r"])([^ ])", "\\1 \\2", s) - s = re.sub("[" + blankCharsPad + blankCharsNoPad + "]", "", s) - - # Replace spaces with a single dash. - s = re.sub(r"[ \*\_]+", "-", s) - s = re.sub("-+", "-", s) - s = re.sub("^-*", "", s) - s = re.sub("-*$", "", s) - - # Ensure the string is only alphanumeric with '-', '+', and '='. - search = re.search("[^0-9a-z\-\+\=]", s) - if search: - print("Error: Unrecognized character in '" + s + "'") - sys.exit(-42) - return s - - -def artist(artistDir): - print("Organizing artist '" + artistDir + "'.") - if not args.ignore_multiple_artists: - artists = set() - for dirname, dirnames, filenames in os.walk(artistDir): - # Make sure there aren't a lot of different artists - # in case this was called from the wrong directory. - for filename in filenames: - try: - audio = EasyID3(os.path.join(dirname, filename)) - artist = audio['artist'][0].decode() - artists.add(artist) - except: - pass - - if len(artists) > 2: - while True: - print("Warning: More than 2 artists found in '{}'.".format( - artistDir)) - print("This will move all songs to the '{}' directory.".format( - artistDir)) - print("Continue? yes/no") - choice = raw_input().lower() - valid = {"yes": True, "y": True, "no": False, "n": False} - if choice in valid: - if valid[choice]: - break - else: - print("Exiting.") - sys.exit(-1) - +def artist(): + print("Organizing artist") delete_dirs = [] - for dirname, dirnames, filenames in os.walk(artistDir): + for dirname, dirnames, filenames in os.walk("."): # Move all the files to the root directory. for filename in filenames: - ext = os.path.splitext(filename)[1] - if ext == ".mp3": - fullPath = os.path.join(dirname, filename) - print("file: " + str(fullPath)) - - try: - audio = EasyID3(fullPath) - title = audio['title'][0].encode('ascii', 'ignore') - print(" title: " + title) - except: - title = None - - if not title: - print("Error: title not found for '" + filename + "'") - sys.exit(-42) - - neatTitle = toNeat(title) - print(" neatTitle: " + neatTitle) - - newFullPath = os.path.join(artistDir, neatTitle + ext) - print(" newFullPath: " + newFullPath) - - if newFullPath != fullPath: - if os.path.isfile(newFullPath): - if args.delete_conflicts: - os.remove(fullPath) - print("File exists: '" + newFullPath + "'") - print("Deleted: '" + fullPath + "'") - else: - print("Error: File exists: '" + newFullPath + "'") - sys.exit(-42) - else: - os.rename(fullPath, newFullPath) - elif ext == ".pdf": - pass + fullPath = os.path.join(dirname, filename) + # formating song + returned = song(fullPath) + if returned is "succes": + print("succesful formated") + elif returned is "delete": + print"deleted remaining files in folder" else: - if not args.delete_unrecognized: - print("Error: Unrecognized file extension in '{}'.".format( - filename)) - sys.exit(-42) + returnedlist = returned.split('/') + for extdir in returnedlist: + if extdir in delete_dirs: + delete_dirs.remove(extdir) - # Delete all subdirectories. + # Add subdirectories to a list for subdirname in dirnames: delete_dirs.append(subdirname) + # deletes subdirectories for d in delete_dirs: - shutil.rmtree(os.path.join(artistDir, d), ignore_errors=True) - + shutil.rmtree(os.path.join(".", d), ignore_errors=True) def song(filename): - if filename[0] == '.': - print("Ignoring dotfile: '{}'".format(filename)) - return - print("Organizing song '" + filename + "'.") +# if filename[0] == '.': +# print("Ignoring dotfile: '{}'".format(filename)) +# return ext = os.path.splitext(filename)[1] - try: - audio = EasyID3(filename) - artist = audio['artist'][0].encode('ascii', 'ignore') - title = audio['title'][0].encode('ascii', 'ignore') - print(" artist: " + artist) - print(" title: " + title) - except: - artist = None - title = None - neatArtist = toNeat(artist) - neatTitle = toNeat(title) - print(" neatArtist: " + neatArtist) - print(" neatTitle: " + neatTitle) - if not os.path.isdir(neatArtist): - os.mkdir(neatArtist) - newFullPath = os.path.join(neatArtist, neatTitle + ext) - os.rename(filename, newFullPath) + if ext in (".mp3" , ".ogg"): + print("Organizing song '" + filename + "'.") + try: + audio = audioFunction.returnAudio(filename) + artist = audio['artist'][0].encode('ascii', 'ignore') + title = audio['title'][0].encode('ascii', 'ignore') + if args.album: + album = audio['album'][0].encode('ascii', 'ignore') + if args.numbering: + neatTracknumber = tracknumber.getTracknumber(filename) + print(" artist: " + artist) + print(" title: " + title) + if args.album: + print(" album: " + album) + except: + artist = None + title = None + if args.album: + album = None + if args.numbering: + neatTracknumber = None + + neatArtist = toNeat.toNeat(artist, args) + if args.numbering: + neatTitle = neatTracknumber + "." + toNeat.toNeat(title, args) + else: + neatTitle = toNeat.toNeat(title, args) + if args.album: + neatAlbum = toNeat.toNeat(album, args) + print(" neatArtist: " + neatArtist) + print(" neatTitle: " + neatTitle) + if args.album: + print(" neatAlbum: " + neatAlbum) + if not os.path.isdir(neatArtist): + os.mkdir(neatArtist) + if args.album: + if not os.path.isdir(neatArtist + "/" + neatAlbum): + os.mkdir(neatArtist + "/" + neatAlbum) + newFullPath = os.path.join(neatArtist, neatAlbum, neatTitle + ext) + else: + newFullPath = os.path.join(neatArtist, neatTitle + ext) + + if newFullPath != filename: + if os.path.isfile(newFullPath): + if args.delete_conflicts: + os.remove(filename) + print("File exists: '" + newFullPath + "'") + print("Deleted: '" + filename + "'") + else: + print("Error: File exists: '" + newFullPath + "'") + return os.path.split(newFullPath)[0] + else: + os.rename(filename, newFullPath) + return "succes" + else: + if not args.delete_unrecognized: + print("Error: Unrecognized file extension in '{}'.".format(filename)) + sys.exit(-42) + else: + return "delete" + def collection(): for f in glob.glob('*'): if os.path.isdir(f): if f != 'iTunes' and f != 'playlists': - artist(f) + artist() elif os.path.isfile(f): song(f) -if args.artist: - artist('.') -else: - collection() +collection() print("\nComplete!") diff --git a/python2.7/toNeat.py b/python2.7/toNeat.py new file mode 100755 index 0000000..34211cd --- /dev/null +++ b/python2.7/toNeat.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python2.7 + +import re +import sys + +# Maps a string such as 'The Beatles' to 'the-beatles'. +def toNeat(s, args): + if args.capital: + s = s.title().replace("&", "and") + else: + s = s.lower().replace("&", "and") + + # Put spaces between and remove blank characters. + blankCharsPad = r"()\[\],.\\\?\#/\!\$\:\;" + blankCharsNoPad = r"'\"" + s = re.sub(r"([" + blankCharsPad + r"])([^ ])", "\\1 \\2", s) + s = re.sub("[" + blankCharsPad + blankCharsNoPad + "]", "", s) + + # Replace spaces with a single dash. + s = re.sub(r"[ \*\_]+", "-", s) + s = re.sub("-+", "-", s) + s = re.sub("^-*", "", s) + s = re.sub("-*$", "", s) + + # Ensure the string is only alphanumeric with '-', '+', and '='. + if args.capital: + search = re.search("[^0-9a-zA-Z\-\+\=]", s) + else: + search = re.search("[^0-9a-z\-\+\=]", s) + if search: + print("Error: Unrecognized character in '" + s + "'") + sys.exit(-42) + return s diff --git a/python2.7/tracknumber.py b/python2.7/tracknumber.py new file mode 100755 index 0000000..c541628 --- /dev/null +++ b/python2.7/tracknumber.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2.7 + +from mutagen.easyid3 import EasyID3 +from mutagen.oggvorbis import OggVorbis +import audioFunction +import os +import re + +def fixTracknumber(fname): + print("fixTracknumber") + audio = audioFunction.returnAudio(fname) + try: + tracknumber = re.findall(r'\d+', os.path.basename(fname).split(' ')[0])[0] + except: + tracknumber = "0" + audio['tracknumber'] = tracknumber.zfill(2) + return formatNumber(audio) + +def formatNumber(fname): + audio = audioFunction.returnAudio(fname) + if 'tracknumber' not in audio: + fixTracknumber(fname) + if '/' in audio['tracknumber'][0]: + audio['tracknumber'] = audio['tracknumber'][0].split('/')[0] + audio['tracknumber'][0] + audio.save() + return audio + +def getTracknumber(fname): + audio = formatNumber(fname) + return audio['tracknumber'][0].encode('ascii', 'ignore')