Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Winamp - Rebuild simple playlists


  • Please log in to reply
5 replies to this topic
jballi
  • Members
  • 1029 posts
  • Last active:
  • Joined: 01 Oct 2005
This is my 1st post so be nice...

I looked around the web for a utility to rebuild a Winamp playlist (outside of Winamp). I found one (don't remember the name) but I couldn't get it to work and/or didn't like how it worked so I decided to write one of my own.

A little background... All of my "production" (ready to play) music is stored in one folder with sub-folders breaking the music up into decade eras. In my case, this means a "1980s", "1990s", "2000s", etc. folders. The script was written to create 1 playlist for each sub-folder and 1 "Master" playlist.

The script creates a "simple" playlist which is just a list of files - no MP3 tags, no play-times, etc. For me, it runs much faster than Winamp.

A couple of warnings. This script will not run as-is. You must modify it to fit your needs. Also the syntax may not be AutoHotkey forum friendly. Here's the code:

; Notes
; -----
; * Absolute path is used instead of relative path so that the Master
;   playlist can be moved/copied to any location and still work.
; 
; * "#EXTM3U" is defined as the first line of every playlist so that
;   Winamp will not verify every file in the playlist when opening the
;   playlist for the first time.
;
; --- Minor edit to clarify the music path.
;
;
#SingleInstance

;-- Confirm
MsgBox 1,,About to rebuild all music playlists...
IfMsgBox Cancel
    return

;-- Initialize
$MusicPath:="C:\Music"
MasterPlaylist=Music.m3u

;-- Initialize master playlist
FileDelete %$MusicPath%\%MasterPlayList%
FileAppend #EXTM3U`n,%$MusicPath%\%MasterPlayList%

;-- Build directory list
$DirectoryList=
Loop %$MusicPath%\*.*,2,0
    $DirectoryList=%$DirectoryList%%A_LoopFileFullPath%`r`n

;-- Sort it
Sort $DirectoryList

;-- Build playlists
Loop parse,$DirectoryList,`n,`r
    {
    if A_LoopField=  ;-- Ignore blank line
        continue

    ;-- Skip Hidden or System folders
    FileGetAttrib $Attributes,%A_LoopField%
    if $Attributes contains h,s
        continue

    SplitPath A_LoopField,$MusicFolder
    gosub BuildPlayList
    }

;-- Houskeeping
SplashImage off

return



;[==================]
;[                  ]
;[  Build Playlist  ]
;[                  ]
;[==================]
BuildPlayList:

;-- Splash
SplashImage,, W250 B2 ,,Building playlist: %$MusicFolder%
Sleep 10  ;-- Allow image to render

;-- Build file list
$FileList=
Loop %$MusicPath%\%$MusicFolder%\*.*,,1
    $FileList=%$FileList%%A_LoopFileFullPath%`n

;-- Sort it
Sort $FileList

;-- Initialize playlist
FileDelete %$MusicPath%\%$MusicFolder%.m3u
FileAppend #EXTM3U`n, %$MusicPath%\%$MusicFolder%.m3u

;-- Populate playlist
Loop parse,$FileList,`n
    {
    StringRight $RString,A_LoopField,5
    if $RString contains .ape,.flac,.mp3,.ogg,.wav
        FileAppend %A_LoopField%`n, %$MusicPath%\%$MusicFolder%.m3u
    }

;-- Add playlist to Master
FileAppend %$MusicPath%\%$MusicFolder%.m3u`n,%$MusicPath%\%MasterPlayList%
return

I hope someone finds this useful.

Later...

evl
  • Members
  • 1237 posts
  • Last active: Oct 20 2010 11:41 AM
  • Joined: 24 Aug 2005
I've only had a quick glance over the script so far but it looks useful to me at least. I'd been thinking about starting on something like this as I'd not come across any simple play-list builder like this.

Some other things I was thinking of doing which I don't think are in yours:

- In addition to generating one big master playlist and single directory playlists, also generate playlists for all subdirectories below the current working directory. e.g. In your case that would generate playlists for "1980s", "1990s", "2000s", and all other directories too.

- Personally I would prefer to keep all the playlist files in a separate directory to my music (less "mess"). I was thinking it should work quite well to mirror the folder layout to another path and store the playlists there and then use/ make a folder explorer script to act as a quick launcher for those.

jballi
  • Members
  • 1029 posts
  • Last active:
  • Joined: 01 Oct 2005
To generate a playlist for EVERY sub-folder under the root, simply change:
Loop %$MusicPath%\*.*,2,0
to:
Loop %$MusicPath%\*.*,2,1
This will work as long as all of your folders have unique names.

This approach makes the "Master" playlist worthless because the contents of a folder at a lower level will be duplicated by the parent folder. If you don't plan to use a "Master" playlist, this may not be an issue. For me, this approach is worthless because I have hundreds of sub-folders under my "Music" folder.

It's easy to put the playlists wherever you want. Just change the path of where the playlists are written to. For example, change:
FileAppend %A_LoopField%`n, %$MusicPath%\%$MusicFolder%.m3u
to:
FileAppend %A_LoopField%`n, %$PlayListPath%\%$MusicFolder%.m3u
Of course, you'll want to change all of the references to where the playlist are written.

I hope this helps...

evl
  • Members
  • 1237 posts
  • Last active: Oct 20 2010 11:41 AM
  • Joined: 24 Aug 2005
I've played around with the code quite a bit (although essentially the same apart from the destination folder for each playlist being in the folder above its contents and playlists including the contents of subdirectories).

It works pretty well to make a shortcut to the playlist folder from the start menu or quicklaunch bar so that it pops up displaying the folders contents like the start menu programs entry does.

I've also added:
- a gui for choosing options
- it saves its settings to the user's registry for next time
-cropping to a user-determined length for folder and file names (for neater display)
- instead of deleting individual m3u files before building the playlist, it now deletes all m3u files in the playlist folder, then deletes any empty sub-directories - this seems like a better way of avoiding a buildup of obsolete entries when folders change.

Updated v1.1:
- major improvement to the structure of playlists (didn't notice due to my weird folder layout at the time)
- more options in the gui
-setbatchlines, 0 for better display
-better default values

; WinAmp Playlist Maker v1.1
;
;   Absolute path is used instead of relative path so that the Master
;   playlist can be moved/copied to any location and still work.

;   "#EXTM3U" is defined as the first line of every playlist so that
;   Winamp will not verify every file in the playlist when opening the
;   playlist for the first time.


#SingleInstance
SetBatchLines, 0 ; less flicker with splashimage

OnMessage(0x201, "WM_LBUTTONDOWN") ; for stopping auto gui sumbit on left click

; Paths for saved settings
Registry_Root = HKEY_CURRENT_USER
Registry_Path = Software\SCRIPTS\Winamp Playlist Builder

Run_Before = 1 ; initialise

Reg_Read("Music_Folder")
  If Music_Folder =
    {
    Run_Before = 0
    Music_Folder =%HOMEDRIVE%%HOMEPATH%\My Documents\My Music
    }
Reg_Read("Playlist_Folder")
  If Playlist_Folder =
    {
    Run_Before = 0
    Playlist_Folder =%HOMEDRIVE%%HOMEPATH%\Local Settings\Temp
    }
Reg_Read("Show_Progress_Checked")
  If Show_Progress_Checked =
    {
    Show_Progress_Checked = 1
    }
Reg_Read("Crop_To_Chars")
  If Crop_To_Chars =
    {
    Crop_To_Chars = 40
    }
Reg_Read("Master_Playlist_Checked")
  If Master_Playlist_Checked =
    {
    Master_Playlist_Checked = 1
    }
Reg_Read("Master_Playlist_Name")
  If Master_Playlist_Name =
    {
    Master_Playlist_Name =----------{ ALL FILES }----------
    }


Gosub, Draw_Gui_Playlist_Options
Return



Run_Script: ;------------------------------------------------------------------------------------------------------

SetTimer, Gui_CountDown, Off
SetTimer, Auto_Gui_Submit, Off
Gui, 1: Submit
Gui, 1: Destroy


If Music_Folder_Checked = 0
  {
  FileSelectFolder, Music_Folder_Check,*%Music_Folder%, 0,`n`nChoose folder that contains all MUSIC:
    If Music_Folder_Check in,%Playlist_Folder%; abort if blank or music folder = playlist folder
      {
      ExitApp
      }
    Else
      {
      Music_Folder =%Music_Folder_Check% ; set to actual variable
      }
  }
If Playlist_Folder_Checked = 0
  {
  FileSelectFolder, Playlist_Folder_Check,*%Playlist_Folder%, 1,`n`nNow choose where to save PLAYLISTS:
    If Playlist_Folder_Check in,%Music_Folder% ; abort if blank or music folder = playlist folder
      {
      ExitApp
      }
    Else
    {
    Playlist_Folder =%Playlist_Folder_Check%
    }
  }


SetTimer, TrayTip_Building_Playlist, 10000
Gosub, TrayTip_Building_Playlist


Reg_Write("Music_Folder",Music_Folder)
Reg_Write("Playlist_Folder",Playlist_Folder)
Reg_Write("Show_Progress_Checked",Show_Progress_Checked)
Reg_Write("Crop_To_Chars",Crop_To_Chars)
Reg_Write("Master_Playlist_Checked",Master_Playlist_Checked)
Reg_Write("Master_Playlist_Name",Master_Playlist_Name)


DelEmpty_Folder =%Playlist_Folder% ; folder to process
DelM3U(DelEmpty_Folder) ; delete all m3u files in playlist folder
DelEmpty(DelEmpty_Folder) ; delete all empty folders in playlist folder


; Build directory list
Directory_List =
Loop, %Music_Folder%\*.*,2,1
  {
  Directory_List =%Directory_List%%A_LoopFileFullPath%`r`n
  }

; Sort it
Sort, Directory_List

; Build playlists
Loop, parse, Directory_List,`n,`r
    {
    FileGetAttrib Folder_Attributes,%A_LoopField% ; Skip Hidden or System folders
    If Folder_Attributes contains h,s
        {
        Continue
        }

    SplitPath, A_LoopField,Music_Folder_Current_Name,Music_Folder_Current_Root
    Music_Path_Being_Processed =%Music_Folder_Current_Root%\%Music_Folder_Current_Name%

    ; modify paths to be relative, crop, etc
    StringReplace, Music_Folder_Current_Root_Relative, Music_Folder_Current_Root, %Music_Folder% 

    If Music_Folder_Current_Name = ; for removing extra slash when in main music path
      {
      If Master_Playlist_Checked = 0
        {
        Continue
        }
      Music_Folder_Current_Name =%Master_Playlist_Name% ; master playlist name
      Music_Path_Being_Processed =%Music_Folder%
      }


    ; Trim playlist paths and m3u file names to %Crop_To_Chars% characters (for display)
    StringLen, Music_Folder_Current_Root_Relative_Length, Music_Folder_Current_Root_Relative
      If Music_Folder_Current_Root_Relative_Length > %Crop_To_Chars%
        {
        Trim_Count := Music_Folder_Current_Root_Relative_Length - Crop_To_Chars
        StringTrimRight, Music_Folder_Current_Root_Relative, Music_Folder_Current_Root_Relative, %Trim_Count%
        }
    StringLen, Music_Folder_Current_Name_Length, Music_Folder_Current_Name
      If Music_Folder_Current_Name_Length > %Crop_To_Chars%
        {
        Trim_Count := Music_Folder_Current_Name_Length - Crop_To_Chars
        StringTrimRight, Music_Folder_Current_Name, Music_Folder_Current_Name, %Trim_Count%
        }

    Gosub, Build_Playlist
    }

; Housekeeping
  If Show_Progress_Checked = 1
    {
    SplashImage,, y600 W700 B2 ,,`nCOMPLETED!`n
    Sleep, 500
    SplashImage, off
    }

  ExitApp
Return



Build_Playlist: ;-----------------------------------------------------------------------------------------------------
  If Show_Progress_Checked = 1
    {
    SplashImage,, c10 y600 W700 B2 ,,Building playlist:`n   %Music_Folder_Current_Name%`n   %Music_Folder_Current_Root_Relative%
    }

  ; Build file list
  File_List =
  Loop %Music_Path_Being_Processed%\*.*,,1
      {
      File_List =%File_List%%A_LoopFileFullPath%`n
      }
  
  ; Sort it
  Sort, File_List
  
  ; Initialize playlist
  FileCreateDir, %Playlist_Folder%%Music_Folder_Current_Root_Relative%
  FileAppend,#EXTM3U`n, %Playlist_Folder%%Music_Folder_Current_Root_Relative%\%Music_Folder_Current_Name%.m3u

  ; Populate playlist
  Loop, parse,File_List,`n
      {
      StringRight, File_Type_Current,A_LoopField,5
      If File_Type_Current contains .ape,.flac,.mp3,.ogg,.wav,.wma
        {
        FileAppend, %A_LoopField%`n, %Playlist_Folder%%Music_Folder_Current_Root_Relative%\%Music_Folder_Current_Name%.m3u
        }
      }
Return



Draw_Gui_Playlist_Options: ;------------------------------------------------------------------------------------------
Countdown_Time_Left = 10

Gui, 1: +Alwaysontop +Border
Gui, 1: Margin, 3, 3
Gui, 1: Color, A6BAFF
Gui, 1: Font, c000000 s10 wbold, Verdana
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Checkbox, xm+5 Checked%Run_Before% vMusic_Folder_Checked, Use Music folder:`n%Music_Folder%
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Checkbox, xm+5 Checked%Run_Before% vPlaylist_Folder_Checked, Use Playlist folder:`n%Playlist_Folder%
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Text,,Crop names to how many characters (e.g. 40):
Gui, 1: Add, Edit, x+1 yp-3 w40 vCrop_To_Chars, %Crop_To_Chars%
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Checkbox, xm+5 Checked%Master_Playlist_Checked% vMaster_Playlist_Checked, Generate MASTER PLAYLIST:
Gui, 1: Add, Edit, x+1 yp-3 w260 vMaster_Playlist_Name, %Master_Playlist_Name%
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Checkbox, xm+5 Checked%Show_Progress_Checked% vShow_Progress_Checked, Show progress when making playlists?
Gui, 1: Add, Text,, ;blank line
Gui, 1: Add, Button, x200 gRun_Script Default, Go! (%Countdown_Time_Left% secs)
Gui, 1: Font, cFF0000 s10 wbold italic, Verdana
Gui, 1: Add, Text, xm+110,(Click anywhere to cancel countdown!)
Gui, 1: Show, xCenter yCenter w515,Playlist options:

SetTimer, Auto_Gui_Submit, 10000
SetTimer, Gui_CountDown, 1000
Return


Gui_CountDown:
  Countdown_Time_Left := Countdown_Time_Left - 1
  ControlSetText, Button5, Go! (%Countdown_Time_Left% secs),Playlist options:
Return



Auto_Gui_Submit:
  Gosub, Run_Script
Return



Auto_Gui_Submit_Stop:
  SetTimer, Gui_CountDown, Off
  SetTimer, Auto_Gui_Submit, Off
  ControlSetText, Button5, Go!,Playlist options:
Return



TrayTip_Building_Playlist: ;----------------------------------------------------------------------------------------
  Traytip,Playlist Maker,( Building playlist ),11,16 ; 11 secs (so never off),no sound
Return



;---------------------------------------------------------------------------------------------------------------------
Reg_Read(Setting)
  {
  global
  RegRead, %Setting%, %Registry_Root%, %Registry_Path%, %Setting%
  }
Return



Reg_Write(Setting, Value)
  {
  global
  RegWrite, REG_SZ, %Registry_Root%, %Registry_Path%, %Setting%, %Value%
  }
Return



DelM3U(dir) ; deletes all m3u playlist files within the target (including sub-directories)
{
  Loop %dir%\*.*, 2
    {
    DelM3U(A_LoopFileFullPath)
    }
  FileDelete, %dir%\*.m3u
}



DelEmpty(dir) ; deletes empty sub-directories within the target
{
  Global DelEmpty_Folder
  Loop %dir%\*.*, 2
    {
    DelEmpty(A_LoopFileFullPath)
    }
  If dir !=%DelEmpty_Folder%
    {
    FileRemoveDir, %dir%
    }
}



WM_LBUTTONDOWN(wParam, lParam)
{
  If A_Gui = 1 ;clicked in Gui, 1:
    {
    Gosub, Auto_Gui_Submit_Stop
    }
}



GuiEscape:
GuiClose:
  ExitApp
Return


jballi
  • Members
  • 1029 posts
  • Last active:
  • Joined: 01 Oct 2005
Wow! You've made a lot of great changes. Like I said earlier, this design doesn't work to well with my file structure -- I've created WAY too many folders and some of the folders are duplicates. But and however, there are definitely many advantages to creating playlists in this manner. I'm sure there are many folks who would find this script useful. Thank you for sharing!

I have a couple of very minor recommendations:

1) When running the script for the very 1st time, the registry entries for "Music Folder" and "PlayList folder" are blank and they are both selected. I didn't know what to do so I clicked the Go! button. Boy, was that a mistake! I would definitely change the script so that the user is prompted for a folder if the either of these registry entries are empty. If you want to be very helpful, you can default the "Music" folder entry with the value found in the "My Music" entry of the following registry key:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders

Since the script creates and deletes directories in order to build/rebuild the playlist files, I would insist (or at least encourage) the user to choose playlist folder that is different than the "Music" folder.

2) You can double or even triple the size of the splash image to accommodate the larger path names.

3) IMHO, the kickoff timer is too short (5 seconds). I know you can stop the countdown by clicking anywhere but 5 seconds is still too short. I had to kill the script on at least 2 occasions because I ran out of time before I was able to make any changes. Let me put it another way - 5 seconds is plenty of time to click on the dialog. However, it is sometimes not enough time to determine that you need to click. 10 - 15 seconds might be better. Just my opinion.

Them be my thoughts...

evl
  • Members
  • 1237 posts
  • Last active: Oct 20 2010 11:41 AM
  • Joined: 24 Aug 2005
Thanks for the reply, it was very helpful!

I've updated my post above with the latest version of the script which has major improvements including:

Updated v1.1:
- major improvement to the structure of playlists (didn't notice due to my weird folder layout at the time) - it was moving the playlists up 2 levels instead of just 1, consequently making a real mess of the playlist contents and layout.
- more options in the gui
-setbatchlines, 0 for better display
-better default values

I implemeted all your recommendations:
-The user's music folder is now the default for the first run (and not ticked) and their temp folder for the playlist (just somewhere "safe" to put it) - incidentally, you can reference the user's directories using the windows built in variables, %HOMEDRIVE% and %HOMEPATH%
- if the playlist folder and music folder are set to the same, the script exits
-improved the splashimage to accomodate much longer paths (displays the cropped names too)
- bumped the countdown up to 10 secs