Automatic Python Playlist Creator

Summary

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.

Python
  1. #Import settings from settings.py
  2. from settings import *
  3.  
  4. def folderBrowser():
  5.         #Opens a folder
  6.         import Tkinter, tkFileDialog
  7.         root = Tkinter.Tk()
  8.         root.withdraw()
  9.         dir = tkFileDialog.askdirectory(parent=root,initialdir=DEFAULT_DIR,title='Please select a directory')
  10.         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.

Python
  1. def fileTree(dir,relDir):
  2.         #Crawls through a directory and finds audio files, returns list of files
  3.         print dir
  4.  
  5.         #Variable to save files to
  6.         filesToSave = []
  7.  
  8.         #Valid mp3 files
  9.         validFiles = set(VALID_AUDIO_FORMATS)
  10.  
  11.         #Crawl through directory
  12.         for dirname, dirnames, filenames in os.walk(dir):
  13.  
  14.                 #Crawl through each directory in a folder
  15.                 for subdirname in dirnames:
  16.                         #Next level relative path
  17.                         relDir2 = os.path.join(relDir,subdirname)
  18.  
  19.                         #Evaluate each file in directory, if audio, add to playlist
  20.                         for filename in os.listdir(os.path.join(dir,subdirname)):
  21.                                 #If there is an intersection, means valid extension, add to playlist
  22.                                 if validFiles.intersection(set(filename.split('.'))):
  23.                                         filesToSave.append(os.path.join(relDir2,filename))
  24.  
  25.                         #Recursion here
  26.                         #After finding all file in a subdirectory, crawl through its folders
  27.                         filesToSave=filesToSave+fileTree(os.path.join(dir,subdirname),relDir2)
  28.  
  29.                 #Finally, get files in root directory, see above for explanation of details
  30.                 for filename in os.listdir(os.path.join(dir,'')):
  31.                         if validFiles.intersection(set(filename.split('.'))):
  32.                                 filesToSave.append(os.path.join(relDir,filename))
  33.  
  34.                 #Break the loop so we don't crawl through subdirectories and add playlists there
  35.                 break
  36.  
  37.         #Return files found
  38.         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.

Python
  1. def saveFilesToPlaylist(files,dir,dirname,CURRENT_DIR):
  2.         #Saves a list of files to a playlist
  3.  
  4.         #Open connection, uses root as name for playlist
  5.         playlist = open(os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u'),'w')
  6.         #Output playlist location
  7.         print os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u')
  8.  
  9.         #Extended M3U format used here
  10.         playlist.write('#EXTM3U
  11. ')
  12.  
  13.         #Remove duplicates
  14.         files = sorted(set(files))
  15.  
  16.         #Loop through each file
  17.         for filename in files:
  18.                 actualFilename = os.path.basename(filename)
  19.                 #Split then rejoin, remove extension
  20.                 filenameName = actualFilename.split('.')
  21.                 filenameName = '.'.join(filenameName[0:-1])
  22.                 #Add name based filename
  23.                 playlist.write('#EXTINF:'+filenameName+'
  24. ')
  25.                 #Write relative location of file
  26.                 playlist.write(os.path.join(filename)+'
  27. ')
  28.  
  29.         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.

Python
  1. def main():
  2.         #Ask user for folder
  3.         dir = folderBrowser()
  4.         #If no folder given, use default
  5.         CURRENT_DIR = dir or DEFAULT_DIR
  6.         #Print the current directory
  7.         print CURRENT_DIR
  8.  
  9.         #Log all the playlist created
  10.         logFileName = os.path.basename(CURRENT_DIR)
  11.         logFile = open(logFileName+'.log','w')
  12.  
  13.         #Change to directory
  14.         os.chdir(CURRENT_DIR)
  15.  
  16.         #Walk along each first level folder in directory
  17.         for dirname, dirnames, filenames in os.walk('.'):
  18.                 print dirname
  19.                 for subdirname in dirnames:
  20.                         #Basename of the current directory
  21.                         dirnameTitle = os.path.basename(subdirname)
  22.  
  23.                         #Get all files under first level folder
  24.                         filesToSave = fileTree(subdirname,'')
  25.  
  26.                         #Save files to playlist, using top-most folder
  27.                         saveFilesToPlaylist(filesToSave,subdirname,dirnameTitle,CURRENT_DIR)
  28.  
  29.                         #Add playlist to log
  30.                         logFile.write(os.path.join(CURRENT_DIR,dirnameTitle,dirnameTitle+'_ssx42.m3u')+'
  31. ')
  32.  
  33.                 #Break the loop so we don't crawl through subdirectories and add playlists there
  34.                 break
  35.  
  36.         #Close connection to log file
  37.         logFile.close()
  38.  
  39. #We are in main, run
  40. if __name__ == '__main__':
  41.         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.

download playlistMaker.py

Python
  1. #!/Python27/env python
  2. #Biafra Ahanonu
  3. #2012.12.02
  4.  
  5. #Makes .m3u extended playlist at first level folders in a directory
  6.  
  7. #Modules used
  8. import os,re,time
  9. #Import settings from settings.py
  10. from settings import *
  11. #Help filter out duplicates
  12. from sets import Set
  13.  
  14. def folderBrowser():
  15.         #Opens a folder
  16.         import Tkinter, tkFileDialog
  17.         root = Tkinter.Tk()
  18.         root.withdraw()
  19.         dir = tkFileDialog.askdirectory(parent=root,initialdir=DEFAULT_DIR,title='Please select a directory')
  20.         return dir
  21.  
  22. def fileTree(dir,relDir):
  23.         #Crawls through a directory and finds audio files, returns list of files
  24.         print dir
  25.  
  26.         #Variable to save files to
  27.         filesToSave = []
  28.  
  29.         #Valid mp3 files
  30.         validFiles = set(VALID_AUDIO_FORMATS)
  31.  
  32.         #Crawl through directory
  33.         for dirname, dirnames, filenames in os.walk(dir):
  34.  
  35.                 #Crawl through each directory in a folder
  36.                 for subdirname in dirnames:
  37.                         #Next level relative path
  38.                         relDir2 = os.path.join(relDir,subdirname)
  39.  
  40.                         #Evaluate each file in directory, if audio, add to playlist
  41.                         for filename in os.listdir(os.path.join(dir,subdirname)):
  42.                                 #If there is an intersection, means valid extension, add to playlist
  43.                                 if validFiles.intersection(set(filename.split('.'))):
  44.                                         filesToSave.append(os.path.join(relDir2,filename))
  45.  
  46.                         #Recursion here
  47.                         #After finding all file in a subdirectory, crawl through its folders           
  48.                         filesToSave=filesToSave+fileTree(os.path.join(dir,subdirname),relDir2)
  49.  
  50.                 #Finally, get files in root directory, see above for explanation of details
  51.                 for filename in os.listdir(os.path.join(dir,'')):
  52.                         if validFiles.intersection(set(filename.split('.'))):
  53.                                 filesToSave.append(os.path.join(relDir,filename))
  54.  
  55.                 #Break the loop so we don't crawl through subdirectories and add playlists there
  56.                 break
  57.        
  58.         #Return files found
  59.         return filesToSave
  60.  
  61. def saveFilesToPlaylist(files,dir,dirname,CURRENT_DIR):
  62.         #Saves a list of files to a playlist
  63.  
  64.         #Open connection, uses root as name for playlist
  65.         playlist = open(os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u'),'w')
  66.         #Output playlist location
  67.         print os.path.join(CURRENT_DIR,dirname,dirname+PLAYLIST_ID_TAG+'.m3u')
  68.        
  69.         #Extended M3U format used here
  70.         if EXTENDED_M3U == 1:
  71.                 playlist.write('#EXTM3U\n')
  72.  
  73.         #Remove duplicates
  74.         files = sorted(set(files))
  75.  
  76.         #Loop through each file
  77.         for filename in files:
  78.                 if EXTENDED_M3U == 1:
  79.                         actualFilename = os.path.basename(filename)
  80.                         #Split then rejoin, remove extension
  81.                         filenameName = actualFilename.split('.')
  82.                         filenameName = '.'.join(filenameName[0:-1])
  83.                         #Add name based filename
  84.                         playlist.write('#EXTINF:'+filenameName+'\n')
  85.                 #Write relative location of file
  86.                 playlist.write(os.path.join(filename)+'\n')
  87.  
  88.         #Close file id
  89.         playlist.close()
  90.  
  91. def main():
  92.         #Ask user for folder
  93.         dir = folderBrowser()
  94.         #If no folder given, use default
  95.         CURRENT_DIR = dir or DEFAULT_DIR
  96.         #Print the current directory
  97.         print CURRENT_DIR
  98.  
  99.         #Log all the playlist created
  100.         logFileName = os.path.basename(CURRENT_DIR)
  101.         logFile = open(logFileName+'.log','w')
  102.  
  103.         #Change to directory
  104.         os.chdir(CURRENT_DIR)
  105.  
  106.         #Walk along each first level folder in directory
  107.         for dirname, dirnames, filenames in os.walk('.'):
  108.                 print dirname
  109.                 for subdirname in dirnames:
  110.                         #Basename of the current directory
  111.                         dirnameTitle = os.path.basename(subdirname)
  112.  
  113.                         #Get all files under first level folder
  114.                         filesToSave = fileTree(subdirname,'')
  115.  
  116.                         #Save files to playlist, using top-most folder
  117.                         saveFilesToPlaylist(filesToSave,subdirname,dirnameTitle,CURRENT_DIR)
  118.  
  119.                         #Add playlist to log
  120.                         logFile.write(os.path.join(CURRENT_DIR,dirnameTitle,dirnameTitle+PLAYLIST_ID_TAG)+'\n')
  121.  
  122.                 #Break the loop so we don't crawl through subdirectories and add playlists there
  123.                 break
  124.  
  125.         #Close connection to log file
  126.         logFile.close()
  127.  
  128. #We are in main, run
  129. if __name__ == '__main__':
  130.         main()
download settings.py

Python
  1. #Settings for playlistMaker
  2.  
  3. #Default directory
  4. DEFAULT_DIR = ''
  5.  
  6. #Current directory
  7. CURRENT_DIR = DEFAULT_DIR
  8.  
  9. #Audio file formats to accept
  10. VALID_AUDIO_FORMATS = ['mp3','m4a','ogg','wma','flac','wav']
  11.  
  12. #Unique ID Tag for playlists, add at the end
  13. PLAYLIST_ID_TAG = ''
  14.  
  15. #Switch on or off extended .m3u file format
  16. EXTENDED_M3U = 0

-biafra
bahanonu [at] alum.mit.edu

other entires to explore:

dynamic goodreads sass formatting
18 january 2015 | programming

Putting up some code that might be useful to others when formatting Goodreads widgets using dynamic class creation in SASS.[...]

comment system!
05 august 2012 | website

Wanted to add the ability for people to comment on this website, but delayed adding the feature until I could write the code myself. There [...]are many pre-built PHP solutions on the market (like commentator), but the original purpose of this site was to allow me to learn how to build a website from scratch. So I've implemented the comment system using about a hundred lines of code to access the MySQL database, verify inputs and display all the comments for a particular article.

bio42: notes
12 may 2013 | teaching

While teaching bio42 (cell biology and animal physiology) I created weekly notes to help students in my section study and focus on the impo[...]rtant materials presented in the class. I built off of the latex boilerplate that I have been improving over time to create weekly notes. This highlights why I love LaTeX so much, especially for larger projects that are heavily linked—it allows easy annotation, indexing, creation of new document styles, and other related processes rapidly and consistently. Plus, separating content and style is always a plus and images stay uncoupled from a propriety source (e.g. Word files).

I really love the resulting notes and student feedback was quite positive. I thought sharing them might be useful for others in the future. The source latex files and raw images can be sent upon request (I'm considering making a Github repository in the future). I'll briefly talk about the document below and certain decisions that were made to get it to its current state.

the origin and evolution of life
01 january 2020 | origin and evolution of life

I am starting a new blog focused on the Origin and Evolution of Life. Here is the first post.[...]
©2006-2024 | Site created & coded by Biafra Ahanonu | Updated 21 October 2024
biafra ahanonu