1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-23 01:16:18 +00:00

Jukebox endpoint + some fixes

This commit is contained in:
Alban Féron 2019-09-01 17:07:35 +02:00
parent 75b89e5f45
commit 0d40ede256
No known key found for this signature in database
GPG Key ID: 8CE0313646D16165
5 changed files with 109 additions and 10 deletions

View File

@ -98,4 +98,5 @@ from .annotation import *
from .chat import * from .chat import *
from .search import * from .search import *
from .playlists import * from .playlists import *
from .jukebox import *
from .unsupported import * from .unsupported import *

94
supysonic/api/jukebox.py Normal file
View File

@ -0,0 +1,94 @@
# coding: utf-8
#
# This file is part of Supysonic.
# Supysonic is a Python implementation of the Subsonic server API.
#
# Copyright (C) 2019 Alban 'spl0k' Féron
#
# Distributed under terms of the GNU AGPLv3 license.
import uuid
from flask import current_app, request
from pony.orm import ObjectNotFound
from ..daemon import DaemonClient
from ..daemon.exceptions import DaemonUnavailableError
from ..db import Track
from . import api
from .exceptions import GenericError, MissingParameter
@api.route("/jukeboxControl.view", methods=["GET", "POST"])
def jukebox_control():
action = request.values["action"]
index = request.values.get("index")
offset = request.values.get("offset")
id = request.values.getlist("id")
gain = request.values.get("gain")
if action not in (
"get",
"status",
"set",
"start",
"stop",
"skip",
"add",
"clear",
"remove",
"shuffle",
"setGain",
):
raise GenericError("Unknown action")
arg = None
if action == "set":
if not id:
arg = []
else:
arg = [uuid.UUID(i) for i in id]
elif action == "skip":
if not index:
raise MissingParameter("index")
else:
arg = int(index)
elif action == "add":
if not id:
raise MissingParameter("id")
else:
arg = [uuid.UUID(i) for i in id]
elif action == "remove":
if not index:
raise MissingParameter("index")
else:
arg = int(index)
elif action == "setGain":
if not gain:
raise MissingParameter("gain")
else:
arg = float(gain)
try:
status = DaemonClient(current_app.config["DAEMON"]["socket"]).jukebox_control(
action, arg
)
except DaemonUnavailableError:
raise GenericError("Jukebox unavaliable")
rv = dict(currentIndex=status.index, playing=status.playing, gain=status.gain)
if action == "get":
playlist = []
for path in status.playlist:
try:
playlist.append(Track.get(path=path))
except ObjectNotFound:
pass
rv["entry"] = [
t.as_subsonic_child(request.user, request.client) for t in playlist
]
return request.formatter("jukeboxPlaylist", rv)
else:
return request.formatter("jukeboxStatus", rv)

View File

@ -119,6 +119,7 @@ class JukeboxResult(DaemonCommandResult):
self.playing = jukebox.playing self.playing = jukebox.playing
self.index = jukebox.index self.index = jukebox.index
self.gain = jukebox.gain self.gain = jukebox.gain
self.playlist = ()
class DaemonClient(object): class DaemonClient(object):
@ -159,9 +160,9 @@ class DaemonClient(object):
with self.__get_connection() as c: with self.__get_connection() as c:
c.send(ScannerStartCommand(folders, force)) c.send(ScannerStartCommand(folders, force))
def jukebox_control(self, action, *args): def jukebox_control(self, action, arg):
if not isinstance(action, strtype): if not isinstance(action, strtype):
raise TypeError("Expecting string, got " + str(type(action))) raise TypeError("Expecting string, got " + str(type(action)))
with self.__get_connection() as c: with self.__get_connection() as c:
c.send(JukeboxCommand(action, args)) c.send(JukeboxCommand(action, arg))
return c.recv() return c.recv()

View File

@ -37,7 +37,7 @@ class Daemon(object):
watcher = property(lambda self: self.__watcher) watcher = property(lambda self: self.__watcher)
scanner = property(lambda self: self.__scanner) scanner = property(lambda self: self.__scanner)
jukbox = property(lambda self: self.__jukebox) jukebox = property(lambda self: self.__jukebox)
def __handle_connection(self, connection): def __handle_connection(self, connection):
cmd = connection.recv() cmd = connection.recv()

View File

@ -11,7 +11,7 @@ import logging
import shlex import shlex
import time import time
from pony.orm import select from pony.orm import db_session, ObjectNotFound
from random import shuffle from random import shuffle
from subprocess import Popen, DEVNULL from subprocess import Popen, DEVNULL
from threading import Thread, Event, RLock from threading import Thread, Event, RLock
@ -59,20 +59,22 @@ class Jukebox(object):
self.__stop.set() self.__stop.set()
def skip(self, index): def skip(self, index):
if not self.playing:
return
if index < 0 or index >= len(self.__playlist): if index < 0 or index >= len(self.__playlist):
raise IndexError() raise IndexError()
with self.__lock: with self.__lock:
self.__index = index - 1 self.__index = index - 1
self.__skip.set() self.__skip.set()
self.start()
def add(self, tracks): def add(self, tracks):
paths = select(t.path for t in Track if t.id in tracks)
with self.__lock: with self.__lock:
self.__playlist += paths[:] with db_session:
for t in tracks:
try:
self.__playlist.append(Track[t].path)
except ObjectNotFound:
pass
def clear(self): def clear(self):
with self.__lock: with self.__lock:
@ -101,10 +103,11 @@ class Jukebox(object):
self.__thread.join() self.__thread.join()
def __play_thread(self): def __play_thread(self):
proc = None
while not self.__stop.is_set(): while not self.__stop.is_set():
if self.__skip.is_set(): if self.__skip.is_set():
proc.terminate() proc.terminate()
proc.join() proc.wait()
self.__skip.clear() self.__skip.clear()
if proc is None or proc.poll() is not None: if proc is None or proc.poll() is not None: