commit
a7dad655df
3 changed files with 90 additions and 10 deletions
17
poetry.lock
generated
17
poetry.lock
generated
|
@ -299,6 +299,21 @@ files = [
|
||||||
{file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"},
|
{file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-mpd2"
|
||||||
|
version = "3.1.0"
|
||||||
|
description = "A Python MPD client library"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "python-mpd2-3.1.0.tar.gz", hash = "sha256:f33c2cdb0d6baa74a36724f38c1c4a099a7ce2c8ec4a2bb7192150a5855df476"},
|
||||||
|
{file = "python_mpd2-3.1.0-py2.py3-none-any.whl", hash = "sha256:c4d44a54e88a675f7301fdb11a1bd31165a6f51a664dd41e8137e92f7b02ebfb"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
twisted = ["Twisted"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rpi-gpio"
|
name = "rpi-gpio"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -354,4 +369,4 @@ files = [
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "5f10ea01a98ffb96107cc03228a3164fc600ce76ac56ed249700e5a581083f1e"
|
content-hash = "b90de4dbcb9039b01369909dd0a6cf26b2346671b8907ccbbe5bbea6a06a6182"
|
||||||
|
|
|
@ -10,6 +10,7 @@ python = "^3.9"
|
||||||
spidev = "^3.6"
|
spidev = "^3.6"
|
||||||
mfrc522 = "^0.0.7"
|
mfrc522 = "^0.0.7"
|
||||||
pydantic = "^1.10.7"
|
pydantic = "^1.10.7"
|
||||||
|
python-mpd2 = "^3.1.0"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
|
|
@ -14,7 +14,7 @@ from dataclasses import dataclass
|
||||||
from mfrc522.SimpleMFRC522 import SimpleMFRC522
|
from mfrc522.SimpleMFRC522 import SimpleMFRC522
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from random import randrange
|
from random import randrange
|
||||||
|
from mpd import MPDClient
|
||||||
|
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
|
|
||||||
|
@ -79,6 +79,10 @@ class Renderer(ABC):
|
||||||
def is_playing(self) -> bool:
|
def is_playing(self) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
reader: SimpleMFRC522
|
reader: SimpleMFRC522
|
||||||
|
@ -194,7 +198,7 @@ class Player:
|
||||||
status_file.write(self.current_title.status.json(indent=2))
|
status_file.write(self.current_title.status.json(indent=2))
|
||||||
self.current_title = None
|
self.current_title = None
|
||||||
|
|
||||||
def process(self):
|
def process(self) -> None:
|
||||||
tag_id: Optional[int] = self.read_burst()
|
tag_id: Optional[int] = self.read_burst()
|
||||||
|
|
||||||
# current_tag = self.current_title.tag_id if self.current_title else None
|
# current_tag = self.current_title.tag_id if self.current_title else None
|
||||||
|
@ -219,7 +223,7 @@ class Player:
|
||||||
self.advance()
|
self.advance()
|
||||||
self.start_playing()
|
self.start_playing()
|
||||||
|
|
||||||
def tag_detected(self, tag_id: int):
|
def tag_detected(self, tag_id: int) -> None:
|
||||||
log.info(f"tag detected: {tag_id:x}")
|
log.info(f"tag detected: {tag_id:x}")
|
||||||
cfg = self.get_title_config(tag_id)
|
cfg = self.get_title_config(tag_id)
|
||||||
if not cfg:
|
if not cfg:
|
||||||
|
@ -243,11 +247,14 @@ class Player:
|
||||||
):
|
):
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
def tag_removed(self):
|
def tag_removed(self) -> None:
|
||||||
if self.current_title is not None:
|
if self.current_title is not None:
|
||||||
log.info(f"tag removed: {self.current_title.tag_id:x}")
|
log.info(f"tag removed: {self.current_title.tag_id:x}")
|
||||||
self.stop_playing()
|
self.stop_playing()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.renderer.close()
|
||||||
|
|
||||||
|
|
||||||
class DebugRenderer(Renderer):
|
class DebugRenderer(Renderer):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
@ -275,16 +282,73 @@ class DebugRenderer(Renderer):
|
||||||
def is_playing(self) -> bool:
|
def is_playing(self) -> bool:
|
||||||
return (self.start != 0) and (self.get_time() - self.offset < 10)
|
return (self.start != 0) and (self.get_time() - self.offset < 10)
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
log.debug("DebugRenderer: close()")
|
||||||
|
|
||||||
|
|
||||||
|
class MpdRenderer(Renderer):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.mpd = MPDClient() # type:ignore
|
||||||
|
self.mpd.timeout = 10
|
||||||
|
self.mpd.connect("/var/run/mpd/socket")
|
||||||
|
self.mpd.clear()
|
||||||
|
self.mpd.single(1)
|
||||||
|
self.mpd.consume(1)
|
||||||
|
|
||||||
|
# self.start: int = 0
|
||||||
|
# self.offset: int = 0
|
||||||
|
|
||||||
|
def get_tracks(self, path: str) -> list[str]:
|
||||||
|
all_files = os.listdir(path)
|
||||||
|
return sorted([f for f in all_files if (f.endswith(".mp3") or f.endswith(".flac"))])
|
||||||
|
|
||||||
|
def play(self, path: str, from_time: int) -> None:
|
||||||
|
log.info(f"MpdRenderer: play({path}, {from_time})")
|
||||||
|
self.mpd.clear()
|
||||||
|
self.mpd.add(f"file://{path}")
|
||||||
|
self.mpd.play()
|
||||||
|
if from_time != 0:
|
||||||
|
self.mpd.seekcur(from_time)
|
||||||
|
# self.start = int(time.time())
|
||||||
|
# self.offset = from_time
|
||||||
|
|
||||||
|
def get_time(self) -> int:
|
||||||
|
# return int(time.time()) - self.start + self.offset
|
||||||
|
return int(float(self.mpd.status()["elapsed"]))
|
||||||
|
|
||||||
|
def stop(self) -> None:
|
||||||
|
log.info("MpdRenderer: stop()")
|
||||||
|
self.mpd.stop()
|
||||||
|
self.mpd.clear()
|
||||||
|
# self.start = 0
|
||||||
|
# self.offset = 0
|
||||||
|
|
||||||
|
def is_playing(self) -> bool:
|
||||||
|
return self.mpd.status()["state"] == "play"
|
||||||
|
# return (self.start != 0) and (self.get_time() - self.offset < 10)
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.mpd.close()
|
||||||
|
self.mpd.disconnect()
|
||||||
|
|
||||||
|
|
||||||
def main(media_path: str, renderer_type: str) -> None:
|
def main(media_path: str, renderer_type: str) -> None:
|
||||||
if renderer_type == "dummy":
|
if renderer_type == "dummy":
|
||||||
renderer = DebugRenderer()
|
renderer = DebugRenderer()
|
||||||
|
elif renderer_type == "mpd":
|
||||||
|
renderer = MpdRenderer()
|
||||||
else:
|
else:
|
||||||
renderer = DebugRenderer()
|
renderer = DebugRenderer()
|
||||||
player = Player(media_path=media_path, renderer=renderer)
|
player = Player(media_path=media_path, renderer=renderer)
|
||||||
while True:
|
log.info('sleepywaves ready.')
|
||||||
player.process()
|
try:
|
||||||
time.sleep(1)
|
while True:
|
||||||
|
player.process()
|
||||||
|
time.sleep(1)
|
||||||
|
finally:
|
||||||
|
player.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -302,8 +366,8 @@ if __name__ == "__main__":
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--renderer",
|
"--renderer",
|
||||||
choices=("dummy"),
|
choices=("dummy", "mpd"),
|
||||||
default="dummy",
|
default="mpd",
|
||||||
help="media renderer to use as backend",
|
help="media renderer to use as backend",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue