feat: 支持多设备分开播放 see #65
This commit is contained in:
parent
aa698667c9
commit
043a9303a5
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -2,7 +2,8 @@ name: ci
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches:
|
||||||
|
- "*"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"hardware": "L07A",
|
|
||||||
"account": "",
|
"account": "",
|
||||||
"password": "",
|
"password": "",
|
||||||
"mi_did": "",
|
"mi_did": "",
|
||||||
"cookie": "",
|
"cookie": "",
|
||||||
"verbose": false,
|
"verbose": false,
|
||||||
"music_path": "music",
|
"music_path": "music",
|
||||||
|
"download_path": "",
|
||||||
"conf_path": null,
|
"conf_path": null,
|
||||||
"hostname": "192.168.2.5",
|
"hostname": "192.168.2.5",
|
||||||
"port": 8090,
|
"port": 8090,
|
||||||
@ -13,12 +13,12 @@
|
|||||||
"proxy": null,
|
"proxy": null,
|
||||||
"search_prefix": "bilisearch:",
|
"search_prefix": "bilisearch:",
|
||||||
"ffmpeg_location": "./ffmpeg/bin",
|
"ffmpeg_location": "./ffmpeg/bin",
|
||||||
"active_cmd": "play,random_play,playlocal,play_music_list,stop",
|
"active_cmd": "play,set_random_play,playlocal,play_music_list,stop",
|
||||||
"exclude_dirs": "@eaDir",
|
"exclude_dirs": "@eaDir",
|
||||||
"music_path_depth": 10,
|
"music_path_depth": 10,
|
||||||
"disable_httpauth": true,
|
"disable_httpauth": true,
|
||||||
"httpauth_username": "admin",
|
"httpauth_username": "",
|
||||||
"httpauth_password": "admin",
|
"httpauth_password": "",
|
||||||
"music_list_url": "",
|
"music_list_url": "",
|
||||||
"music_list_json": "",
|
"music_list_json": "",
|
||||||
"disable_download": false,
|
"disable_download": false,
|
||||||
@ -29,12 +29,10 @@
|
|||||||
"下一首": "play_next",
|
"下一首": "play_next",
|
||||||
"单曲循环": "set_play_type_one",
|
"单曲循环": "set_play_type_one",
|
||||||
"全部循环": "set_play_type_all",
|
"全部循环": "set_play_type_all",
|
||||||
"随机播放": "random_play",
|
"随机播放": "set_random_play",
|
||||||
"分钟后关机": "stop_after_minute",
|
"分钟后关机": "stop_after_minute",
|
||||||
"播放列表": "play_music_list",
|
"播放列表": "play_music_list",
|
||||||
"刷新列表": "gen_music_list",
|
"刷新列表": "gen_music_list",
|
||||||
"set_volume#": "set_volume",
|
|
||||||
"get_volume#": "get_volume",
|
|
||||||
"本地播放歌曲": "playlocal",
|
"本地播放歌曲": "playlocal",
|
||||||
"放歌曲": "play",
|
"放歌曲": "play",
|
||||||
"暂停": "stop",
|
"暂停": "stop",
|
||||||
@ -44,8 +42,6 @@
|
|||||||
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
|
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
|
||||||
},
|
},
|
||||||
"key_match_order": [
|
"key_match_order": [
|
||||||
"set_volume#",
|
|
||||||
"get_volume#",
|
|
||||||
"分钟后关机",
|
"分钟后关机",
|
||||||
"播放歌曲",
|
"播放歌曲",
|
||||||
"下一首",
|
"下一首",
|
||||||
@ -71,11 +67,15 @@
|
|||||||
"fuzzy_match_cutoff": 0.6,
|
"fuzzy_match_cutoff": 0.6,
|
||||||
"enable_fuzzy_match": true,
|
"enable_fuzzy_match": true,
|
||||||
"stop_tts_msg": "收到,再见",
|
"stop_tts_msg": "收到,再见",
|
||||||
|
"enable_config_example": true,
|
||||||
"keywords_playlocal": "播放本地歌曲,本地播放歌曲",
|
"keywords_playlocal": "播放本地歌曲,本地播放歌曲",
|
||||||
"keywords_play": "播放歌曲,放歌曲",
|
"keywords_play": "播放歌曲,放歌曲",
|
||||||
"keywords_stop": "关机,暂停,停止,停止播放",
|
"keywords_stop": "关机,暂停,停止,停止播放",
|
||||||
"user_key_word_dict": {
|
"user_key_word_dict": {
|
||||||
"测试自定义口令": "exec#code1(\"hello\")",
|
"测试自定义口令": "exec#code1(\"hello\")",
|
||||||
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
|
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
|
||||||
}
|
},
|
||||||
|
"enable_force_stop": false,
|
||||||
|
"devices": {},
|
||||||
|
"group_list": ""
|
||||||
}
|
}
|
@ -18,12 +18,10 @@ def default_key_word_dict():
|
|||||||
"下一首": "play_next",
|
"下一首": "play_next",
|
||||||
"单曲循环": "set_play_type_one",
|
"单曲循环": "set_play_type_one",
|
||||||
"全部循环": "set_play_type_all",
|
"全部循环": "set_play_type_all",
|
||||||
"随机播放": "random_play",
|
"随机播放": "set_random_play",
|
||||||
"分钟后关机": "stop_after_minute",
|
"分钟后关机": "stop_after_minute",
|
||||||
"播放列表": "play_music_list",
|
"播放列表": "play_music_list",
|
||||||
"刷新列表": "gen_music_list",
|
"刷新列表": "gen_music_list",
|
||||||
"set_volume#": "set_volume",
|
|
||||||
"get_volume#": "get_volume",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,8 +41,6 @@ KEY_WORD_ARG_BEFORE_DICT = {
|
|||||||
# 口令匹配优先级
|
# 口令匹配优先级
|
||||||
def default_key_match_order():
|
def default_key_match_order():
|
||||||
return [
|
return [
|
||||||
"set_volume#",
|
|
||||||
"get_volume#",
|
|
||||||
"分钟后关机",
|
"分钟后关机",
|
||||||
"播放歌曲",
|
"播放歌曲",
|
||||||
"下一首",
|
"下一首",
|
||||||
@ -57,12 +53,22 @@ def default_key_match_order():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Device:
|
||||||
|
did: str = ""
|
||||||
|
device_id: str = ""
|
||||||
|
hardware: str = ""
|
||||||
|
name: str = ""
|
||||||
|
play_type: int = ""
|
||||||
|
cur_music: str = ""
|
||||||
|
cur_playlist: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Config:
|
class Config:
|
||||||
account: str = os.getenv("MI_USER", "")
|
account: str = os.getenv("MI_USER", "")
|
||||||
password: str = os.getenv("MI_PASS", "")
|
password: str = os.getenv("MI_PASS", "")
|
||||||
mi_did: str = os.getenv("MI_DID", "") # 逗号分割支持多设备
|
mi_did: str = os.getenv("MI_DID", "") # 逗号分割支持多设备
|
||||||
hardware: str = os.getenv("MI_HARDWARE", "L07A") # 逗号分割支持多设备
|
|
||||||
cookie: str = ""
|
cookie: str = ""
|
||||||
verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true"
|
verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true"
|
||||||
music_path: str = os.getenv(
|
music_path: str = os.getenv(
|
||||||
@ -79,7 +85,7 @@ class Config:
|
|||||||
) # "bilisearch:" or "ytsearch:"
|
) # "bilisearch:" or "ytsearch:"
|
||||||
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin")
|
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin")
|
||||||
active_cmd: str = os.getenv(
|
active_cmd: str = os.getenv(
|
||||||
"XIAOMUSIC_ACTIVE_CMD", "play,random_play,playlocal,play_music_list,stop"
|
"XIAOMUSIC_ACTIVE_CMD", "play,set_random_play,playlocal,play_music_list,stop"
|
||||||
)
|
)
|
||||||
exclude_dirs: str = os.getenv("XIAOMUSIC_EXCLUDE_DIRS", "@eaDir")
|
exclude_dirs: str = os.getenv("XIAOMUSIC_EXCLUDE_DIRS", "@eaDir")
|
||||||
music_path_depth: int = int(os.getenv("XIAOMUSIC_MUSIC_PATH_DEPTH", "10"))
|
music_path_depth: int = int(os.getenv("XIAOMUSIC_MUSIC_PATH_DEPTH", "10"))
|
||||||
@ -123,7 +129,10 @@ class Config:
|
|||||||
enable_force_stop: bool = (
|
enable_force_stop: bool = (
|
||||||
os.getenv("XIAOMUSIC_ENABLE_FORCE_STOP", "false").lower() == "true"
|
os.getenv("XIAOMUSIC_ENABLE_FORCE_STOP", "false").lower() == "true"
|
||||||
)
|
)
|
||||||
play_type: int = int(os.getenv("XIAOMUSIC_PLAY_TYPE", "2"))
|
devices: dict[str, Device] = field(default_factory=dict)
|
||||||
|
group_list: str = os.getenv(
|
||||||
|
"XIAOMUSIC_GROUP_LIST", ""
|
||||||
|
) # did2:group_name,did2:group_name
|
||||||
|
|
||||||
def append_keyword(self, keys, action):
|
def append_keyword(self, keys, action):
|
||||||
for key in keys.split(","):
|
for key in keys.split(","):
|
||||||
@ -150,7 +159,7 @@ class Config:
|
|||||||
if self.enable_config_example:
|
if self.enable_config_example:
|
||||||
with open("config-example.json", "w") as f:
|
with open("config-example.json", "w") as f:
|
||||||
data = asdict(self)
|
data = asdict(self)
|
||||||
json.dump(data, f, ensure_ascii=False, indent=4)
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_options(cls, options: argparse.Namespace) -> Config:
|
def from_options(cls, options: argparse.Namespace) -> Config:
|
||||||
@ -171,6 +180,10 @@ class Config:
|
|||||||
converted_value = False
|
converted_value = False
|
||||||
if str(v).lower() == "true":
|
if str(v).lower() == "true":
|
||||||
converted_value = True
|
converted_value = True
|
||||||
|
elif expected_type == dict[str, Device]:
|
||||||
|
converted_value = {}
|
||||||
|
for kk, vv in v.items():
|
||||||
|
converted_value[kk] = Device(**vv)
|
||||||
else:
|
else:
|
||||||
converted_value = expected_type(v)
|
converted_value = expected_type(v)
|
||||||
return converted_value
|
return converted_value
|
||||||
@ -192,7 +205,7 @@ class Config:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def update_config(self, data):
|
def update_config(self, data):
|
||||||
type_hints = get_type_hints(self)
|
type_hints = get_type_hints(self, globals(), locals())
|
||||||
|
|
||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
converted_value = self.convert_value(k, v, type_hints)
|
converted_value = self.convert_value(k, v, type_hints)
|
||||||
|
@ -9,3 +9,13 @@ SUPPORT_MUSIC_TYPE = [
|
|||||||
|
|
||||||
LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}×tamp={timestamp}&limit=2"
|
LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}×tamp={timestamp}&limit=2"
|
||||||
COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}"
|
COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}"
|
||||||
|
|
||||||
|
PLAY_TYPE_ONE = 0 # 单曲循环
|
||||||
|
PLAY_TYPE_ALL = 1 # 全部循环
|
||||||
|
PLAY_TYPE_RND = 2 # 随机播放
|
||||||
|
|
||||||
|
PLAY_TYPE_TTS = {
|
||||||
|
PLAY_TYPE_ONE: "已经设置为单曲循环",
|
||||||
|
PLAY_TYPE_ALL: "已经设置为全部循环",
|
||||||
|
PLAY_TYPE_RND: "已经设置为随机播放",
|
||||||
|
}
|
||||||
|
@ -53,11 +53,29 @@ def getversion():
|
|||||||
|
|
||||||
@app.route("/getvolume", methods=["GET"])
|
@app.route("/getvolume", methods=["GET"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def getvolume():
|
async def getvolume():
|
||||||
volume = xiaomusic.get_volume_ret()
|
did = request.args.get("did")
|
||||||
return {
|
if not xiaomusic.did_exist(did):
|
||||||
"volume": volume,
|
return {"volume": 0}
|
||||||
}
|
|
||||||
|
volume = await xiaomusic.call_main_thread_function(xiaomusic.get_volume, did=did)
|
||||||
|
return {"volume": volume}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/setvolume", methods=["POST"])
|
||||||
|
@auth.login_required
|
||||||
|
async def setvolume():
|
||||||
|
data = request.get_json()
|
||||||
|
did = data.get("did")
|
||||||
|
volume = data.get("volume")
|
||||||
|
if not xiaomusic.did_exist(did):
|
||||||
|
return {"ret": "Did not exist"}
|
||||||
|
|
||||||
|
log.info(f"set_volume {did} {volume}")
|
||||||
|
await xiaomusic.call_main_thread_function(
|
||||||
|
xiaomusic.set_volume, did=did, arg1=volume
|
||||||
|
)
|
||||||
|
return {"ret": "OK", "volume": volume}
|
||||||
|
|
||||||
|
|
||||||
@app.route("/searchmusic", methods=["GET"])
|
@app.route("/searchmusic", methods=["GET"])
|
||||||
@ -70,13 +88,19 @@ def searchmusic():
|
|||||||
@app.route("/playingmusic", methods=["GET"])
|
@app.route("/playingmusic", methods=["GET"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def playingmusic():
|
def playingmusic():
|
||||||
return xiaomusic.playingmusic()
|
did = request.args.get("did")
|
||||||
|
if not xiaomusic.did_exist(did):
|
||||||
|
return ""
|
||||||
|
return xiaomusic.playingmusic(did)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/isplaying", methods=["GET"])
|
@app.route("/isplaying", methods=["GET"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def isplaying():
|
def isplaying():
|
||||||
return xiaomusic.isplaying()
|
did = request.args.get("did")
|
||||||
|
if not xiaomusic.did_exist(did):
|
||||||
|
return False
|
||||||
|
return xiaomusic.isplaying(did)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", methods=["GET"])
|
@app.route("/", methods=["GET"])
|
||||||
@ -88,10 +112,14 @@ def index():
|
|||||||
@auth.login_required
|
@auth.login_required
|
||||||
async def do_cmd():
|
async def do_cmd():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
did = data.get("did")
|
||||||
cmd = data.get("cmd")
|
cmd = data.get("cmd")
|
||||||
|
if not xiaomusic.did_exist(did):
|
||||||
|
return {"ret": "Did not exist"}
|
||||||
|
|
||||||
if len(cmd) > 0:
|
if len(cmd) > 0:
|
||||||
log.debug("docmd. cmd:%s", cmd)
|
log.info(f"docmd. did:{did} cmd:{cmd}")
|
||||||
xiaomusic.set_last_record(cmd)
|
xiaomusic.set_last_record(did, cmd)
|
||||||
return {"ret": "OK"}
|
return {"ret": "OK"}
|
||||||
return {"ret": "Unknow cmd"}
|
return {"ret": "Unknow cmd"}
|
||||||
|
|
||||||
@ -101,10 +129,9 @@ async def do_cmd():
|
|||||||
async def getsetting():
|
async def getsetting():
|
||||||
config = xiaomusic.getconfig()
|
config = xiaomusic.getconfig()
|
||||||
data = asdict(config)
|
data = asdict(config)
|
||||||
alldevices = await xiaomusic.call_main_thread_function(xiaomusic.getalldevices)
|
device_list = await xiaomusic.call_main_thread_function(xiaomusic.getalldevices)
|
||||||
log.info(f"getsetting alldevices: {alldevices}")
|
log.info(f"getsetting device_list: {device_list}")
|
||||||
data["mi_did_list"] = alldevices["did_list"]
|
data["device_list"] = device_list
|
||||||
data["mi_hardware_list"] = alldevices["hardware_list"]
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +154,10 @@ async def musiclist():
|
|||||||
@app.route("/curplaylist", methods=["GET"])
|
@app.route("/curplaylist", methods=["GET"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
async def curplaylist():
|
async def curplaylist():
|
||||||
return xiaomusic.get_cur_play_list()
|
did = request.args.get("did")
|
||||||
|
if not xiaomusic.did_exist(did):
|
||||||
|
return ""
|
||||||
|
return xiaomusic.get_cur_play_list(did)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/delmusic", methods=["POST"])
|
@app.route("/delmusic", methods=["POST"])
|
||||||
@ -149,7 +179,7 @@ def downloadjson():
|
|||||||
ret = "OK"
|
ret = "OK"
|
||||||
content = downloadfile(url)
|
content = downloadfile(url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"downloadjson failed. url:{url} e:{e}")
|
log.exception(f"Execption {e}")
|
||||||
ret = "Download JSON file failed."
|
ret = "Download JSON file failed."
|
||||||
return {
|
return {
|
||||||
"ret": ret,
|
"ret": ret,
|
||||||
@ -166,9 +196,15 @@ def downloadlog():
|
|||||||
@app.route("/playurl", methods=["GET"])
|
@app.route("/playurl", methods=["GET"])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
async def playurl():
|
async def playurl():
|
||||||
|
did = request.args.get("did")
|
||||||
url = request.args.get("url")
|
url = request.args.get("url")
|
||||||
log.info(f"play_url:{url}")
|
if not xiaomusic.did_exist(did):
|
||||||
return await xiaomusic.call_main_thread_function(xiaomusic.play_url, arg1=url)
|
return {"ret": "Did not exist"}
|
||||||
|
|
||||||
|
log.info(f"playurl did: {did} url: {url}")
|
||||||
|
return await xiaomusic.call_main_thread_function(
|
||||||
|
xiaomusic.play_url, did=did, arg1=url
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/debug_play_by_music_url", methods=["POST"])
|
@app.route("/debug_play_by_music_url", methods=["POST"])
|
||||||
|
@ -13,12 +13,41 @@ $(function(){
|
|||||||
append_op_button_name("30分钟后关机");
|
append_op_button_name("30分钟后关机");
|
||||||
append_op_button_name("60分钟后关机");
|
append_op_button_name("60分钟后关机");
|
||||||
|
|
||||||
// 拉取声音
|
// 拉取现有配置
|
||||||
sendcmd("get_volume#");
|
$.get("/getsetting", function(data, status) {
|
||||||
$.get("/getvolume", function(data, status) {
|
console.log(data, status);
|
||||||
|
localStorage.setItem('mi_did', data.mi_did);
|
||||||
|
|
||||||
|
var did = localStorage.getItem('cur_did');
|
||||||
|
if ((did == null || did == "") && data.mi_did != null) {
|
||||||
|
var dids = data.mi_did.split(',');
|
||||||
|
did = dids[0];
|
||||||
|
localStorage.setItem('cur_did', did);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.did = did;
|
||||||
|
$.get(`/getvolume?did=${did}`, function(data, status) {
|
||||||
console.log(data, status, data["volume"]);
|
console.log(data, status, data["volume"]);
|
||||||
$("#volume").val(data.volume);
|
$("#volume").val(data.volume);
|
||||||
});
|
});
|
||||||
|
refresh_music_list();
|
||||||
|
|
||||||
|
$("#did").empty();
|
||||||
|
var dids = data.mi_did.split(',');
|
||||||
|
$.each(dids, function(index, value) {
|
||||||
|
var device = data.device_list.find(function(device) {
|
||||||
|
return device.miotDID == value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (device) {
|
||||||
|
var option = $('<option></option>')
|
||||||
|
.val(value)
|
||||||
|
.text(device.name)
|
||||||
|
.prop('selected', value === did);
|
||||||
|
$("#did").append(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 拉取版本
|
// 拉取版本
|
||||||
$.get("/getversion", function(data, status) {
|
$.get("/getversion", function(data, status) {
|
||||||
@ -47,13 +76,20 @@ $(function(){
|
|||||||
$('#music_list').trigger('change');
|
$('#music_list').trigger('change');
|
||||||
|
|
||||||
// 获取当前播放列表
|
// 获取当前播放列表
|
||||||
$.get("curplaylist", function(data, status) {
|
$.get(`curplaylist?did=${did}`, function(data, status) {
|
||||||
|
if (data != "") {
|
||||||
$('#music_list').val(data);
|
$('#music_list').val(data);
|
||||||
$('#music_list').trigger('change');
|
$('#music_list').trigger('change');
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
refresh_music_list();
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 每3秒获取下正在播放的音乐
|
||||||
|
get_playing_music();
|
||||||
|
setInterval(() => {
|
||||||
|
get_playing_music();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
$("#play_music_list").on("click", () => {
|
$("#play_music_list").on("click", () => {
|
||||||
var music_list = $("#music_list").val();
|
var music_list = $("#music_list").val();
|
||||||
@ -84,7 +120,7 @@ $(function(){
|
|||||||
|
|
||||||
$("#playurl").on("click", () => {
|
$("#playurl").on("click", () => {
|
||||||
var url = $("#music-url").val();
|
var url = $("#music-url").val();
|
||||||
$.get(`/playurl?url=${url}`, function(data, status) {
|
$.get(`/playurl?url=${url}&did=${did}`, function(data, status) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -115,9 +151,18 @@ $(function(){
|
|||||||
sendcmd(cmd);
|
sendcmd(cmd);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#volume").on('input', function () {
|
$("#volume").on('change', function () {
|
||||||
var value = $(this).val();
|
var value = $(this).val();
|
||||||
sendcmd("set_volume#"+value);
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/setvolume",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
data: JSON.stringify({did: did, volume: value}),
|
||||||
|
success: () => {
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function sendcmd(cmd) {
|
function sendcmd(cmd) {
|
||||||
@ -125,7 +170,7 @@ $(function(){
|
|||||||
type: "POST",
|
type: "POST",
|
||||||
url: "/cmd",
|
url: "/cmd",
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
data: JSON.stringify({cmd: cmd}),
|
data: JSON.stringify({did: did, cmd: cmd}),
|
||||||
success: () => {
|
success: () => {
|
||||||
if (cmd == "刷新列表") {
|
if (cmd == "刷新列表") {
|
||||||
setTimeout(refresh_music_list, 3000);
|
setTimeout(refresh_music_list, 3000);
|
||||||
@ -160,18 +205,12 @@ $(function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
function get_playing_music() {
|
function get_playing_music() {
|
||||||
$.get("/playingmusic", function(data, status) {
|
$.get(`/playingmusic?did=${did}`, function(data, status) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
$("#playering-music").text(data);
|
$("#playering-music").text(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每3秒获取下正在播放的音乐
|
|
||||||
get_playing_music();
|
|
||||||
setInterval(() => {
|
|
||||||
get_playing_music();
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
function custom_sort_key(a, b) {
|
function custom_sort_key(a, b) {
|
||||||
// 使用正则表达式提取数字前缀
|
// 使用正则表达式提取数字前缀
|
||||||
const numericPrefixA = a.match(/^(\d+)/) ? parseInt(a.match(/^(\d+)/)[1], 10) : null;
|
const numericPrefixA = a.match(/^(\d+)/) ? parseInt(a.match(/^(\d+)/)[1], 10) : null;
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
<script src="/static/jquery-3.7.1.min.js"></script>
|
<script src="/static/jquery-3.7.1.min.js"></script>
|
||||||
<script src="/static/app.js"></script>
|
<script src="/static/app.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var vConsole = new window.VConsole();
|
||||||
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>小爱音箱操控面板
|
<h2>小爱音箱操控面板
|
||||||
@ -14,6 +20,8 @@
|
|||||||
</a>)
|
</a>)
|
||||||
</h2>
|
</h2>
|
||||||
<hr>
|
<hr>
|
||||||
|
<select id="did">
|
||||||
|
</select>
|
||||||
<div id="cmds">
|
<div id="cmds">
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -24,8 +24,8 @@ var vConsole = new window.VConsole();
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="rows">
|
<div class="rows">
|
||||||
<label for="mi_did_hardware">*勾选设备(至少勾选1个):</label>
|
<label for="mi_did">*勾选设备(至少勾选1个):</label>
|
||||||
<div id="mi_did_hardware">
|
<div id="mi_did">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
@ -16,23 +16,22 @@ $(function(){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateCheckbox(selector, mi_did_list, mi_did, mi_hardware_list) {
|
function updateCheckbox(selector, mi_did, device_list) {
|
||||||
// 清除现有的内容
|
// 清除现有的内容
|
||||||
$(selector).empty();
|
$(selector).empty();
|
||||||
|
|
||||||
// 将 mi_did 字符串通过逗号分割转换为数组,以便于判断默认选中项
|
// 将 mi_did 字符串通过逗号分割转换为数组,以便于判断默认选中项
|
||||||
var selected_dids = mi_did.split(',');
|
var selected_dids = mi_did.split(',');
|
||||||
|
|
||||||
// 遍历传入的 mi_did_list 和 mi_hardware_list
|
$.each(device_list, function(index, device) {
|
||||||
$.each(mi_did_list, function(index, did) {
|
var did = device.miotDID;
|
||||||
// 获取硬件标识,假定列表是一一对应的
|
var hardware = device.hardware;
|
||||||
var hardware = mi_hardware_list[index];
|
var name = device.name;
|
||||||
|
|
||||||
// 创建复选框元素
|
// 创建复选框元素
|
||||||
var checkbox = $('<input>', {
|
var checkbox = $('<input>', {
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
id: did,
|
id: did,
|
||||||
value: `${did}|${hardware}`,
|
value: `${did}`,
|
||||||
class: 'custom-checkbox', // 添加样式类
|
class: 'custom-checkbox', // 添加样式类
|
||||||
// 如果mi_did中包含了该did,则默认选中
|
// 如果mi_did中包含了该did,则默认选中
|
||||||
checked: selected_dids.indexOf(did) !== -1
|
checked: selected_dids.indexOf(did) !== -1
|
||||||
@ -42,7 +41,7 @@ $(function(){
|
|||||||
var label = $('<label>', {
|
var label = $('<label>', {
|
||||||
for: did,
|
for: did,
|
||||||
class: 'checkbox-label', // 添加样式类
|
class: 'checkbox-label', // 添加样式类
|
||||||
text: `【${hardware}】 ${did}` // 设定标签内容为did和hardware的拼接
|
text: `【${hardware}】${name}` // 设定标签内容
|
||||||
});
|
});
|
||||||
|
|
||||||
// 将复选框和标签添加到目标选择器元素中
|
// 将复选框和标签添加到目标选择器元素中
|
||||||
@ -50,29 +49,22 @@ $(function(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedDidsAndHardware(containerSelector) {
|
function getSelectedDids(containerSelector) {
|
||||||
var selectedDids = [];
|
var selectedDids = [];
|
||||||
var selectedHardware = [];
|
|
||||||
|
|
||||||
// 仅选择给定容器中选中的复选框
|
// 仅选择给定容器中选中的复选框
|
||||||
$(containerSelector + ' .custom-checkbox:checked').each(function() {
|
$(containerSelector + ' .custom-checkbox:checked').each(function() {
|
||||||
// 解析当前复选框的值(值中包含了 did 和 hardware,使用 '|' 分割)
|
var did = this.value;
|
||||||
var parts = this.value.split('|');
|
selectedDids.push(did);
|
||||||
selectedDids.push(parts[0]);
|
|
||||||
selectedHardware.push(parts[1]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返回包含 did_list 和 hardware_list 的对象
|
return selectedDids.join(',');
|
||||||
return {
|
|
||||||
did_list: selectedDids.join(','),
|
|
||||||
hardware_list: selectedHardware.join(',')
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拉取现有配置
|
// 拉取现有配置
|
||||||
$.get("/getsetting", function(data, status) {
|
$.get("/getsetting", function(data, status) {
|
||||||
console.log(data, status);
|
console.log(data, status);
|
||||||
updateCheckbox("#mi_did_hardware", data.mi_did_list, data.mi_did, data.mi_hardware_list);
|
updateCheckbox("#mi_did", data.mi_did, data.device_list);
|
||||||
|
|
||||||
// 初始化显示
|
// 初始化显示
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
@ -103,9 +95,8 @@ $(function(){
|
|||||||
data[id] = $(this).val();
|
data[id] = $(this).val();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var selectedData = getSelectedDidsAndHardware("#mi_did_hardware");
|
var did_list = getSelectedDids("#mi_did");
|
||||||
data["mi_did"] = selectedData.did_list;
|
data["mi_did"] = did_list;
|
||||||
data["hardware"] = selectedData.hardware_list;
|
|
||||||
console.log(data)
|
console.log(data)
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -253,3 +253,19 @@ def deepcopy_data_no_sensitive_info(data, fields_to_anonymize=None):
|
|||||||
setattr(copy_data, field, "******")
|
setattr(copy_data, field, "******")
|
||||||
|
|
||||||
return copy_data
|
return copy_data
|
||||||
|
|
||||||
|
|
||||||
|
# k1:v1,k2:v2
|
||||||
|
def parse_str_to_dict(s, d1=",", d2=":"):
|
||||||
|
# 初始化一个空字典
|
||||||
|
result = {}
|
||||||
|
parts = s.split(d1)
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
# 根据冒号切割
|
||||||
|
subparts = part.split(d2)
|
||||||
|
if len(subparts) == 2: # 防止数据不是成对出现
|
||||||
|
k, v = subparts
|
||||||
|
result[k] = v
|
||||||
|
|
||||||
|
return result
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user