feat: streamlit api
This commit is contained in:
parent
bdf44bcd94
commit
573429acd2
3 changed files with 74 additions and 61 deletions
14
.envrc
14
.envrc
|
@ -10,9 +10,21 @@ layout_uv() {
|
||||||
VIRTUAL_ENV="$(pwd)/.venv"
|
VIRTUAL_ENV="$(pwd)/.venv"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export PATH=$PATH":$VIRTUAL_ENV/bin"
|
PATH_add "$VIRTUAL_ENV/bin"
|
||||||
export UV_ACTIVE=1 # or VENV_ACTIVE=1
|
export UV_ACTIVE=1 # or VENV_ACTIVE=1
|
||||||
export VIRTUAL_ENV
|
export VIRTUAL_ENV
|
||||||
}
|
}
|
||||||
|
|
||||||
layout_uv
|
layout_uv
|
||||||
|
|
||||||
|
|
||||||
|
# Path to your virtual environment directory
|
||||||
|
VENV_DIR="./venv"
|
||||||
|
|
||||||
|
# Load and activate the virtual environment if it exists
|
||||||
|
if [[ -d "$VENV_DIR" ]]; then
|
||||||
|
layout python "$VENV_DIR"
|
||||||
|
else
|
||||||
|
echo "Virtual environment not found at $VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
49
main.py
49
main.py
|
@ -1,7 +1,6 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from pygame.mixer import music
|
|
||||||
|
|
||||||
from download_service import DownloadService
|
from download_service import DownloadService
|
||||||
from music_player import MusicPlayer
|
from music_player import MusicPlayer
|
||||||
|
@ -10,15 +9,10 @@ from music_player import MusicPlayer
|
||||||
class ChangePlayerState(Enum):
|
class ChangePlayerState(Enum):
|
||||||
play = "play"
|
play = "play"
|
||||||
pause = "pause"
|
pause = "pause"
|
||||||
unpause = "unpause"
|
resume = "resume"
|
||||||
stop = "stop"
|
stop = "stop"
|
||||||
|
|
||||||
|
|
||||||
class Songs(Enum):
|
|
||||||
era = "era.mp3"
|
|
||||||
royal_beggars = "royal-beggars.mp3"
|
|
||||||
|
|
||||||
|
|
||||||
queue: list[str] = []
|
queue: list[str] = []
|
||||||
|
|
||||||
tags_metadata = [
|
tags_metadata = [
|
||||||
|
@ -32,22 +26,37 @@ dl_service = DownloadService()
|
||||||
|
|
||||||
|
|
||||||
# Experimental
|
# Experimental
|
||||||
@app.get("/test/player/queue", tags=["experimental"])
|
@app.get("/queue", tags=["queue"])
|
||||||
def play_music():
|
def play_music():
|
||||||
return player.get_queue()
|
return player.get_queue()
|
||||||
|
|
||||||
|
|
||||||
@app.post("/test/queue", tags=["experimental"])
|
@app.post("/queue", tags=["queue"])
|
||||||
def post_to_queue(url: str):
|
def post_to_queue(url: str):
|
||||||
track = dl_service.download(url)
|
track = dl_service.download(url)
|
||||||
player.add_to_queue(track)
|
player.add_to_queue(track)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/player/play", tags=["experimental"])
|
@app.post("/player/play", tags=["player"])
|
||||||
def play():
|
def player_play():
|
||||||
player.play()
|
player.play()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/player/pause", tags=["player"])
|
||||||
|
def player_pause():
|
||||||
|
player.pause()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/player/resume", tags=["player"])
|
||||||
|
def player_resume():
|
||||||
|
player.resume()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/player/stop", tags=["player"])
|
||||||
|
def player_stop():
|
||||||
|
player.stop()
|
||||||
|
|
||||||
|
|
||||||
# Player
|
# Player
|
||||||
@app.put("/player/volume", tags=["player"])
|
@app.put("/player/volume", tags=["player"])
|
||||||
def set_volume(volume: float):
|
def set_volume(volume: float):
|
||||||
|
@ -59,24 +68,6 @@ def get_volume():
|
||||||
return {"volume": player.get_volume()}
|
return {"volume": player.get_volume()}
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/player/state", tags=["player"])
|
|
||||||
def update_state(status: ChangePlayerState):
|
|
||||||
match status:
|
|
||||||
case ChangePlayerState.play:
|
|
||||||
music.play()
|
|
||||||
case ChangePlayerState.pause:
|
|
||||||
music.pause()
|
|
||||||
case ChangePlayerState.unpause:
|
|
||||||
music.unpause()
|
|
||||||
case ChangePlayerState.stop:
|
|
||||||
music.stop()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/player/state", tags=["player"])
|
@app.get("/player/state", tags=["player"])
|
||||||
def get_state():
|
def get_state():
|
||||||
return {"state": player.get_state(), "current_track": player.get_current_track()}
|
return {"state": player.get_state(), "current_track": player.get_current_track()}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/queue", tags=["queue"])
|
|
||||||
def add_to_queue(filename: str):
|
|
||||||
queue.append(filename)
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import time
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
class PlayerState(Enum):
|
class PlayerState(Enum):
|
||||||
|
@ -16,23 +16,26 @@ class Track(BaseModel):
|
||||||
artist: str
|
artist: str
|
||||||
title: str
|
title: str
|
||||||
duration: int
|
duration: int
|
||||||
filepath: str
|
filepath: str = Field(hidden=True) # don't show it in API responses
|
||||||
|
|
||||||
|
|
||||||
class Queue:
|
class Queue:
|
||||||
queue: list[Track]
|
queue: list[Track]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.queue: list[Track] = []
|
self._queue: list[Track] = []
|
||||||
|
|
||||||
def next(self) -> Track | None:
|
def next(self) -> Track | None:
|
||||||
if len(self.queue) > 0:
|
if len(self._queue) > 0:
|
||||||
return self.queue.pop(0)
|
return self._queue.pop(0)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add(self, track: Track) -> None:
|
def add(self, track: Track) -> None:
|
||||||
self.queue.append(track)
|
self._queue.append(track)
|
||||||
|
|
||||||
|
def len(self) -> int:
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
|
||||||
class MusicPlayer:
|
class MusicPlayer:
|
||||||
|
@ -47,21 +50,23 @@ class MusicPlayer:
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
# Music Player
|
# Music Player
|
||||||
self.queue: Queue = Queue()
|
self.queue: Queue = Queue()
|
||||||
self._current_track: Track | None = None
|
self.current_track: Track | None = None
|
||||||
self._state: PlayerState = PlayerState.Stopped
|
self._state: PlayerState = PlayerState.Stopped
|
||||||
|
# State change flags
|
||||||
|
self._queue_changed: bool = False
|
||||||
|
self._track_changed: bool = False
|
||||||
|
|
||||||
def _loop(self):
|
def _loop(self):
|
||||||
while self._running:
|
while self._running:
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.USEREVENT:
|
if event.type == pygame.USEREVENT: # a song just ended
|
||||||
if self._current_track:
|
if self.current_track:
|
||||||
print(f"Track {self._current_track.title} ended")
|
print(f"Track {self.current_track.title} ended")
|
||||||
self._play_next_track()
|
self._play_next_track()
|
||||||
|
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def _load_track(self, track: Track):
|
def _load_track(self, track: Track):
|
||||||
with self.lock:
|
|
||||||
self.current_track = track
|
self.current_track = track
|
||||||
pygame.mixer.music.unload()
|
pygame.mixer.music.unload()
|
||||||
pygame.mixer.music.load(self.current_track.filepath)
|
pygame.mixer.music.load(self.current_track.filepath)
|
||||||
|
@ -71,43 +76,48 @@ class MusicPlayer:
|
||||||
if next_track:
|
if next_track:
|
||||||
self._load_track(next_track)
|
self._load_track(next_track)
|
||||||
pygame.mixer.music.play()
|
pygame.mixer.music.play()
|
||||||
|
self._state = PlayerState.Playing
|
||||||
|
|
||||||
def add_to_queue(self, track: Track):
|
def add_to_queue(self, track: Track):
|
||||||
|
with self.lock:
|
||||||
|
que_len = self.queue.len()
|
||||||
self.queue.add(track)
|
self.queue.add(track)
|
||||||
|
# If queue is empty and no corrent track, start playing
|
||||||
|
if que_len == 0 and not self.current_track:
|
||||||
|
self._play_next_track()
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
if self._current_track:
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
if self.current_track:
|
||||||
pygame.mixer.music.play()
|
pygame.mixer.music.play()
|
||||||
|
self._state = PlayerState.Playing
|
||||||
else:
|
else:
|
||||||
self._play_next_track()
|
self._play_next_track()
|
||||||
self._state = PlayerState.Playing
|
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
pygame.mixer.music.pause()
|
pygame.mixer.music.pause()
|
||||||
self._state = PlayerState.Paused
|
self._state = PlayerState.Paused
|
||||||
print("Set player state to Paused")
|
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
pygame.mixer.music.unpause()
|
pygame.mixer.music.unpause()
|
||||||
self._state = PlayerState.Playing
|
self._state = PlayerState.Playing
|
||||||
print("Set player state to Playing")
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
pygame.mixer.music.stop()
|
pygame.mixer.music.stop()
|
||||||
self._state = PlayerState.Stopped
|
self._state = PlayerState.Stopped
|
||||||
self._current_track = None
|
self.current_track = None
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
with self.lock:
|
||||||
self._running = False
|
self._running = False
|
||||||
self.thread.join()
|
self.thread.join()
|
||||||
pygame.mixer.quit()
|
pygame.mixer.quit()
|
||||||
|
|
||||||
def get_queue(self) -> Queue:
|
def get_queue(self) -> Queue:
|
||||||
return self.queue
|
return self.queue._queue
|
||||||
|
|
||||||
def set_volume(self, volume: float):
|
def set_volume(self, volume: float):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
@ -120,4 +130,4 @@ class MusicPlayer:
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def get_current_track(self) -> Track | None:
|
def get_current_track(self) -> Track | None:
|
||||||
return self._current_track
|
return self.current_track
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue