Last active
December 3, 2022 06:07
-
-
Save ohaiibuzzle/19e382cb668263048719d29c99a649e4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A simple and silly HTTP server that plays audio from internet sources | |
""" | |
from http import server | |
import urllib | |
import subprocess | |
ffplay = None | |
player = None | |
url = None | |
class Handler(server.BaseHTTPRequestHandler): | |
CSS_CODE = """ | |
<style> | |
body { | |
background-color: #000000; | |
color: #ffffff; | |
font-family: Arial, Helvetica, sans-serif; | |
display:flex; flex-direction:column; justify-content:center; | |
min-height:100vh; | |
align-items:center; | |
text-align:center; | |
} | |
input { | |
padding: 12px 20px; | |
margin: 8px 0; | |
box-sizing: border-box; | |
border: 2px solid #000000; | |
border-radius: 4px; | |
background-color: #ffffff; | |
color: #000000; | |
text-align: center; | |
width: 100%; | |
height: 100%; | |
} | |
input[type=submit] { | |
background-color: #000000; | |
color: #ffffff; | |
border: 2px solid #000000; | |
border-radius: 4px; | |
padding: 12px 20px; | |
cursor: pointer; | |
width: 100%; | |
} | |
input[type=submit]:hover { | |
background-color: #ffffff; | |
color: #000000; | |
border: 2px solid #000000; | |
border-radius: 4px; | |
padding: 12px 20px; | |
cursor: pointer; | |
width: 100%; | |
} | |
.container { | |
width: 80%; | |
margin: auto; | |
max-width: 800px; | |
} | |
#playback-form { | |
display: flex; | |
flex-direction: row; | |
justify-content: center; | |
} | |
.control-button { | |
display: flex; | |
flex-direction: row; | |
justify-content: center; | |
width: 80%; | |
margin: auto; | |
max-width: 800px; | |
} | |
</style> | |
""" | |
HOME_PAGE = """ | |
<html> | |
<head> | |
<title>Wireless Radio</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
{css} | |
</head> | |
<body> | |
<div class="container"> | |
<h1 style="text-align: center; width: 100%;">Wireless Radio</h1> | |
<div class="container"> | |
<p style="text-align: center;">Enter a YouTube URL to play</p> | |
<form method="POST" action="/play" autocomplete="off" id="playback-form"> | |
<input type=url name="url" placeholder="https://www.youtube.com/watch?v=..." style="width: 70%; margin: auto;" required> | |
<input type=text name="time" style="width: 30%; margin: auto;" value="00:00:00" autocomplete="off" pattern=[0-9]{{2,}}:[0-9]{{2}}:[0-9]{{2}}> | |
</form> | |
</div> | |
<div class="control-button"> | |
<input type="submit" value="Play" form="playback-form" style="width: 50%"> | |
<form method="POST" action="/stop" style="width: 50%; height: 100%"><input type=submit value="Stop"></form> | |
</div> | |
<p style="text-align: center;">Currently playing:</p> | |
{url} | |
</div> | |
</body> | |
</html> | |
""" | |
def do_GET(self): | |
global url | |
global ffplay | |
now_playing = ( | |
"Nothing" if (ffplay is None or ffplay.poll() is not None) else url | |
) | |
if now_playing.startswith( | |
"https://www.youtube.com/watch?v=" | |
) or now_playing.startswith("https://youtu.be/"): | |
now_playing = now_playing.replace( | |
"https://www.youtube.com/watch?v=", "https://www.youtube.com/embed/" | |
) | |
now_playing = now_playing.replace( | |
"https://youtu.be/", "https://www.youtube.com/embed/" | |
) | |
now_playing = now_playing + "?autoplay=1" | |
now_playing = f""" | |
<iframe src="{now_playing}" frameborder="0" | |
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" | |
allowfullscreen style="display:block;margin: 0 auto; width: 100%; max-width: 800px; height: 450px;"></iframe> | |
</iframe> | |
""" | |
else: | |
now_playing = f"""<b style="text-align: center;">{now_playing}</b>""" | |
# Construct a simple HTTP resp with a text box and submit as a form | |
self.send_response(200) | |
self.send_header("Content-type", "text/html") | |
self.end_headers() | |
self.wfile.write( | |
self.HOME_PAGE.format(css=self.CSS_CODE, url=now_playing).encode("utf-8") | |
) | |
def do_POST(self): | |
routing_table: dict = { | |
"/play": self.play, | |
"/stop": self.stop, | |
} | |
if self.path in routing_table: | |
routing_table[self.path]() | |
else: | |
self.send_response(302) | |
self.send_header("Location", "/") | |
def play(self): | |
global ffplay | |
global player | |
global url | |
# Check if the radio is playing | |
if ffplay is not None: | |
print("Killing ffplay") | |
ffplay.terminate() | |
ffplay = None | |
if player is not None: | |
print("Killing player") | |
player.terminate() | |
player = None | |
# Read the form data | |
length = int(self.headers["Content-Length"]) | |
post_data = self.rfile.read(length).decode("utf-8") | |
# Parse the form data | |
post_data = urllib.parse.parse_qs(post_data) | |
# Get the URL | |
url = post_data["url"][0] | |
# Get the time offset | |
time = post_data["time"][0] | |
print(f"Playing {url} at {time}") | |
# Start the player | |
# yt-dlp -f bestaudio <url> -o - | ffplay -nodisp -autoexit -ss <time> - | |
player = subprocess.Popen( | |
["yt-dlp", "-f", "bestaudio", url, "-o", "-"], | |
stdout=subprocess.PIPE, | |
stderr=subprocess.DEVNULL, | |
) | |
ffplay = subprocess.Popen( | |
["ffplay", "-hide_banner", "-nodisp", "-autoexit", "-ss", time, "-"], | |
stdin=player.stdout, | |
) | |
# Redirect to the home page | |
self.send_response(302) | |
self.send_header("Location", "/") | |
self.end_headers() | |
def stop(self): | |
global player | |
global ffplay | |
# Check if the radio is playing | |
if ffplay is not None: | |
print("Killing ffplay") | |
ffplay.terminate() | |
ffplay = None | |
if player is not None: | |
print("Killing player") | |
# If it is, kill the process | |
player.terminate() | |
player = None | |
self.send_response(302) | |
self.send_header("Location", "/") | |
self.end_headers() | |
if __name__ == "__main__": | |
server_address = ("", 8000) | |
httpd = server.HTTPServer(server_address, Handler) | |
print("Starting server at http://localhost:8000") | |
httpd.serve_forever() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment