feat: 新增播放上一首歌曲功能 #90

This commit is contained in:
涵曦 2024-09-10 16:13:44 +00:00
parent d7385405d9
commit d71f99de53
3 changed files with 89 additions and 64 deletions

View File

@ -16,6 +16,7 @@ def default_key_word_dict():
"播放本地歌曲": "playlocal", "播放本地歌曲": "playlocal",
"关机": "stop", "关机": "stop",
"下一首": "play_next", "下一首": "play_next",
"上一首": "play_prev",
"单曲循环": "set_play_type_one", "单曲循环": "set_play_type_one",
"全部循环": "set_play_type_all", "全部循环": "set_play_type_all",
"随机播放": "set_random_play", "随机播放": "set_random_play",
@ -46,6 +47,7 @@ def default_key_match_order():
"分钟后关机", "分钟后关机",
"播放歌曲", "播放歌曲",
"下一首", "下一首",
"上一首",
"单曲循环", "单曲循环",
"全部循环", "全部循环",
"随机播放", "随机播放",

View File

@ -1,6 +1,9 @@
$(function(){ $(function(){
$container=$("#cmds"); $container=$("#cmds");
append_op_button_name("加入收藏");
append_op_button_name("取消收藏");
const PLAY_TYPE_ONE = 0; // 单曲循环 const PLAY_TYPE_ONE = 0; // 单曲循环
const PLAY_TYPE_ALL = 1; // 全部循环 const PLAY_TYPE_ALL = 1; // 全部循环
const PLAY_TYPE_RND = 2; // 随机播放 const PLAY_TYPE_RND = 2; // 随机播放
@ -8,12 +11,11 @@ $(function(){
append_op_button("play_type_one", "单曲循环", "单曲循环"); append_op_button("play_type_one", "单曲循环", "单曲循环");
append_op_button("play_type_rnd", "随机播放", "随机播放"); append_op_button("play_type_rnd", "随机播放", "随机播放");
append_op_button_name("刷新列表"); append_op_button_name("上一首");
append_op_button_name("下一首");
append_op_button_name("关机"); append_op_button_name("关机");
append_op_button_name("下一首");
append_op_button_name("加入收藏"); append_op_button_name("刷新列表");
append_op_button_name("取消收藏");
$container.append($("<hr>")); $container.append($("<hr>"));
@ -100,8 +102,7 @@ $(function(){
const selectedValue = $(this).val(); const selectedValue = $(this).val();
localStorage.setItem('cur_playlist', selectedValue); localStorage.setItem('cur_playlist', selectedValue);
$('#music_name').empty(); $('#music_name').empty();
const sorted_musics = data[selectedValue].sort(custom_sort_key); $.each(data[selectedValue], function(index, item) {
$.each(sorted_musics, function(index, item) {
$('#music_name').append($('<option></option>').val(item).text(item)); $('#music_name').append($('<option></option>').val(item).text(item));
}); });
}); });
@ -273,23 +274,4 @@ $(function(){
} }
}); });
} }
function custom_sort_key(a, b) {
// 使用正则表达式提取数字前缀
const numericPrefixA = a.match(/^(\d+)/) ? parseInt(a.match(/^(\d+)/)[1], 10) : null;
const numericPrefixB = b.match(/^(\d+)/) ? parseInt(b.match(/^(\d+)/)[1], 10) : null;
// 如果两个键都有数字前缀,则按数字大小排序
if (numericPrefixA !== null && numericPrefixB !== null) {
return numericPrefixA - numericPrefixB;
}
// 如果一个键有数字前缀而另一个没有,则有数字前缀的键排在前面
if (numericPrefixA !== null) return -1;
if (numericPrefixB !== null) return 1;
// 如果两个键都没有数字前缀,则按照常规字符串排序
return a.localeCompare(b);
}
}); });

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import copy
import json import json
import logging import logging
import math import math
@ -721,6 +722,9 @@ class XiaoMusic:
async def play_next(self, did="", **kwargs): async def play_next(self, did="", **kwargs):
return await self.devices[did].play_next() return await self.devices[did].play_next()
async def play_prev(self, did="", **kwargs):
return await self.devices[did].play_prev()
# 停止 # 停止
async def stop(self, did="", arg1="", **kwargs): async def stop(self, did="", arg1="", **kwargs):
return await self.devices[did].stop(arg1=arg1) return await self.devices[did].stop(arg1=arg1)
@ -797,7 +801,7 @@ class XiaoMusic:
# 正在播放中的音乐 # 正在播放中的音乐
def playingmusic(self, did): def playingmusic(self, did):
cur_music = self.devices[did].cur_music cur_music = self.devices[did].get_cur_music()
self.log.debug(f"playingmusic. cur_music:{cur_music}") self.log.debug(f"playingmusic. cur_music:{cur_music}")
return cur_music return cur_music
@ -841,7 +845,6 @@ class XiaoMusic:
def save_cur_config(self): def save_cur_config(self):
for did in self.config.devices.keys(): for did in self.config.devices.keys():
deviceobj = self.devices.get(did) deviceobj = self.devices.get(did)
self.log.info(deviceobj.device)
if deviceobj is not None: if deviceobj is not None:
self.config.devices[did] = deviceobj.device self.config.devices[did] = deviceobj.device
data = asdict(self.config) data = asdict(self.config)
@ -917,7 +920,6 @@ class XiaoMusicDevice:
self.ffmpeg_location = self.config.ffmpeg_location self.ffmpeg_location = self.config.ffmpeg_location
self._download_proc = None # 下载对象 self._download_proc = None # 下载对象
self.cur_music = self.device.cur_music
self._next_timer = None self._next_timer = None
self._timeout = 0 self._timeout = 0
self._playing = False self._playing = False
@ -926,12 +928,21 @@ class XiaoMusicDevice:
self._last_cmd = None self._last_cmd = None
self.update_playlist() self.update_playlist()
def get_cur_music(self):
return self.device.cur_music
# 初始化播放列表 # 初始化播放列表
def update_playlist(self): def update_playlist(self):
self._cur_play_list = self.device.cur_playlist if self.device.cur_playlist not in self.xiaomusic.music_list:
if self._cur_play_list not in self.xiaomusic.music_list: self.device.cur_playlist = "全部"
self._cur_play_list = "全部"
self._play_list = self.xiaomusic.music_list.get(self._cur_play_list) list_name = self.device.cur_playlist
self._play_list = copy.copy(self.xiaomusic.music_list[list_name])
if self.device.play_type == PLAY_TYPE_RND:
random.shuffle(self._play_list)
self.log.info(f"随机打乱 {list_name} {self._play_list}")
else:
self.log.info(f"没打乱 {list_name} {self._play_list}")
# 播放歌曲 # 播放歌曲
async def play(self, name="", search_key=""): async def play(self, name="", search_key=""):
@ -944,7 +955,7 @@ class XiaoMusicDevice:
await self._play_next() await self._play_next()
return return
else: else:
name = self.cur_music name = self.get_cur_music()
self.log.info(f"play. search_key:{search_key} name:{name}") self.log.info(f"play. search_key:{search_key} name:{name}")
# 本地歌曲不存在时下载 # 本地歌曲不存在时下载
@ -966,7 +977,7 @@ class XiaoMusicDevice:
async def _play_next(self): async def _play_next(self):
self.log.info("开始播放下一首") self.log.info("开始播放下一首")
name = self.cur_music name = self.get_cur_music()
if ( if (
self.device.play_type == PLAY_TYPE_ALL self.device.play_type == PLAY_TYPE_ALL
or self.device.play_type == PLAY_TYPE_RND or self.device.play_type == PLAY_TYPE_RND
@ -974,7 +985,27 @@ class XiaoMusicDevice:
or (name not in self._play_list) or (name not in self._play_list)
): ):
name = self.get_next_music() name = self.get_next_music()
self.log.info(f"_play_next. name:{name}, cur_music:{self.cur_music}") self.log.info(f"_play_next. name:{name}, cur_music:{self.get_cur_music()}")
if name == "":
await self.do_tts("本地没有歌曲")
return
await self._play(name)
# 上一首
async def play_prev(self):
return await self._play_prev()
async def _play_prev(self):
self.log.info("开始播放上一首")
name = self.get_cur_music()
if (
self.device.play_type == PLAY_TYPE_ALL
or self.device.play_type == PLAY_TYPE_RND
or name == ""
or (name not in self._play_list)
):
name = self.get_prev_music()
self.log.info(f"_play_prev. name:{name}, cur_music:{self.get_cur_music()}")
if name == "": if name == "":
await self.do_tts("本地没有歌曲") await self.do_tts("本地没有歌曲")
return return
@ -988,7 +1019,7 @@ class XiaoMusicDevice:
await self._play_next() await self._play_next()
return return
else: else:
name = self.cur_music name = self.get_cur_music()
self.log.info(f"playlocal. name:{name}") self.log.info(f"playlocal. name:{name}")
@ -1004,10 +1035,9 @@ class XiaoMusicDevice:
self.cancel_group_next_timer() self.cancel_group_next_timer()
self._playing = True self._playing = True
self.cur_music = name
self.device.cur_music = name self.device.cur_music = name
self.log.info(f"cur_music {self.cur_music}") self.log.info(f"cur_music {self.get_cur_music()}")
sec, url = await self.xiaomusic.get_music_sec_url(name) sec, url = await self.xiaomusic.get_music_sec_url(name)
await self.group_force_stop_xiaoai() await self.group_force_stop_xiaoai()
self.log.info(f"播放 {url}") self.log.info(f"播放 {url}")
@ -1041,7 +1071,7 @@ class XiaoMusicDevice:
# 最大等8秒 # 最大等8秒
sec = min(8, int(len(value) / 3)) sec = min(8, int(len(value) / 3))
await asyncio.sleep(sec) await asyncio.sleep(sec)
self.log.info(f"do_tts ok. cur_music:{self.cur_music}") self.log.info(f"do_tts ok. cur_music:{self.get_cur_music()}")
await self.check_replay() await self.check_replay()
async def force_stop_xiaoai(self, device_id): async def force_stop_xiaoai(self, device_id):
@ -1143,51 +1173,62 @@ class XiaoMusicDevice:
self.log.info(f"add_download_music add_music {name}") self.log.info(f"add_download_music add_music {name}")
self.log.debug(self._play_list) self.log.debug(self._play_list)
# 获取下一首 def get_music(self, direction="next"):
def get_next_music(self):
play_list_len = len(self._play_list) play_list_len = len(self._play_list)
if play_list_len == 0: if play_list_len == 0:
self.log.warning("当前播放列表没有歌曲") self.log.warning("当前播放列表没有歌曲")
return "" return ""
index = 0 index = 0
try: try:
index = self._play_list.index(self.cur_music) index = self._play_list.index(self.get_cur_music())
except ValueError: except ValueError:
pass pass
if play_list_len == 1: if play_list_len == 1:
next_index = index # 当只有一首歌曲时保持当前索引不变 new_index = index # 当只有一首歌曲时保持当前索引不变
else: else:
# 顺序往后找1个 if direction == "next":
next_index = index + 1 new_index = index + 1
if next_index >= play_list_len: if new_index >= play_list_len:
next_index = 0 new_index = 0
# 排除当前歌曲随机找1个 elif direction == "prev":
if self.device.play_type == PLAY_TYPE_RND: new_index = index - 1
indices = list(range(play_list_len)) if new_index < 0:
indices.remove(index) new_index = play_list_len - 1
next_index = random.choice(indices) else:
name = self._play_list[next_index] self.log.error("无效的方向参数")
return ""
name = self._play_list[new_index]
if not self.xiaomusic.is_music_exist(name): if not self.xiaomusic.is_music_exist(name):
self._play_list.pop(next_index) self._play_list.pop(new_index)
self.log.info(f"pop not exist music:{name}") self.log.info(f"pop not exist music: {name}")
return self.get_next_music() return self.get_music(direction)
return name return name
# 获取下一首
def get_next_music(self):
return self.get_music(direction="next")
# 获取上一首
def get_prev_music(self):
return self.get_music(direction="prev")
# 判断是否播放下一首歌曲 # 判断是否播放下一首歌曲
def check_play_next(self): def check_play_next(self):
# 当前歌曲不在当前播放列表 # 当前歌曲不在当前播放列表
if self.cur_music not in self._play_list: if self.get_cur_music() not in self._play_list:
self.log.info(f"当前歌曲 {self.cur_music} 不在当前播放列表") self.log.info(f"当前歌曲 {self.get_cur_music()} 不在当前播放列表")
return True return True
# 当前没我在播放的歌曲 # 当前没我在播放的歌曲
if self.cur_music == "": if self.get_cur_music() == "":
self.log.info("当前没我在播放的歌曲") self.log.info("当前没我在播放的歌曲")
return True return True
else: else:
# 当前播放的歌曲不存在了 # 当前播放的歌曲不存在了
if not self.xiaomusic.is_music_exist(self.cur_music): if not self.xiaomusic.is_music_exist(self.get_cur_music()):
self.log.info(f"当前播放的歌曲 {self.cur_music} 不存在了") self.log.info(f"当前播放的歌曲 {self.get_cur_music()} 不存在了")
return True return True
return False return False
@ -1289,12 +1330,12 @@ class XiaoMusicDevice:
self.xiaomusic.save_cur_config() self.xiaomusic.save_cur_config()
tts = PLAY_TYPE_TTS[play_type] tts = PLAY_TYPE_TTS[play_type]
await self.do_tts(tts) await self.do_tts(tts)
self.update_playlist()
async def play_music_list(self, list_name, music_name): async def play_music_list(self, list_name, music_name):
self._last_cmd = "play_music_list" self._last_cmd = "play_music_list"
self._cur_play_list = list_name
self.device.cur_playlist = list_name self.device.cur_playlist = list_name
self._play_list = self.xiaomusic.music_list[list_name] self.update_playlist()
self.log.info(f"开始播放列表{list_name}") self.log.info(f"开始播放列表{list_name}")
await self._play(music_name) await self._play(music_name)
@ -1344,7 +1385,7 @@ class XiaoMusicDevice:
device.cancel_next_timer() device.cancel_next_timer()
def get_cur_play_list(self): def get_cur_play_list(self):
return self._cur_play_list return self.device.cur_playlist
# 清空所有定时器 # 清空所有定时器
def cancel_all_timer(self): def cancel_all_timer(self):