树莓派或其他开发板播放音乐 提供REST形式的API

需求描述

我在树莓派4b上开发一个工具,需要提供http接口。
1. 添加播放列表并播放。
接口接受参数如下:

{"mode":"local","path":["/home/nicolas/Music/demo1.mp3","/home/nicolas/Music/demo2.mp3"],"action":"tolist"}


表示播放本地的音频文件,并将本地的2个文件添加到到播放列表,顺序播放,播放完成后停止。或者接受以下参数:

{"mode":"remote","path":["http://minio.file.thewebpath.com/share/music/demo.mp3"],"action":"play"}


表示播放远程音频文件(可以使用缓存的形式存储到本地)然后加入到播放列表,并直接开始播放。
2. 查询当前播放列表,返回的内容类似以下:

[{"mode":"local","path":"filepath.mp3","sortting":1},{"mode":"remote","path":"filepath2.mp3","sortting":2}]


3. 默认情况下"action":"play",也就是说如果我并没有传递action键时,默认的操作就是将我指定的文件添加到到播放列表,顺序播放,播放完成后停止。
4. 当我传递"action":"tolist"时,并不影响当前的播放情况,直接将我传递的文件加入到播放列表的末尾。
5. 当我传递"action":"pause"时,忽略其他参数,只将播放暂停。
6。 当我传递"action":"stop"时,播放停止,并清空播放队列,清空临时缓存的文件。
用python编写实现,播放音频可以使用playsound,rest接口用FastAPI实现。

这样子,我就可以通过远程发起rest请求,播放指定音乐文件了。

完整的代码如下:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import os
import requests
from playsound import playsound
from threading import Thread, Event
import time
from urllib.parse import urlparse

app = FastAPI()

# 全局播放列表
playlist = []
# 控制播放的事件
play_event = Event()
# 当前播放的线程
play_thread = None

class AudioRequest(BaseModel):
    mode: str
    path: list
    action: str = "play"  # 默认值为"play"

@app.post("/play")
async def play_audio(request: AudioRequest):
    global playlist, play_thread, play_event

    # 如果有播放线程,则停止播放并清理
    if play_thread and play_thread.is_alive():
        play_event.set()
        play_thread.join()

    if request.action == "tolist":
        for idx, file_path in enumerate(request.path):
            if not os.path.exists(file_path):
                raise HTTPException(status_code=404, detail=f"File {file_path} not found")
            playlist.append({"mode": request.mode, "path": file_path, "sortting": idx + 1})
        return {"status": "success", "message": "Files added to playlist"}

    elif request.action == "play" or not request.action:
        for file_path in request.path:
            if request.mode == "remote":
                file_path = cache_remote_file(file_path)
            playlist.append({"mode": request.mode, "path": file_path, "sortting": len(playlist) + 1})

        # 启动播放线程
        play_event.clear()
        play_thread = Thread(target=play_playlist)
        play_thread.start()

        return {"status": "success", "message": "Playing audio"}

    elif request.action == "pause":
        play_event.set()
        return {"status": "success", "message": "Playback paused"}

    elif request.action == "stop":
        play_event.set()
        if play_thread and play_thread.is_alive():
            play_thread.join()
        clear_playlist()
        return {"status": "success", "message": "Playback stopped and playlist cleared"}

    else:
        raise HTTPException(status_code=400, detail="Invalid action")

@app.get("/playlist")
async def get_playlist():
    return playlist

def cache_remote_file(remote_path: str) -> str:
    local_dir = "/tmp/"
    parsed_url = urlparse(remote_path)
    file_extension = os.path.splitext(parsed_url.path)[1]
    timestamp = int(time.time())
    local_path = os.path.join(local_dir, f"{timestamp}{file_extension}")
    
    if not os.path.exists(local_path):
        response = requests.get(remote_path)
        with open(local_path, "wb") as file:
            file.write(response.content)
    return local_path

def play_playlist():
    global playlist
    while playlist and not play_event.is_set():
        item = playlist.pop(0)
        file_path = item["path"]
        playsound(file_path)
        # 清理播放完成的文件
        if os.path.exists(file_path):
            os.remove(file_path)
        # 清理缓存文件
        if item["mode"] == "remote" and file_path.startswith("/tmp/"):
            os.remove(file_path)
    # 清理播放完成后的播放列表
    if play_event.is_set():
        clear_playlist()

def clear_playlist():
    global playlist
    playlist = []

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值