Now that I've gotten back in the python groove somewhat, wanted to make a playlist script that recursively went through a folder, found all files in a sub-folder and create a playlist from them. Implementation described and link to github repository.
Now that I've gotten back in the python groove somewhat, I wanted to make a small playlist script that recursively went through a folder, found all files in a sub-folder and create a playlist from them. Unlike the facebook to youtube script, this would need no APIs, third-party modules or other fluff. I'll go over the script; you can find a copy here: python playlist maker on github.
First, ask the user what directory they wanted to run the script on. DEFAULT_DIR is found in settings.py. Using from and import along with a separate settings file is probably the easiest way to allow a user to set defaults. Avoid magic constants that are not explained or easily found.
- #Import settings from settings.py
- from settings import *
- def folderBrowser():
- #Opens a folder
- import Tkinter, tkFileDialog
- root = Tkinter.Tk()
- root.withdraw()
- dir = tkFileDialog.askdirectory(parent=root,initialdir=DEFAULT_DIR,title='Please select a directory')
- return dir
Next, you need to crawls through a directory and searches only for valid files. A nifty feature is using set.intersection() to find if a particular filename contains any valid format inputs (as decided by the user). Because audio programs won't play invalid audio files, improperly labeled files with audio extensions will be included, but this was a trade-off of accuracy vs. speed of the script. Check whether a file is valid would take too long, especially for larger libraries. After finding all audio files in a folder, the function calls itself by passing the sub-folders in a particular folder. This will in effect crawl until it finds no more folders. Files in the root directory are found separately to avoid infinite looping since I use the last break instead of a clear base case. Use a base case if doing recursion otherwise.
- def fileTree(dir,relDir):
- #Crawls through a directory and finds audio files, returns list of files
- print dir
- #Variable to save files to
- filesToSave = []
- #Valid mp3 files
- validFiles = set(VALID_AUDIO_FORMATS)
- #Crawl through directory
- for dirname, dirnames, filenames in os.walk(dir):
- #Crawl through each directory in a folder
- for subdirname in dirnames:
- #Next level relative path
- relDir2 = os.path.join(relDir,subdirname)
- #Evaluate each file in directory, if audio, add to playlist
- for filename in os.listdir(os.path.join(dir,subdirname)):
- #If there is an intersection, means valid extension, add to playlist
- if validFiles.intersection(set(filename.split('.'))):
- filesToSave.append(os.path.join(relDir2,filename))
- #Recursion here
- #After finding all file in a subdirectory, crawl through its folders
- filesToSave=filesToSave+fileTree(os.path.join(dir,subdirname),relDir2)
- #Finally, get files in root directory, see above for explanation of details
- for filename in os.listdir(os.path.join(dir,'')):
- if validFiles.intersection(set(filename.split('.'))):
- filesToSave.append(os.path.join(relDir,filename))
- #Break the loop so we don't crawl through subdirectories and add playlists there
- break
- #Return files found
- return filesToSave
Then you'll want to go through all the files found and add them to the playlist. The current iteration of this script makes extended .m3u files and thus I strip the file extension (filename.split('.') then '.'.join(filenameName[0:-1])) and include this as the name. Again, this is quicker than reading the properties of each audio file; though, that can be added later. PLAYLIST_ID_TAG (originally _ssx42) is added to each playlist so they can be easily searched for and deleted. Basically SSX (i.e. awesomeness distinguished as a game) was on my mind...don't ask. Moving on. Also, using sorted(set(files)) allows removing of duplicate files from the playlist.
- def saveFilesToPlaylist(files,dir,dirname,CURRENT_DIR):
- #Saves a list of files to a playlist
- #Open connection, uses root as name for playlist
- playlist = open(os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u'),'w')
- #Output playlist location
- print os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u')
- #Extended M3U format used here
- playlist.write('#EXTM3U
- ')
- #Remove duplicates
- files = sorted(set(files))
- #Loop through each file
- for filename in files:
- actualFilename = os.path.basename(filename)
- #Split then rejoin, remove extension
- filenameName = actualFilename.split('.')
- filenameName = '.'.join(filenameName[0:-1])
- #Add name based filename
- playlist.write('#EXTINF:'+filenameName+'
- ')
- #Write relative location of file
- playlist.write(os.path.join(filename)+'
- ')
- playlist.close()
The main function basically ask for the folder to browse, makes a log file (to check which playlists were created), and then crawls through each folder in the root. Only folders in the root have playlists added to them. This avoids having a playlist for each sub-folder; though that ability can be added by turning off the break in the main for loop.
- def main():
- #Ask user for folder
- dir = folderBrowser()
- #If no folder given, use default
- CURRENT_DIR = dir or DEFAULT_DIR
- #Print the current directory
- print CURRENT_DIR
- #Log all the playlist created
- logFileName = os.path.basename(CURRENT_DIR)
- logFile = open(logFileName+'.log','w')
- #Change to directory
- os.chdir(CURRENT_DIR)
- #Walk along each first level folder in directory
- for dirname, dirnames, filenames in os.walk('.'):
- print dirname
- for subdirname in dirnames:
- #Basename of the current directory
- dirnameTitle = os.path.basename(subdirname)
- #Get all files under first level folder
- filesToSave = fileTree(subdirname,'')
- #Save files to playlist, using top-most folder
- saveFilesToPlaylist(filesToSave,subdirname,dirnameTitle,CURRENT_DIR)
- #Add playlist to log
- logFile.write(os.path.join(CURRENT_DIR,dirnameTitle,dirnameTitle+'_ssx42.m3u')+'
- ')
- #Break the loop so we don't crawl through subdirectories and add playlists there
- break
- #Close connection to log file
- logFile.close()
- #We are in main, run
- if __name__ == '__main__':
- main()
So there you have it, a simple script to help create a plethora of playlists very quickly with little input on your end. Just choose the root folder and wait a couple seconds. Due to the unique identifier (PLAYLIST_ID_TAG), it should be easy to remove the playlist if they aren't needed. To update the playlists, simple re-run the script.
See below for the full scripts.
- #!/Python27/env python
- #Biafra Ahanonu
- #2012.12.02
- #Makes .m3u extended playlist at first level folders in a directory
- #Modules used
- import os,re,time
- #Import settings from settings.py
- from settings import *
- #Help filter out duplicates
- from sets import Set
- def folderBrowser():
- #Opens a folder
- import Tkinter, tkFileDialog
- root = Tkinter.Tk()
- root.withdraw()
- dir = tkFileDialog.askdirectory(parent=root,initialdir=DEFAULT_DIR,title='Please select a directory')
- return dir
- def fileTree(dir,relDir):
- #Crawls through a directory and finds audio files, returns list of files
- print dir
- #Variable to save files to
- filesToSave = []
- #Valid mp3 files
- validFiles = set(VALID_AUDIO_FORMATS)
- #Crawl through directory
- for dirname, dirnames, filenames in os.walk(dir):
- #Crawl through each directory in a folder
- for subdirname in dirnames:
- #Next level relative path
- relDir2 = os.path.join(relDir,subdirname)
- #Evaluate each file in directory, if audio, add to playlist
- for filename in os.listdir(os.path.join(dir,subdirname)):
- #If there is an intersection, means valid extension, add to playlist
- if validFiles.intersection(set(filename.split('.'))):
- filesToSave.append(os.path.join(relDir2,filename))
- #Recursion here
- #After finding all file in a subdirectory, crawl through its folders
- filesToSave=filesToSave+fileTree(os.path.join(dir,subdirname),relDir2)
- #Finally, get files in root directory, see above for explanation of details
- for filename in os.listdir(os.path.join(dir,'')):
- if validFiles.intersection(set(filename.split('.'))):
- filesToSave.append(os.path.join(relDir,filename))
- #Break the loop so we don't crawl through subdirectories and add playlists there
- break
- #Return files found
- return filesToSave
- def saveFilesToPlaylist(files,dir,dirname,CURRENT_DIR):
- #Saves a list of files to a playlist
- #Open connection, uses root as name for playlist
- playlist = open(os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u'),'w')
- #Output playlist location
- print os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u')
- #Extended M3U format used here
- if EXTENDED_M3U == 1:
- playlist.write('#EXTM3U\n')
- #Remove duplicates
- files = sorted(set(files))
- #Loop through each file
- for filename in files:
- if EXTENDED_M3U == 1:
- actualFilename = os.path.basename(filename)
- #Split then rejoin, remove extension
- filenameName = actualFilename.split('.')
- filenameName = '.'.join(filenameName[0:-1])
- #Add name based filename
- playlist.write('#EXTINF:'+filenameName+'\n')
- #Write relative location of file
- playlist.write(os.path.join(filename)+'\n')
- #Close file id
- playlist.close()
- def main():
- #Ask user for folder
- dir = folderBrowser()
- #If no folder given, use default
- CURRENT_DIR = dir or DEFAULT_DIR
- #Print the current directory
- print CURRENT_DIR
- #Log all the playlist created
- logFileName = os.path.basename(CURRENT_DIR)
- logFile = open(logFileName+'.log','w')
- #Change to directory
- os.chdir(CURRENT_DIR)
- #Walk along each first level folder in directory
- for dirname, dirnames, filenames in os.walk('.'):
- print dirname
- for subdirname in dirnames:
- #Basename of the current directory
- dirnameTitle = os.path.basename(subdirname)
- #Get all files under first level folder
- filesToSave = fileTree(subdirname,'')
- #Save files to playlist, using top-most folder
- saveFilesToPlaylist(filesToSave,subdirname,dirnameTitle,CURRENT_DIR)
- #Add playlist to log
- logFile.write(os.path.join(CURRENT_DIR,dirnameTitle,dirnameTitle+PLAYLIST_ID_TAG)+'\n')
- #Break the loop so we don't crawl through subdirectories and add playlists there
- break
- #Close connection to log file
- logFile.close()
- #We are in main, run
- if __name__ == '__main__':
- main()
- #Settings for playlistMaker
- #Default directory
- DEFAULT_DIR = ''
- #Current directory
- CURRENT_DIR = DEFAULT_DIR
- #Audio file formats to accept
- VALID_AUDIO_FORMATS = ['mp3','m4a','ogg','wma','flac','wav']
- #Unique ID Tag for playlists, add at the end
- PLAYLIST_ID_TAG = ''
- #Switch on or off extended .m3u file format
- EXTENDED_M3U = 0