#SingleInstance Force #Persistent SetWorkingDir %A_ScriptDir% ; Global variables global CurrentFile := "" global IsPlaying := false global FileList := [] global CurrentIndex := 1 global WMP := ComObjCreate("WMPlayer.OCX") global currentAlbum := 0 global albums := [] global currentPage := 1 global imageSize := 80 ; Album art size global margin := 2 global columns := 10 global rows := 10 global albumsPerPage := 100 global highlightColor := "FF00FF" ; Get user profile path and set albums path EnvGet, userProfile, USERPROFILE albumsPath := userProfile . "\Music" ; You can change the folder used here ; Create default album art defaultArtPath := A_ScriptDir "\default_album.png" defaultArt = ( No Album Art ) FileDelete, %defaultArtPath% FileAppend, %defaultArt%, %defaultArtPath% ; Scan for albums in direct subfolders of Music Loop, Files, %albumsPath%\*, D { folderPath := A_LoopFileFullPath SongCount := 0 ; Look for audio files in this folder (MP3, M4A, FLAC) Loop, Files, %folderPath%\*.* { if (A_LoopFileExt = "mp3" || A_LoopFileExt = "m4a" || A_LoopFileExt = "flac") { SongCount++ break } } if (SongCount > 0) { album := {} album.path := folderPath album.songs := [] album.name := A_LoopFileName ; Get all audio files in this folder Loop, Files, %folderPath%\*.* { if (A_LoopFileExt = "mp3" || A_LoopFileExt = "m4a" || A_LoopFileExt = "flac") { album.songs.Push(A_LoopFileFullPath) } } ; Look for cover art in this folder (jpg or png) coverPath := "" Loop, Files, %folderPath%\*.*, F { if (A_LoopFileExt = "jpg" || A_LoopFileExt = "png") { if (A_LoopFileName = "cover." A_LoopFileExt || A_LoopFileName = "folder." A_LoopFileExt) { coverPath := A_LoopFileFullPath break } } } album.cover := coverPath ? coverPath : defaultArtPath album.isPlaying := false album.currentSongIndex := 1 albums.Push(album) } } ; Check if we found any albums if (albums.Length() = 0) { MsgBox, No albums found in %albumsPath%. Please make sure you have music folders with supported audio files (MP3, M4A, FLAC). ExitApp } ; Calculate window size gridWidth := (imageSize * columns) + (margin * (columns + 1)) playlistWidth := 350 winWidth := gridWidth + playlistWidth + margin winHeight := (imageSize * rows) + (margin * (rows + 1)) + 40 ; Create GUI Gui, -Resize Gui, Color, 202020 Gui, Font, cWhite ; Function to update the album grid UpdateAlbumGrid() { global ; Hide all existing album controls Loop % albums.Length() { GuiControl, Hide, _Album%A_Index% GuiControl, Hide, _BorderTop%A_Index% GuiControl, Hide, _BorderLeft%A_Index% GuiControl, Hide, _BorderRight%A_Index% GuiControl, Hide, _BorderBottom%A_Index% } ; Calculate start and end indices for current page startIndex := (currentPage - 1) * albumsPerPage + 1 endIndex := Min(startIndex + albumsPerPage - 1, albums.Length()) ; Show and position albums for current page currentRow := 1 currentCol := 1 Loop % endIndex - startIndex + 1 { i := startIndex + A_Index - 1 xPos := margin + ((currentCol - 1) * (imageSize + margin)) yPos := margin + ((currentRow - 1) * (imageSize + margin)) ; Position and show controls GuiControl, Move, _Album%i%, % "x" (xPos + borderSize) " y" (yPos + borderSize) " w" (imageSize - 2*borderSize) " h" (imageSize - 2*borderSize) GuiControl, Move, _BorderTop%i%, x%xPos% y%yPos% w%imageSize% h%borderSize% GuiControl, Move, _BorderLeft%i%, x%xPos% y%yPos% w%borderSize% h%imageSize% GuiControl, Move, _BorderRight%i%, % "x" (xPos + imageSize - borderSize) " y" yPos " w" borderSize " h" imageSize GuiControl, Move, _BorderBottom%i%, % "x" xPos " y" (yPos + imageSize - borderSize) " w" imageSize " h" borderSize GuiControl, Show, _Album%i% GuiControl, Show, _BorderTop%i% GuiControl, Show, _BorderLeft%i% GuiControl, Show, _BorderRight%i% GuiControl, Show, _BorderBottom%i% currentCol++ if (currentCol > columns) { currentCol := 1 currentRow++ } } ; Update navigation button states GuiControl, % currentPage = 1 ? "Disable" : "Enable", PrevPage GuiControl, % endIndex >= albums.Length() ? "Disable" : "Enable", NextPage ; Update page counter totalPages := Ceil(albums.Length() / albumsPerPage) GuiControl,, PageCounter, % "Page " currentPage " of " totalPages ; Update playlist position playlistX := gridWidth + margin GuiControl, Move, Playlist, x%playlistX% GuiControl, Move, AlbumName, x%playlistX% GuiControl, Move, PlayPauseBtn, x%playlistX% ; Update navigation controls position navY := winHeight - 40 GuiControl, Move, PrevPage, x%playlistX% y%navY% GuiControl, Move, NextPage, % "x" . (playlistX + 45) . " y" . navY GuiControl, Move, PageCounter, % "x" . (playlistX + 100) . " y" . navY } ; Create album grid with highlight borders currentRow := 1 currentCol := 1 Loop % albums.Length() { i := A_Index xPos := margin + ((currentCol - 1) * (imageSize + margin)) yPos := margin + ((currentRow - 1) * (imageSize + margin)) ; Add border Progress bars borderSize := 3 Gui, Add, Progress, x%xPos% y%yPos% w%imageSize% h%borderSize% v_BorderTop%i% Background202020, 0 Gui, Add, Progress, x%xPos% y%yPos% w%borderSize% h%imageSize% v_BorderLeft%i% Background202020, 0 Gui, Add, Progress, % "x" (xPos + imageSize - borderSize) " y" yPos " w" borderSize " h" imageSize " v_BorderRight" i " Background202020", 0 Gui, Add, Progress, % "x" xPos " y" (yPos + imageSize - borderSize) " w" imageSize " h" borderSize " v_BorderBottom" i " Background202020", 0 ; Add album art Gui, Add, Picture, % "x" (xPos + borderSize) " y" (yPos + borderSize) " w" (imageSize - 2*borderSize) " h" (imageSize - 2*borderSize) " gAlbumClick v_Album" i, % albums[i].cover currentCol++ if (currentCol > columns) { currentCol := 1 currentRow++ } } ; Add playlist controls playlistX := gridWidth + margin Gui, Add, Text, x%playlistX% y%margin% cWhite, Current Album: Gui, Add, Text, x%playlistX% y%margin% w%playlistWidth% vAlbumName cWhite Gui, Add, Button, x%playlistX% y30 w80 h30 gPlayPause vPlayPauseBtn, Play Gui, Add, Button, x+5 w80 h30 gPrevTrack, Previous Gui, Add, Button, x+5 w80 h30 gNextTrack, Next ; Add ListView playlistHeight := winHeight - 110 Gui, Add, ListView, x%playlistX% y70 w%playlistWidth% h%playlistHeight% vPlaylist gPlaylistSelect -Multi +Background202020 cWhite, Tracks LV_ModifyCol(1, playlistWidth - 5) ; Add navigation controls navY := playlistHeight + 80 Gui, Add, Button, x%playlistX% y%navY% w40 h30 vPrevPage gPrevPageClick, < Gui, Add, Button, x+5 w40 h30 vNextPage gNextPageClick, > Gui, Add, Text, x+10 yp+5 vPageCounter cWhite, Page 1 of 1 WMP.settings.volume := 50 ; Start timers SetTimer, CheckPlayState, 1000 ; Show initial grid UpdateAlbumGrid() Gui, Show, w%winWidth% h%winHeight%, Album Grid Player return ; Navigation handlers PrevPageClick: if (currentPage > 1) { currentPage-- UpdateAlbumGrid() } return NextPageClick: if ((currentPage * albumsPerPage) < albums.Length()) { currentPage++ UpdateAlbumGrid() } return ; Event Handlers PlaylistSelect: if (A_GuiEvent = "DoubleClick") { if (currentAlbum > 0) { row := LV_GetNext() if (row > 0) { album := albums[currentAlbum] album.currentSongIndex := row WMP.URL := album.songs[row] WMP.controls.play() album.isPlaying := true GuiControl,, PlayPauseBtn, Pause } } } return CheckPlayState: if (currentAlbum > 0 && albums[currentAlbum].isPlaying) { playState := WMP.playState if (playState = 1) { ; 1 = Stopped album := albums[currentAlbum] album.currentSongIndex++ if (album.currentSongIndex > album.songs.Length()) { album.currentSongIndex := 1 } WMP.URL := album.songs[album.currentSongIndex] WMP.controls.play() LV_Modify(album.currentSongIndex, "Select Focus") } } return AlbumClick: RegExMatch(A_GuiControl, "\d+$", albumNum) ; Reset all borders for i, album in albums { GuiControl, +Background202020, _BorderTop%i% GuiControl, +Background202020, _BorderLeft%i% GuiControl, +Background202020, _BorderRight%i% GuiControl, +Background202020, _BorderBottom%i% } ; Clear and update playlist LV_Delete() GuiControl,, AlbumName, % albums[albumNum].name ; Add all songs from the album to the playlist Loop % albums[albumNum].songs.Length() { songPath := albums[albumNum].songs[A_Index] SplitPath, songPath, fileName LV_Add("", fileName) } if (albums[albumNum].isPlaying) { WMP.controls.pause() albums[albumNum].isPlaying := false currentAlbum := 0 GuiControl,, PlayPauseBtn, Play } else { ; Stop any currently playing album for i, album in albums album.isPlaying := false ; Play clicked album albums[albumNum].currentSongIndex := 1 WMP.URL := albums[albumNum].songs[1] WMP.controls.play() albums[albumNum].isPlaying := true currentAlbum := albumNum GuiControl,, PlayPauseBtn, Pause LV_Modify(1, "Select Focus") ; Set borders to pink GuiControl, +Background%highlightColor%, _BorderTop%albumNum% GuiControl, +Background%highlightColor%, _BorderLeft%albumNum% GuiControl, +Background%highlightColor%, _BorderRight%albumNum% GuiControl, +Background%highlightColor%, _BorderBottom%albumNum% } return PlayPause: if (currentAlbum = 0) { MsgBox, Please select an album first. return } album := albums[currentAlbum] if (!album.isPlaying) { WMP.controls.play() album.isPlaying := true GuiControl,, PlayPauseBtn, Pause } else { WMP.controls.pause() album.isPlaying := false GuiControl,, PlayPauseBtn, Play } return PrevTrack: if (currentAlbum > 0) { album := albums[currentAlbum] album.currentSongIndex := (album.currentSongIndex > 1) ? album.currentSongIndex - 1 : album.songs.Length() WMP.URL := album.songs[album.currentSongIndex] if (album.isPlaying) WMP.controls.play() LV_Modify(album.currentSongIndex, "Select Focus") } return NextTrack: if (currentAlbum > 0) { album := albums[currentAlbum] album.currentSongIndex := (album.currentSongIndex < album.songs.Length()) ? album.currentSongIndex + 1 : 1 WMP.URL := album.songs[album.currentSongIndex] if (album.isPlaying) WMP.controls.play() LV_Modify(album.currentSongIndex, "Select Focus") } return GuiClose: ExitApp