#!/bin/bash # file: playlist-m3u.sh # script to create an M3U Playlist using full albums (all tracks/album) # Pete Nesbitt Sept 2024 # expects dir structure of "/..../artist_dir/album_dir/tracks" # /music/Steely_Dan/Katy_Lied/[tracks] ########################## # vars # *** CHANGE THESE TO MATCH ENVIRONMENT SQUEEZE_DIR="/data/media/music/squeezebox" PLAYLIST_DIR="/data/media/music/squeezebox-playlists" TITLE="Playlist Creator" SCREEN_COLOURS="`setterm --term linux --background \"blue\" --foreground \"white\"`" # *** DO NOT CHANGE THESE (unless you know what you are doing) PL_TITLE="new list" #place holder, replaced at prompt TRACK_COUNT=0 # for looping thru each track in build_list function FINAL="no" # for printing either in-progress or final summary LAST_ART="b" # place holder, used in show_summary LAST_ALB="b" # place holder, used in show_summary #array to hold Artist or Album lines. Print at summary. SUMMARY_LINES=() ########################## # functions get_title(){ # get Playlist Title, and also output file name echo -e "\n EXISTING PLAYLISTS:" EXISTING_LISTS="`ls -1 ${PLAYLIST_DIR} | sed -e 's/\.m3u$//g' -e 's/_/ /g'`" echo "${EXISTING_LISTS[@]}" | sed -E 's/(\w)(\w*)/\U\1\L\2/g' # sed upper's first letter each word echo -e "\n **Playlist Title use Letters Numbers dashes underscores or spaces ONLY**\n" read -p 'Enter the TITLE for this Playlist: ' PL_TITLE # create output file based on PL_TITLE PL_OUTFILE=(`echo "${PL_TITLE}.m3u" |sed -e 's/ /_/g' | tr '[:upper:]' '[:lower:]'`) #proper file format FQ_OUT="${PLAYLIST_DIR}/${PL_OUTFILE}" # does it already exist? ls ${FQ_OUT} &>/dev/null EXISTS=$? if [ ${EXISTS} -eq 0 ]; then # file exists! echo printf " Playlist Name already in use. Replace it? [y|N]: " read REPLACE if [ "${REPLACE}" == "y" ] || [ "${REPLACE}" == "Y" ]; then echo "Replacing Playlist ${PL_TITLE}" rm ${FQ_OUT} &> /dev/null else echo -e "\n\n\tPlaylist Exists, exiting script\n" exit fi fi clear -x touch ${FQ_OUT} } # end of get_title ############ build_list() { # assemble playlist based on user input via a loop # display a list of artists echo cd ${SQUEEZE_DIR} CD_OK=$? if [ ${CD_OK} -ne 0 ]; then echo -e "\n\n\t ERROR, could not access ${SQUEEZE_DIR}" exit fi echo ARTIST_ENTRY="a" # so it is not null on first test ALBUM_ENTRY="a" # so it is not null on first test FIRST_RUN="y" # build is only called once so okay to set here # UNTIL LOOP -START until [[ -z "${ARTIST_ENTRY}" ]] do clear -x # added -x for troubleshooting to scroll back FIRST_RUN="n" show_summary cd ${SQUEEZE_DIR} CD_OK=$? if [ ${CD_OK} -ne 0 ]; then echo -e "\n\n\t ERROR, could not access ${SQUEEZE_DIR}" exit fi echo -e "\n\n\t----------------------------------------------\n" echo -e "\n\t Artist List:\n" ls echo #after album selection, maybe do a add confirm, then write vars to file read -p 'enter or copy Artist Name from above. (or blank when done): ' ARTIST_ENTRY #if blank, start at top of Until Loop test -z "${ARTIST_ENTRY}" && continue # set current Artist, for use in summary CUR_ART="`echo ${ARTIST_ENTRY} | sed -e 's/_/ /g'`" #now show albums in that dir cd ${SQUEEZE_DIR}/${ARTIST_ENTRY} && ls echo #-e "\n\tSelect Album Name from above list\n" read -p 'enter or copy Album Name from above. (or blank for artists): ' ALBUM_ENTRY #if blank, start top of Until Loop test -z "${ALBUM_ENTRY}" && continue CUR_ALB="`echo ${ALBUM_ENTRY} | sed -e 's/_/ /g'`" # for summary # ask if ok to add this album to playlist (could show tracks??) echo -e "\n\n\tAdd ${ARTIST_ENTRY}/${ALBUM_ENTRY} to Playlist?\n\n" printf " Press Y, y or enter to Add, 'n' to skip: [Y|n]: " read OK_ADD if [ "${OK_ADD}" == "n" ] || [ "${OK_ADD}" == "N" ]; then echo -e "\n\nNew Entry Skipped\n" continue fi # write Artist (if new) to Summary if [ "${CUR_ART}" != "${LAST_ART}" ]; then #new artist SUMMARY_LINES+=("${CUR_ART}\n") # append Artist to array fi # cd into album, do listing and loop for each to add cd ${SQUEEZE_DIR}/${ARTIST_ENTRY}/${ALBUM_ENTRY} CD_OK=$? if [ ${CD_OK} -ne 0 ]; then echo -e "\n\n\t ERROR, could not access ${ALBUM_ENTRY}" exit fi # write Album into summary CUR_ALB="`basename \`pwd\` | sed -e 's/_/ /g'`" SUMMARY_LINES+=("\t\t${CUR_ALB}\n") # append Album to array TRACK_COUNT=0 for track in `ls` do let TRACK_COUNT++ # clean up $track as it is a file name not a nice entry TRK_NAME="`echo ${track%.flac} | sed -e 's/_/ /g' `" cat >> ${FQ_OUT} << eoe #EXTINF:${TRACK_COUNT}, ${ARTIST_ENTRY} - ${TRK_NAME} #EXTART: ${ARTIST_ENTRY} #EXTALB: ${ALBUM_ENTRY} ${SQUEEZE_DIR}/${ARTIST_ENTRY}/${ALBUM_ENTRY}/${track} eoe done LAST_ART=${CUR_ART} LAST_ALB=${CUR_ALB} done # } # end of build_list ############ show_summary() { clear -x # summarize playlist (artist/Album). # centre script title tput bold printf "%*s\n" $(((${#TITLE}+$COLUMNS)/2)) "${TITLE}" tput rmso # see is called while still building if [ "${FINAL}" == "yes" ];then STAT_STRING="Final Playlist" echo -e "\n\n" printf "%*s\n" $(((${#STAT_STRING}+$COLUMNS)/2)) "${STAT_STRING}" echo -e "\n\n" else STAT_STRING="Playlist So Far" echo -e "\n\n" printf "%*s\n" $(((${#STAT_STRING}+$COLUMNS)/2)) "${STAT_STRING}" echo -e "\n\n" fi # below PL Title should be case set already, but upper first letter (^) cat << oes Playlist Name: ${PL_TITLE^} Playlist Filename: ${PL_OUTFILE} oes # check if summary already artists in list, if so add the Includes: to heading test ${#SUMMARY_LINES[@]} -ne 0 && echo -e "Includes:\n" printf '%b' "${SUMMARY_LINES[@]}" } # end of show_summary ########################## ########################## # start echo ${SCREEN_COLOURS} clear # add -x to disable scroll # script title tput bold printf "%*s\n" $(((${#TITLE}+$COLUMNS)/2)) "${TITLE}" tput rmso # get playlist title get_title #do some initial stuff at top of playlist file cat > ${FQ_OUT} << eoh #EXTM3U #PLAYLIST: ${PL_TITLE} eoh # sort out what to include build_list FINAL="yes" show_summary # pause before exit (& clear screen) echo -e "\n\n ######################\n" echo " Save and EXIT press " echo END_NOW="no" until [[ -z "${END_NOW}" ]] do read -p "Press to Quit" END_NOW done tput init # resets screen colours clear -x # clear screen, keep scroll buffer #eof