Skip to content

Commit

Permalink
feat: 新增播放上一首歌曲功能 #90
Browse files Browse the repository at this point in the history
  • Loading branch information
hanxi committed Sep 10, 2024
1 parent d738540 commit d71f99d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 64 deletions.
2 changes: 2 additions & 0 deletions xiaomusic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def default_key_word_dict():
"播放本地歌曲": "playlocal",
"关机": "stop",
"下一首": "play_next",
"上一首": "play_prev",
"单曲循环": "set_play_type_one",
"全部循环": "set_play_type_all",
"随机播放": "set_random_play",
Expand Down Expand Up @@ -46,6 +47,7 @@ def default_key_match_order():
"分钟后关机",
"播放歌曲",
"下一首",
"上一首",
"单曲循环",
"全部循环",
"随机播放",
Expand Down
32 changes: 7 additions & 25 deletions xiaomusic/static/app.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
$(function(){
$container=$("#cmds");

append_op_button_name("加入收藏");
append_op_button_name("取消收藏");

const PLAY_TYPE_ONE = 0; // 单曲循环
const PLAY_TYPE_ALL = 1; // 全部循环
const PLAY_TYPE_RND = 2; // 随机播放
append_op_button("play_type_all", "全部循环", "全部循环");
append_op_button("play_type_one", "单曲循环", "单曲循环");
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("刷新列表");

$container.append($("<hr>"));

Expand Down Expand Up @@ -100,8 +102,7 @@ $(function(){
const selectedValue = $(this).val();
localStorage.setItem('cur_playlist', selectedValue);
$('#music_name').empty();
const sorted_musics = data[selectedValue].sort(custom_sort_key);
$.each(sorted_musics, function(index, item) {
$.each(data[selectedValue], function(index, item) {
$('#music_name').append($('<option></option>').val(item).text(item));
});
});
Expand Down Expand Up @@ -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);
}

});
119 changes: 80 additions & 39 deletions xiaomusic/xiaomusic.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import asyncio
import copy
import json
import logging
import math
Expand Down Expand Up @@ -721,6 +722,9 @@ async def playlocal(self, did="", arg1="", **kwargs):
async def play_next(self, did="", **kwargs):
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):
return await self.devices[did].stop(arg1=arg1)
Expand Down Expand Up @@ -797,7 +801,7 @@ def get_cur_play_list(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}")
return cur_music

Expand Down Expand Up @@ -841,7 +845,6 @@ def do_saveconfig(self, data):
def save_cur_config(self):
for did in self.config.devices.keys():
deviceobj = self.devices.get(did)
self.log.info(deviceobj.device)
if deviceobj is not None:
self.config.devices[did] = deviceobj.device
data = asdict(self.config)
Expand Down Expand Up @@ -917,7 +920,6 @@ def __init__(self, xiaomusic: XiaoMusic, device: Device, group_name: str):
self.ffmpeg_location = self.config.ffmpeg_location

self._download_proc = None # 下载对象
self.cur_music = self.device.cur_music
self._next_timer = None
self._timeout = 0
self._playing = False
Expand All @@ -926,12 +928,21 @@ def __init__(self, xiaomusic: XiaoMusic, device: Device, group_name: str):
self._last_cmd = None
self.update_playlist()

def get_cur_music(self):
return self.device.cur_music

# 初始化播放列表
def update_playlist(self):
self._cur_play_list = self.device.cur_playlist
if self._cur_play_list not in self.xiaomusic.music_list:
self._cur_play_list = "全部"
self._play_list = self.xiaomusic.music_list.get(self._cur_play_list)
if self.device.cur_playlist not in self.xiaomusic.music_list:
self.device.cur_playlist = "全部"

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=""):
Expand All @@ -944,7 +955,7 @@ async def _play(self, name="", search_key=""):
await self._play_next()
return
else:
name = self.cur_music
name = self.get_cur_music()
self.log.info(f"play. search_key:{search_key} name:{name}")

# 本地歌曲不存在时下载
Expand All @@ -966,15 +977,35 @@ async def play_next(self):

async def _play_next(self):
self.log.info("开始播放下一首")
name = self.cur_music
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_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 == "":
await self.do_tts("本地没有歌曲")
return
Expand All @@ -988,7 +1019,7 @@ async def playlocal(self, name):
await self._play_next()
return
else:
name = self.cur_music
name = self.get_cur_music()

self.log.info(f"playlocal. name:{name}")

Expand All @@ -1004,10 +1035,9 @@ async def _playmusic(self, name):
self.cancel_group_next_timer()

self._playing = True
self.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)
await self.group_force_stop_xiaoai()
self.log.info(f"播放 {url}")
Expand Down Expand Up @@ -1041,7 +1071,7 @@ async def do_tts(self, value):
# 最大等8秒
sec = min(8, int(len(value) / 3))
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()

async def force_stop_xiaoai(self, device_id):
Expand Down Expand Up @@ -1143,51 +1173,62 @@ def add_download_music(self, name):
self.log.info(f"add_download_music add_music {name}")
self.log.debug(self._play_list)

# 获取下一首
def get_next_music(self):
def get_music(self, direction="next"):
play_list_len = len(self._play_list)
if play_list_len == 0:
self.log.warning("当前播放列表没有歌曲")
return ""
index = 0
try:
index = self._play_list.index(self.cur_music)
index = self._play_list.index(self.get_cur_music())
except ValueError:
pass

if play_list_len == 1:
next_index = index # 当只有一首歌曲时保持当前索引不变
new_index = index # 当只有一首歌曲时保持当前索引不变
else:
# 顺序往后找1个
next_index = index + 1
if next_index >= play_list_len:
next_index = 0
# 排除当前歌曲随机找1个
if self.device.play_type == PLAY_TYPE_RND:
indices = list(range(play_list_len))
indices.remove(index)
next_index = random.choice(indices)
name = self._play_list[next_index]
if direction == "next":
new_index = index + 1
if new_index >= play_list_len:
new_index = 0
elif direction == "prev":
new_index = index - 1
if new_index < 0:
new_index = play_list_len - 1
else:
self.log.error("无效的方向参数")
return ""

name = self._play_list[new_index]
if not self.xiaomusic.is_music_exist(name):
self._play_list.pop(next_index)
self.log.info(f"pop not exist music:{name}")
return self.get_next_music()
self._play_list.pop(new_index)
self.log.info(f"pop not exist music: {name}")
return self.get_music(direction)
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):
# 当前歌曲不在当前播放列表
if self.cur_music not in self._play_list:
self.log.info(f"当前歌曲 {self.cur_music} 不在当前播放列表")
if self.get_cur_music() not in self._play_list:
self.log.info(f"当前歌曲 {self.get_cur_music()} 不在当前播放列表")
return True

# 当前没我在播放的歌曲
if self.cur_music == "":
if self.get_cur_music() == "":
self.log.info("当前没我在播放的歌曲")
return True
else:
# 当前播放的歌曲不存在了
if not self.xiaomusic.is_music_exist(self.cur_music):
self.log.info(f"当前播放的歌曲 {self.cur_music} 不存在了")
if not self.xiaomusic.is_music_exist(self.get_cur_music()):
self.log.info(f"当前播放的歌曲 {self.get_cur_music()} 不存在了")
return True
return False

Expand Down Expand Up @@ -1289,12 +1330,12 @@ async def set_play_type(self, play_type):
self.xiaomusic.save_cur_config()
tts = PLAY_TYPE_TTS[play_type]
await self.do_tts(tts)
self.update_playlist()

async def play_music_list(self, list_name, music_name):
self._last_cmd = "play_music_list"
self._cur_play_list = 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}")
await self._play(music_name)

Expand Down Expand Up @@ -1344,7 +1385,7 @@ def cancel_group_next_timer(self):
device.cancel_next_timer()

def get_cur_play_list(self):
return self._cur_play_list
return self.device.cur_playlist

# 清空所有定时器
def cancel_all_timer(self):
Expand Down

0 comments on commit d71f99d

Please sign in to comment.