Files
WebJammies/webjammies.py
2025-07-11 13:31:36 +02:00

338 lines
12 KiB
Python

# This is WebJammies, it retrieves music from servers!
# It's like radio, but better!
# WebJammies is very easy to set up!
# It uses simple HTTP(S) and INI files as a sort DB
import requests
import time
import os
import shutil
# FIXME: Just for testing, take out later:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# FIXME: Just for testing!
try:
from discordrp import Presence
richpresense = True
except ImportError:
print("You don't have `discordrp` installed! Disabling Discord Rich Presence.")
richpresense = False
try:
import pygame
except ImportError:
print("Fatal! You need to install the `pygame` module to use WebJammies!")
exit(1)
class MusicPlayer:
def __init__(self):
self.discordrp: DiscordRP = None
self.nextfile = "song0.mp3"
self.nextmeta = None
self.looping = False
self.server = None
self.mixer = pygame.mixer
self.mixer.init()
def play_audio_file(self, _file=None):
if not _file and not self.nextfile:
return
if not _file:
self.discordrp.set_drp(self.nextmeta, self.server)
self.mixer.music.load(self.nextfile)
self.mixer.music.play()
print(
f"Currently playing: {self.nextmeta}\n"
"Press <Ctrl>+<C> to open the menu!"
)
paused = False
while True:
try:
if not self.mixer.music.get_busy() and not paused:
self.mixer.music.unload()
return
while self.mixer.music.get_busy():
time.sleep(0.1)
except KeyboardInterrupt:
print(
"\n"
f"Currently listening to: {self.nextmeta}\n"
"Here's some things you can do while listening:\n"
"[1] Play / Pause\n"
"[2] Skip\n"
"[3] Previous song\n"
"[4] Stop\n"
"[5] Close this menu (return to listening)\n"
"[6] Stop looping music"
)
choice = int(input())
if choice == 1:
if not paused:
paused = True
self.mixer.music.pause()
print(
f"Currently paused: {self.nextmeta}\n"
"Press <Ctrl>+<C> to open the menu and start playing again!"
)
else:
paused = False
self.mixer.music.unpause()
print(
f"Currently playing: {self.nextmeta}\n"
"Press <Ctrl>+<C> to open the menu!"
)
continue
elif choice == 2:
# figure smth out.
pass
elif choice == 3:
# same thing.
pass
elif choice == 4:
self.mixer.music.stop()
self.mixer.music.unload()
return
elif choice == 5:
print(
f"Currently playing: {self.nextmeta}\n"
"Press <Ctrl>+<C> to open the menu!"
)
continue
elif choice == 6:
if self.looping is False:
print("Can't stop looping music when we're not looping!")
continue
else:
self.mixer.music.stop()
self.mixer.music.unload()
self.looping = False
return
else:
self.discordrp.set_drp(_file, "localfile")
self.mixer.music.load(_file)
self.mixer.music.play()
while self.mixer.music.get_busy():
print(
"Currently listening to: localfile\n"
"Here's some things you can do while listening:\n"
"[1] Pause\n"
"[2] Skip\n"
"[3] Previous song\n"
"[4] Stop"
)
choice = input()
class MusicGetter:
def __init__(self):
self.musicplayer = None
self.server = None
self.discordrp = None
self.cursong_idx = 0
self.maxidx = None
self.songs = []
self.metadata = {}
print("Welcome to WebJammies!")
print("Checking some things...")
if richpresense is True:
self.discordrp = DiscordRP()
print("Discord Rich Presence enabled!")
else:
self.discordrp = DiscordRP()
# We still init it to prevent any bad code thingys
print("Discord Rich Presence not active.")
print("Initializing MusicPlayer...")
if os.path.exists("webjammies_temp"):
shutil.rmtree("webjammies_temp")
os.mkdir("webjammies_temp")
os.chdir("webjammies_temp")
self.musicplayer = MusicPlayer()
self.musicplayer.discordrp = self.discordrp
def get_server(self):
server = input("Enter the server of choice: ")
self.server = server
content = requests.get(f"{server}/webjammies/index.ini", verify=False)
text = content.text.splitlines()
append_to_songs = False
append_to_meta = False
for line in text:
if line.startswith("#"):
continue
elif line == "[FILES]":
append_to_songs = True
continue
elif line == "[META]":
append_to_songs = False
append_to_meta = True
i_since = 0
elif line == "[EOF]":
break
elif append_to_songs is True:
self.songs.append(f"{line}")
elif append_to_meta is True:
self.metadata[f"{self.songs[i_since]}"] = line
i_since += 1
else:
print("This server is not a WebJammies server! Please enter the correct server.")
exit(1)
self.maxidx = len(self.songs)
print("Loaded server configuration.")
def download_song(self, song):
content = requests.get(f"{self.server}/webjammies/{song}", verify=False)
# for some reason windows will throw permission denied errors trying to
# overwrite the file, so that's why i'll remove it first for windows
if os.name == "nt":
if os.path.exists("song0.mp3"):
os.remove("song0.mp3")
with open("song0.mp3", "wb") as f:
f.write(content.content)
self.musicplayer.nextmeta = self.metadata.get(song)
def menu(self):
self.discordrp.default_drp()
if self.server is None:
print(
"\n"
"Whatcha wanna do?\n"
"[1] Connect to a server\n"
"[2] Start playing audio\n"
"[5] Loop music\n"
"[6] Exit"
)
else:
print(
"\n"
f"Currently you're connected to: {self.server}\n"
"Whatcha wanna do?\n"
"[1] Connect to a different server\n"
"[2] Start playing audio\n"
"[3] Choose a song\n"
"[4] Show whole playlist\n"
"[5] Loop music\n"
"[6] Exit"
)
# TODO: add some error checking
choice = int(input())
if choice == 1:
self.get_server()
print("MusicPlayer is loading the new configuration...")
self.musicplayer.server = self.server
self.download_song(self.songs[0])
self.cursong_idx += 1
return
elif choice == 2:
print("")
if self.server is None:
print("Cannot play music without a server!")
return
self.musicplayer.play_audio_file()
# automatically retrieve the next file after playing.
self.download_song(self.songs[self.cursong_idx])
# if we've reached the end of the list of songs, just wrap to 0
if self.cursong_idx >= self.maxidx:
self.cursong_idx = 0
else:
self.cursong_idx += 1
return
elif choice == 3:
print("")
print("Enter some details about the song to listen to!")
song = input()
for key, value in self.metadata.items():
if song.lower() in value.lower():
print(
"We might have a match!\n"
f"Is the song you're looking for: {value}?"
)
choice = input("You can enter 'y' for yes, 'n' for no.\n")
if choice.lower() == "y":
# I know this is stupid, but I really just wanna make sure it works :'(
song_idx = self.songs.index(key)
self.download_song(self.songs[song_idx])
self.musicplayer.play_audio_file()
break
elif choice.lower() == "n":
print("Aww shucks! We'll keep looking!")
continue
else:
print("Uuh, that wasn't what we were expecting!")
return
else:
print("Bad news, we couldn't find anything! :(")
return
return
elif choice == 4:
print("")
print(f"Alrighty! Here's the whole playlist from the server {self.server}")
for value in self.metadata.values():
print(f"{value}")
print("And that's all!")
return
elif choice == 5:
print("")
# TODO: make this better
if self.server is None:
print("Cannot play music without a server!")
return
self.musicplayer.looping = True
while self.musicplayer.looping:
self.musicplayer.play_audio_file()
self.download_song(self.songs[self.cursong_idx])
if self.cursong_idx >= self.maxidx:
self.cursong_idx = 0
self.download_song(self.songs[self.cursong_idx])
else:
self.cursong_idx += 1
return
elif choice == 6:
exit(0)
class DiscordRP:
def __init__(self):
self.client_id = "1346549750555476018"
try:
self.presence = Presence(self.client_id)
except Exception:
print("Error! Could not connect to Discord! Make sure Discord is active!")
print("Disabling presence...")
self.presence = None
self.default_drp()
def default_drp(self):
if self.presence is None:
return
self.presence.set(
{
"name": "WebJammies",
"type": 2,
"details": "Not jamming at the moment",
"state": "Looking for a server",
"timestamps": {"start": int(time.time())}
}
)
def set_drp(self, song, server):
if self.presence is None:
return
self.presence.set(
{
"name": "WebJammies",
"type": 2,
"details": f"Jamming to {song}!",
"state": f"Found on server: {server}",
"timestamps": {"start": int(time.time())}
}
)
if __name__ == "__main__":
m = MusicGetter()
while True:
m.menu()