pythonpycharmbase64mp3alarm

How can I make it so other people (and myself) can open the .py file and not have it crash when using mp3 files in the code?


I'm pretty new to programming, and I've made a few projects so far, but this was my first time using files in my program which weren't just txt files which were getting automatically created using code (with open...)

The program in question is an alarm clock. I have an mp3 file which plays an alarm clock sound, and I've provided the absolute path to it within the code. It works perfectly inside of PyCharm, but when trying to launch the .py file, it just crashes instantly. I've opened the file inside of the default directory, where the mp3 file is also located, and it still crashes.

import time
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame

def set_alarm():
    """Function to set the alarm"""
    while True:
        try:
            print("Set the alarm time:")
            hours = int(input("Enter the hour for the alarm to ring (0-23): "))
            if hours < 0 or hours > 23:  # if hours are below 0 and higher than 23
                print("Invalid hour entered. Please choose between hour 0 and 23.")
            else:  # if hours are between 0 and 23
                while True:  # start another loop
                    try:
                        minutes = int(input("Enter the minutes for the alarm to ring (0-59): "))
                        if minutes < 0 or minutes > 59:  # if minutes are below 0 and higher than 59
                            print("Invalid minute entered. Please choose between minute 0 and 59.")
                        else:  # if minutes are between 0 and 59
                            return hours, minutes  # returns hours and minutes value
                    except ValueError:
                        print("Please enter a number between 0 and 59.")
        except ValueError:
            print("Please enter a number between 0 and 23.")


def check_time():
    """Function to get the current time"""
    current_time = time.localtime()  # current_time variable assigned to the localtime command which retrieves local time
    return current_time.tm_hour, current_time.tm_min  # .tm_hour retrieves current hour and .tm_min retrieves current minute


def trigger_alarm(alarm_name, alarm_message, alarm_sound):
    """Function to trigger the alarm"""
    alarm_sound.play()  # plays the audio file assigned to alarm_sound variable
    print(f"{alarm_name} - {alarm_message} is ringing!")
    while True:
        stop_alarm = input("Press Enter to stop the alarm: ")
        if not stop_alarm:  # if stop_alarm input is empty (Enter is pressed)
            alarm_sound.stop()  # audio file stops playing
            break  # while True loop starts again if condition is not met


def snooze_alarm(alarm_name, alarm_message, alarm_sound):
    """Function to snooze the alarm"""
    while True:
        try:
            snooze = input("Do you want to snooze the alarm? (yes/no): ")
            if snooze.lower() == "yes":
                try:
                    snooze_minutes = int(input("Enter how long to snooze the alarm for (in minutes): "))
                    print(
                        f"Alarm will ring again in {snooze_minutes} minute/s.")  # prints in how many minutes the alarm will ring again (initial input)
                    snooze_length_seconds = snooze_minutes * 60  # snooze_length_seconds variable assigned to the "minutes" (seconds) entered and multiplies it by 60
                    time.sleep(snooze_length_seconds)  # sleeps for that many seconds
                    trigger_alarm(alarm_name, alarm_message, alarm_sound)  # triggers the alarm after the sleep

                except ValueError:
                    print("Please enter a number.")

            else:  # if user inputs anything other than "yes"
                print("You chose not to snooze the alarm.")
                print("=======================")
                break  # break the snooze alarm loop

        except ValueError:
            print("Please choose yes or no.")


def main():
    print("Loading...")
    pygame.mixer.init()  # initializes pygame mixer module

    alarm_sound_path = r"C:\Users\Marko\Documents\python projects\files\mp3\alarm sound.mp3"  # alarm sound location specified, absolute path
    if not os.path.exists(alarm_sound_path):  # if the path does not exist
        print("Alarm sound file not found.")
        return

    alarm_sound = pygame.mixer.Sound(alarm_sound_path)  # alarm sound variable assigned to mp3 file which is played using .Sound
    alarm_hours, alarm_minutes = set_alarm()  # double variable used, one assigned to hour, one assigned to minute, in order of which they are retrieved
    alarm_name = input("Enter the alarm name: ")
    alarm_message = input("Enter the alarm message: ")
    alarm_set = True  # alarm_set set to True indicating that the alarm is enabled initially if set by user
    print(f"Alarm will ring at {alarm_hours}:{alarm_minutes}")
    print("====================================")

    while True:
        current_hour, current_minute = check_time()  # checks time in a while loop (continuously)

        if alarm_set and current_hour == alarm_hours and current_minute == alarm_minutes:  # if the set time is the same as the hours and minute of current time
            trigger_alarm(alarm_name, alarm_message, alarm_sound)  # alarm is triggered
            snooze_alarm(alarm_name, alarm_message,
                         alarm_sound)  # snoozes the alarm if user requests, else skips this line
            alarm_set = False  # disables alarm after triggering

            another_alarm = input("Do you want to set another alarm? (yes/no): ")
            if another_alarm.lower() == "yes":  # if user inputs "yes"
                main()  # calls the main function again
            else:
                print("Exiting.")
                break  # breaks entire code loop

        time.sleep(1)  # checks time every 1 second


if __name__ == "__main__":
    main()
`Traceback (most recent call last): File "C:\Users\Marko\PycharmProjects\alarm_clock\alarm_clock.py", line 4, in <module> import pygame ModuleNotFoundError: No module named 'pygame'`

I don't mind sending the .py file bundled with the mp3 file, at least for a start, but if there's a way to not have to do that and it's not too complicated, I don't mind getting an explanation for that either. I prefer not to get into too complex things too soon, I prefer taking it slow while learning everything on the way, but still, I don't mind.

I've looked at some resources online for the things I could do, and I've tried doing some of them but none of it seemed to work, I was definitely just doing something wrong. I saw some things about base64 but I didn't quite understand it I think. If that's a valid option then please let me know how I can do that.


Solution

  • Python is an interpreted language. In order for anyone to run the python code, they need both the python runtime itself and any dependencies to be installed. This is why it is conventional to distribute Python code along with a requirements file.

    Pycharm likely is using a virtual environment that includes the pygame dependency, whereas this does not appear to be present in your default Python installation. A virtual environment enables different programs to have different and non-conflicting project requirements. If you load the same virtual environment being used by Pycharm in your shell, it should work. Note that Pycharm provides a shell/terminal with this pre-configured that you can use in development.

    Concerning bundling an MP3 with it, you could simply use a relative path and distribute it along with the Python script, or you could use something like pyinstaller to make an executable with a bundled resource. This has the added advantage that the person running it does not need Python installed along with the dependencies for your program. If you want to get even more creative, you could encode the mp3 file in the python script itself then write it to disk or use it in memory as needed.