wow asyncio (fix shit)

This commit is contained in:
deneb 2025-03-03 08:26:05 +01:00
parent ec60817409
commit e63652b0f7
2 changed files with 82 additions and 68 deletions

149
app.py
View file

@ -1,3 +1,4 @@
from asyncio.streams import StreamReader, StreamWriter
import re
from typing import Any, Mapping
from flask import Flask, render_template, request, send_file, jsonify
@ -6,9 +7,9 @@ import subprocess
import signal
from pathlib import Path
import tempfile
import socket
import json
import time
import asyncio
mpv_pidfile = Path(tempfile.gettempdir()).joinpath("laas_mpv.pidfile")
mpv_socket = Path(tempfile.gettempdir()).joinpath("mpvsocket")
@ -34,7 +35,7 @@ def cleanup_unclean():
def shutdown(status: int = 0):
playback_stop()
mpv_stop()
os._exit(status)
@ -43,25 +44,36 @@ def sigh(signum: int, _frame):
shutdown(status)
def playback_start():
async def mpv_start(args: list[str] = ["--idle"]):
global mpv_process
mpv_process = subprocess.Popen(
[
"mpv",
f"--input-ipc-server={mpv_socket}",
"--shuffle",
# "--loop-playlist",
"--no-video",
f"--volume={volume}",
str(music_path),
*args,
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
open(mpv_pidfile, "w").write(str(mpv_process.pid))
while not mpv_socket.exists():
await asyncio.sleep(0.01)
def playback_stop():
async def playback_start(args: list[str] = ["--idle"]):
return await mpv_start(
args=[
"--shuffle",
# "--loop-playlist",
str(music_path),
]
)
def mpv_stop():
global mpv_process
if mpv_process is not None:
mpv_process.terminate()
@ -71,7 +83,7 @@ def playback_stop():
mpv_pidfile.unlink()
def is_playing():
def mpv_running():
global mpv_process
running = mpv_process is not None
if mpv_process is not None:
@ -81,30 +93,33 @@ def is_playing():
return running
def mpv_socket_open() -> socket.socket | None:
if is_playing():
sock = socket.socket(socket.AF_UNIX)
async def mpv_socket_open() -> tuple[StreamReader, StreamWriter] | None:
async def socket_open_helper():
while True:
try:
sock.connect(str(mpv_socket).encode())
break
except (ConnectionRefusedError, FileNotFoundError):
return await asyncio.open_unix_connection(mpv_socket)
except ConnectionRefusedError:
pass
return sock
if mpv_running():
try:
return await asyncio.wait_for(socket_open_helper(), timeout=3)
except asyncio.TimeoutError:
mpv_stop()
return None
def mpv_socket_command(
sock: socket.socket, command: dict[str, Any]
) -> Mapping[str, Any]:
sock.send((json.dumps(command) + "\n").encode())
async def mpv_socket_command(
reader: StreamReader, writer: StreamWriter, command: dict[str, Any]
) -> Mapping[str, Any] | None:
if writer.is_closing():
return
reply = b""
while (data := sock.recv(1)) != b"\n":
reply += data
writer.write((json.dumps(command) + "\n").encode())
await writer.drain()
reply = await reader.readline()
return json.loads(reply.decode())
@ -118,20 +133,20 @@ def sizeof_fmt(num, suffix="B"):
@app.route("/", methods=["GET"])
def route_interface():
return render_template("player.html", playing=is_playing())
return render_template("player.html", playing=mpv_running())
@app.route("/", methods=["POST"])
def route_toggle():
async def route_toggle():
global mpv_process
if is_playing():
if mpv_running():
print("Stopping Audio Playback..")
playback_stop()
mpv_stop()
else:
print("Starting Audio Playback..")
playback_start()
await playback_start()
return render_template("player.html", playing=is_playing())
return render_template("player.html", playing=mpv_running())
@app.route("/files/<path:path>", methods=["GET"])
@ -156,33 +171,33 @@ def filemgr(path=""):
@app.route("/api/start", methods=["POST"])
def api_start():
if is_playing():
async def api_start():
if mpv_running():
return jsonify("Laas is already lofi'ing"), 400
else:
playback_start()
await playback_start()
return jsonify("ok"), 200
@app.route("/api/stop", methods=["POST"])
def api_stop():
if not is_playing():
if not mpv_running():
return jsonify("You cant stop when theres no playback, womp womp!"), 400
else:
playback_stop()
mpv_stop()
return jsonify("ok"), 200
@app.route("/api/status", methods=["GET"])
def api_status():
return jsonify(is_playing())
return jsonify(mpv_running())
@app.route("/api/nowplaying", methods=["GET"])
def api_nowplaying():
async def api_nowplaying():
response: dict[str, Any] = {"status": "stopped"}
sock = mpv_socket_open()
sock = await mpv_socket_open()
if sock is not None:
response["status"] = "playing"
@ -199,16 +214,21 @@ def api_nowplaying():
else:
key = prop
reply_json = mpv_socket_command(sock, {"command": ["get_property", prop]})
reply_json = await mpv_socket_command(
*sock, {"command": ["get_property", prop]}
)
if reply_json is None:
break
response[key] = reply_json["data"] if "data" in reply_json else "Unknown"
sock.close()
sock[1].close()
await sock[1].wait_closed()
return jsonify(response)
@app.route("/api/play/<path:filename_or_url>", methods=["POST"])
def api_play_file(
async def api_play_file(
filename_or_url: str, error_str: str = "Could not play file '{filename}'"
):
if re.match("^https?://.*", filename_or_url):
@ -219,23 +239,19 @@ def api_play_file(
return jsonify(error_str.format(filename=filename_or_url)), 404
playback_uri = str(file_path)
if not is_playing():
playback_start()
if mpv_running():
mpv_stop()
sock = mpv_socket_open()
if sock is not None:
mpv_socket_command(sock, {"command": ["loadfile", playback_uri]})
sock.close()
await mpv_start([playback_uri])
time.sleep(0.1)
if is_playing():
return jsonify("ok")
if not mpv_running():
return jsonify(error_str.format(filename=filename_or_url)), 500
return jsonify(error_str.format(filename=filename_or_url)), 500
return jsonify("ok")
@app.route("/api/play", methods=["POST"])
def api_play_file2(error_str: str = "Could not play file '{filename}'"):
async def api_play_file2(error_str: str = "Could not play file '{filename}'"):
filename_or_url = request.get_data().decode()
if re.match("^https?://.*", filename_or_url):
playback_uri = filename_or_url
@ -245,30 +261,26 @@ def api_play_file2(error_str: str = "Could not play file '{filename}'"):
return jsonify(error_str.format(filename=filename_or_url)), 404
playback_uri = str(file_path)
if not is_playing():
playback_start()
if mpv_running():
mpv_stop()
sock = mpv_socket_open()
if sock is not None:
mpv_socket_command(sock, {"command": ["loadfile", playback_uri]})
sock.close()
await mpv_start([playback_uri])
time.sleep(0.1)
if is_playing():
return jsonify("ok")
if not mpv_running():
return jsonify(error_str.format(filename=filename_or_url)), 500
return jsonify(error_str.format(filename=filename_or_url)), 500
return jsonify("ok")
@app.route("/api/isengard", methods=["POST"])
def api_isengard():
return api_play_file(
async def api_isengard():
return await api_play_file(
"isengard.mp3", error_str="Could not take the hobbits to Isengard"
)
@app.route("/api/volume", methods=["GET", "PUT"])
def api_volume():
async def api_volume():
if request.method == "PUT":
global volume
try:
@ -278,10 +290,11 @@ def api_volume():
except Exception:
return jsonify("bad volume"), 400
sock = mpv_socket_open()
sock = await mpv_socket_open()
if sock is not None:
mpv_socket_command(sock, {"command": ["set", "volume", str(volume)]})
sock.close()
await mpv_socket_command(*sock, {"command": ["set", "volume", str(volume)]})
sock[1].close()
await sock[1].wait_closed()
return jsonify(volume)
@ -297,4 +310,4 @@ if __name__ == "__main__":
try:
app.run(host="::", port=1337)
finally:
playback_stop()
mpv_stop()

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
flask[async]