Here is the completed code for my first Python project, for which I received some help and feedback here: Collate votes on MP3 files to list them by popularity
Seeing as there have been considerable changes since that post, I figured it would be best to start a new post.
My goals when starting this project were:
- Scan sub folders for MP3 files (named in a predetermined format).
- Sum the total amount of votes per song and output the results to a CSV file.
- Create a list of who voted for which songs.
- Copy those MP3 files to a single sub directory and rename them based on the results. For example, if a song received a total of 18 votes and came in at 11th position, it would have '11.' prepended at the start. This allows the MP3 files to be imported straight into a playlist in the correct order for a countdown.
I have completed these goals and my tests so far have worked perfectly. I am not totally satisfied with the final function, process_mp3. It seems a bit long-winded and could be improved, so I am looking for some feedback on that in particular.
import os, csv, operator, itertools, shutil
from collections import Counter, defaultdict
print("*****Welcome to the MP3 Vote Collator*****" + '\n')
# Global variables and lists
votes = defaultdict(list)
tally = Counter()
folder_list = os.getcwd()
base_dir = os.getcwd()
# The tally_votes function is responsible for searching for all .mp3 files, retrieving the vote count for each song and
# storing the results in the 'tally' counter.
def tally_votes():
print("STAGE 1. Processing Votes..." + '\n')
ignore_dir = ["Playlist"]
for subdir, dirs, files in os.walk(folder_list):
dirs[:] = [d for d in dirs if d not in ignore_dir]
for file in files:
if file.endswith(".mp3"):
file_name = file.split('.')[1] # Removes the .mp3 extension and numbers at the start of the file to clean up song name
vote_count = file.split('.')[0] # Retrieves vote count for each song
_, tail = os.path.split(subdir) # Retrieves the name of the voter from the folder name
song = file_name.lower() # Lower-casing prevents song name match issues
try:
tally[song] += int(vote_count) # Adds song to tally. If song already exists, adds votes to that song
votes[song].append((tail, int(vote_count))) # Adds voter name and vote count to votes dict
except:
break
# The write_tally function will write the results to a CSV file called 'results_out.csv' in the base directory
def write_tally():
tally_list = sorted(list(Counter((tally)).items()),key=operator.itemgetter(1), reverse=True) # Converts tally counter to a list and sorts songs based on highest votes
votes_list = sorted(list((votes).items()),key=operator.itemgetter(0)) # Converts votes dict to a list and sorts in alphabetical order
with open('results_out.csv', 'w') as resultsout, open('vote_list.csv', 'w') as votelistout:
resultswriter = csv.writer(resultsout)
votelistwriter = csv.writer(votelistout)
resultswriter.writerows(tally_list)
votelistwriter.writerows(votes_list)
# The process_result function will read the results from the CSV and print them to output
def process_result():
print("STAGE 2. Listing Top 3 Songs:" + '\n')
with open('results_out.csv', 'r') as results:
reader = csv.reader(results)
for iteration, row in enumerate(csv.reader(results)):
print("The number {} song for this year's count is: {}, with a total of {} votes!".format(
["one", "two", "three"][iteration], row[0], row[1]))
if iteration >= 2:
break
print('\n' + "There are a total number of " + str(len(tally)) + " songs in this year's count.")
# The process_mp3 function will read the CSV file line by line, rename MP3 files accordingly and then copy them
# to the 'Playlist' directory
def process_mp3():
print('\n' + "STAGE 3. Grouping and Renaming Mp3 Files")
ignore_dir = ["Playlist"] # Ignore this dir to avoid issues when copying MP3 files(I.e. src/dst are the same). This dir must be empty prior to running script
for subdir, dirs, files in os.walk(folder_list):
dirs[:] = [d for d in dirs if d not in ignore_dir]
for file in files:
if file.endswith(".mp3"):
file_name = file.split('.')[1]
with open('results_out.csv', 'r') as results:
reader = csv.reader(results)
for iteration, row in enumerate(csv.reader(results)):
song_name = row[0].title() # Capitalize song names for cleaner look
mp3_file = os.path.join(os.path.join(subdir), file)
new_name = ("{}".format("%02d" % range(1, 1000, 1)[iteration]) + "." + song_name + ".mp3") # Rename MP3 files according to correct order
playlist_dir = ("Playlist")
if not os.path.exists(playlist_dir): # For storing renamed MP3 files
os.mkdir(playlist_dir)
if (file_name.lower() == song_name.lower() and
song_name not in playlist_dir):
shutil.copy(mp3_file, playlist_dir) # Currently copies for testing purposes. Probably best to change this to shutil.move
os.chdir(playlist_dir)
os.rename(file, new_name)
os.chdir(base_dir) # Return to base directory for restart of function
if __name__ == '__main__':
tally_votes()
write_tally()
process_result()
process_mp3()