#! /usr/bin/python3 # DemoMusicProgram.py, derived from K7.py 17 October 2021, and amended 16 February 2022 # INITIALIZATION import pygame # contains modules to play music etc import glob # contains a single function to handle pathnames import os # module to interact with the operating system import time # used for delay between tunes etc. import random # used to randomise tunes within the playlist import keyboard # module needed for keyboard press detection import sys # using the stdin and exit functions #from termios import tcflush, TCIFLUSH # Used to clear left over keyboard entries from the input queue VSCODE can't find this module from socket import gethostname # function to identify host and set path to musicdirectory from pathlib import Path # to enable checking which directory is the cwd (current working directory) pygame.init() # VSCode sometimes flags this "Module 'pygame' has no 'init' member", this may be ignored print() # VARIABLES USED # musicDirectory - complete path to the directory containing the playlists # playLists - a list variable of all the directories in the musicDirectory directory, contains the complete path # playListName - a list variable of all the directories in the musicDirectory directory, stripped of all the path info # chosenPlayListNumber - an integer, the number of the playListName chosen (which will also be the number of the playLists) # songList - a list variable of all the music files inn the selected playlist, contains the complete path # songListNames - a list variable of all the music files inn the selected playlist, without path # myHost - name of the machine on which the program is running # playOrderList - a list variable specifying the order to play the songs # playNext - the number in the playlist of the next tune to play # songNext - count of number of songs played # FUNCTIONS def playListPick(playLists): playListName = [0] * len(playLists) # make a list as long as the number of playlists place = 0 while place < len(playLists): playListName[place] = os.path.basename(playLists[place]) # Remove directory path to clean up playlist names, using'os' to strip off directory headers print(f"Playlist name {place} is {(playListName[place])} ") place += 1 print() chosenPlayListNumber = int(input("What is the number of the playlist you want to hear? ")) chosenPlayListName = playLists[chosenPlayListNumber] print() print(f"ChosenPlayListName is {playListName[chosenPlayListNumber]}") return chosenPlayListName pass def makeSongList(chosenPlayListName): songListTemp = [] with open(f"{chosenPlayListName}", "r") as f: songListTemp = f.readlines() # This list object includes a line feed at the end of each line which causes a failure to open the music file songList = [] for item in songListTemp: item = item.replace("\n", "") songList.append(item) # This list object is stripped of the line feed totalSongs = len(songList) print(f"There are {totalSongs} tunes in this playlist") print() return songList pass def displayFirstFewSongs(songList): totalSongs = len(songList) x = min(5,totalSongs) print(f"The first {x} songs are;") count = 0 while count < x: songName=os.path.basename(songList[count]) print(songName) count = count+1 print() pass def randomizer(songList): totalSongs = len(songList) playOrderList = list(range(0, totalSongs)) # range start number to stop number, stop number is not included in the list randFlag = False randSelect = input("Do you want to shuffle the songs? Y or N ") if randSelect == "Y" or randSelect == "y": randFlag = True print() print("Music will play in random order") print() else: print() print("Music will not be randomized") print() if randFlag == True: random.shuffle(playOrderList) return playOrderList # END OF FUNCTIONS # IDENTIFY HOST AND SET WORKING DIRECTORY LOCATION APPROPRIATELY myHost = gethostname() print(f"Found host called {(myHost)}") print() # some host if myHost == "Idra": # LaCie now persistant as M: os.chdir("M:/") elif myHost == "raspberrypi": os.chdir("/media/pi/MUSICPLAYER/") else: print("No known host found, program ending.") sys.exit() # MUSIC LOCATIONS # '/media/pi/MUSICPLAYER/Music/' On Raspberry Pi thumb drive # 'M:/Music on Idra # PLAYLIST LOCATIONS # '/media/pi/MUSICPLAYER/MusicCategories/' On Raspberry Pi thumb drive # 'M:/MusicCategories on Idra - forward slashes used as python uses backslash as an escape, and windows doesn't care # INITIALIZATION COMPLETE, BEGIN OUTER LOOP # ***************************************************** while True: # GET THE NAMES OF THE PLAYLISTS IN THE MUSIC DIRECTORY playLists = glob.glob("MusicCategories" + "/*") # returns a list 'playLists' of files in the format MusicCategories\\ZZZ5.txt print(f"Number of Playlists found = {len(playLists)}.") # check how many playlists found, this can be commented out later print() #print (playLists) This was used to show the format of the entries in "playLists" # SECTION TO DISPLAY PLAYLIST NAMES AND PICK ONE TO PLAY chosenPlayListName = playListPick(playLists) # CONVERT CHOSEN PLAYLIST FROM A TEXT FILE TO A LIST OBJECT, STRIPPED OF LINE FEEDS songList = makeSongList(chosenPlayListName) # SHOW THE NAMES OF A MAXIMUM OF 5 TUNES displayFirstFewSongs(songList) # GENERATE A PLAY ORDER LIST, RANDOMIZED IF REQUIRED playOrderList = randomizer(songList) # PLAY THE MUSIC songNext = 0 # BEGIN INNER LOOP # ****************************************************** while True: if pygame.mixer.music.get_busy() == False: # Music not playing if songNext == len(songList): # No more songs in list reasonForBreak = "AllSongsPlayed" break else: # Play the next song and increment the counter playNext = playOrderList[songNext] nextSong = os.path.basename(songList[playNext]) print(f"Next song is {nextSong}") print(f"s = skip, q = quit, p = pause, n = new playlist") print() pygame.mixer.music.load(songList[playNext]) pygame.mixer.music.play() #playNext(songList, songNext, playNext) songNext += 1 else: # Music is playing time.sleep(0.1) if keyboard.is_pressed("n"): # Change to another Playlist pygame.mixer.music.stop() reasonForBreak = "PickAnotherPlaylist" break if keyboard.is_pressed("s"): # Skip to next track print("Skipping this song") print() pygame.mixer.music.stop() if songNext == len(songList): # No more songs in list print(f"That was the final song") reasonForBreak = "NoMoreSongs" break playNext = playOrderList[songNext] nextSong = os.path.basename(songList[playNext]) time.sleep(1.0) print(f"Next song is {nextSong}") print(f"s = skip, q = quit, p = pause, n = new playlist") print() pygame.mixer.music.load(songList[playNext]) pygame.mixer.music.play() songNext += 1 if keyboard.is_pressed("p"): # Pause on input "p" and continue on input "c" pygame.mixer.music.pause() print("Music paused") print("Waiting for 5 seconds") print() time.sleep(5.0) print("Time up, waiting for keyboard input with letter c") keyboard.wait("c") print() pygame.mixer.music.unpause() print(f"s = skip, q = quit, p = pause, n = new playlist") if keyboard.is_pressed("q"): # Quit pygame.mixer.music.stop() reasonForBreak = "Quit" break # END INNER LOOP # *********************************** if reasonForBreak == "AllSongsPlayed": print("All songs played") time.sleep(0.1) print("Program terminated") break if reasonForBreak == "NoMoreSongs": print("No More Songs") time.sleep(0.1) print("Program terminated") break if reasonForBreak == "Quit": print("Stopping on keypress 'q'") time.sleep(0.1) print("Program terminated") break if reasonForBreak == "PickAnotherPlaylist": print("Setting up to choose another playlist") #tcflush(sys.stdin, TCIFLUSH) # clean out anything in the input queue - not available for windows time.sleep(2.0) pass # END OUTER LOOP # ******************************** print("Program normal termination")