Skip to content

Commit

Permalink
*: introduce webserver (optional, experimental) (#736)
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven authored Nov 8, 2023
1 parent 7a24530 commit d755c88
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
pip install --upgrade pip
pip install pyqt5
pip install "pytest<7.2"
pip install -e .[dev,cookies]
pip install -e .[dev,cookies,webserver]
- name: Install Python(macOS) Dependencies
if: startsWith(matrix.os, 'macos')
Expand Down
10 changes: 8 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{
"files.trimTrailingWhitespace": true
}
"files.trimTrailingWhitespace": true,
"editor.formatOnSave": false,
"[python]": {
"editor.formatOnSaveMode": "file",
"editor.formatOnSave": false,
"editor.defaultFormatter": "eeyore.yapf"
},
}
2 changes: 2 additions & 0 deletions feeluown/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def create_config() -> Config:
config.deffield('VERBOSE', default=0, type_=int, desc='日志详细程度')
config.deffield('RPC_PORT', default=23333, type_=int, desc='RPC 端口')
config.deffield('PUBSUB_PORT', default=23334, type_=int, desc='PUBSUB 端口')
config.deffield('WEB_PORT', default=23332, type_=int, desc='WEB 服务端口')
config.deffield('ENABLE_WEB_SERVER', default=False, type_=bool)
config.deffield('MODE', default=0x0000, desc='CLI or GUI 模式')
config.deffield('THEME', default='auto', desc='auto/light/dark')
config.deffield('MPV_AUDIO_DEVICE', default='auto', desc='MPV 播放设备')
Expand Down
20 changes: 12 additions & 8 deletions feeluown/app/server_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@


class ServerApp(App):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down Expand Up @@ -43,12 +44,15 @@ def initialize(self):
def run(self):
super().run()

asyncio.create_task(self.rpc_server.run(
self.get_listen_addr(),
self.config.RPC_PORT
))
asyncio.create_task(self.pubsub_server.run(
self.get_listen_addr(),
self.config.PUBSUB_PORT,
))
asyncio.create_task(
self.rpc_server.run(self.get_listen_addr(), self.config.RPC_PORT))
asyncio.create_task(
self.pubsub_server.run(
self.get_listen_addr(),
self.config.PUBSUB_PORT,
))
if self.config.ENABLE_WEB_SERVER:
from feeluown.webserver import run_web_server
asyncio.create_task(
run_web_server(self.get_listen_addr(), self.config.WEB_PORT))
asyncio.create_task(run_nowplaying_server(self))
1 change: 1 addition & 0 deletions feeluown/webserver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .server import run_web_server # noqa
82 changes: 82 additions & 0 deletions feeluown/webserver/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import asyncio
import json
import logging

from sanic import Sanic, json as jsonify, Websocket

from feeluown.app import get_app
from feeluown.serializers import serialize
from feeluown.server.pubsub import Gateway as PubsubGateway
from feeluown.server.handlers.cmd import Cmd
from feeluown.server.handlers.status import StatusHandler
from feeluown.server.handlers.player import PlayerHandler

logger = logging.getLogger(__name__)

sanic_app = Sanic('FeelUOwn')


def resp(js):
return jsonify({'code': 200, 'msg': 'ok', 'data': js})


@sanic_app.route('/api/v1/status')
async def status(request):
cmd = Cmd('status')
app = get_app()
handler = StatusHandler(app)
rv = handler.handle(cmd)
return resp(serialize('python', rv, brief=False))


@sanic_app.post('/api/v1/player/pause')
async def pause(request):
cmd = Cmd('pause')
app = get_app()
rv = PlayerHandler(app).handle(cmd)
return resp(serialize('python', rv, brief=False))


@sanic_app.post('/api/v1/player/resume')
async def resume(request):
cmd = Cmd('resume')
app = get_app()
rv = PlayerHandler(app).handle(cmd)
return resp(serialize('python', rv, brief=False))


@sanic_app.post('/api/v1/player/play')
async def play(request):
js = request.json
cmd = Cmd('play', js['uri'])
app = get_app()
rv = PlayerHandler(app).handle(cmd)
return resp(serialize('python', rv, brief=False))


@sanic_app.websocket('/signal/v1')
async def signal(request, ws: Websocket):
# TODO: 优化这个代码,比如处理连接的关闭。
queue = asyncio.Queue()

class Subscriber:

def write_topic_msg(self, topic, msg):
# TODO: 这个结构体可能会变化,需要注意一下
queue.put_nowait(json.dumps({'topic': topic, 'data': msg, 'format': 'json'}))

subscriber = Subscriber()

pubsub_gateway: PubsubGateway = get_app().pubsub_gateway
for topic in pubsub_gateway.topics:
pubsub_gateway.link(topic, subscriber)

while True:
data = await queue.get()
await ws.send(data)


async def run_web_server(host, port):
server = await sanic_app.create_server(host, port, return_asyncio_server=True)
await server.startup()
await server.serve_forever()
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
'pyshortcuts',
'aionowplaying>=0.9.4',
],
'webserver': ['sanic', 'websockets'],
'webengine': ['PyQtWebEngine'],
# Load cookies from chrome/firefox/...
'cookies': [
Expand Down

0 comments on commit d755c88

Please sign in to comment.