1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2026-05-16 10:56:46 +08:00

feat: 整理第三方播放设备的代码

This commit is contained in:
涵曦
2025-03-07 09:42:14 +08:00
parent 009a361b6b
commit 7209b03a12
4 changed files with 243 additions and 244 deletions

View File

@@ -151,8 +151,8 @@ async def custom_event(sid, data):
await sio.emit("response", {"action": "切歌", "status": data})
@app.post("/items/")
async def create_item(item: Item):
@app.post("thdaction")
async def thdaction(item: Item):
await sio.emit(
"response",
{"action": item.action, "args": item.args, "status": item.args},

View File

@@ -1,54 +1,54 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PWA必须的配置 -->
<link rel="manifest" href="/manifest.json">
<title>Mini音乐播放器</title>
<style>
/* 新增音量控制样式 */
.volume-control {
position: relative;
align-items: center;
display: flex;
align-items: center;
gap: 10px;
margin-left: 30px;
}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PWA必须的配置 -->
<link rel="manifest" href="/manifest.json">
<title>Mini音乐播放器</title>
<style>
/* 新增音量控制样式 */
.volume-control {
position: relative;
align-items: center;
display: flex;
align-items: center;
gap: 10px;
margin-left: 30px;
}
.btn-volume {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
padding: 0;
}
.btn-volume {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
padding: 0;
}
.volume-slider {
width: 100px;
height: 4px;
-webkit-appearance: none;
background: #ddd;
border-radius: 2px;
outline: none;
opacity: 0.8;
transition: opacity 0.2s;
}
.volume-slider {
width: 100px;
height: 4px;
-webkit-appearance: none;
background: #ddd;
border-radius: 2px;
outline: none;
opacity: 0.8;
transition: opacity 0.2s;
}
.volume-slider:hover {
opacity: 1;
}
.volume-slider:hover {
opacity: 1;
}
/* 自定义滑动条样式 */
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: #ff4d4d;
border-radius: 50%;
cursor: pointer;
}
/* 自定义滑动条样式 */
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
background: #ff4d4d;
border-radius: 50%;
cursor: pointer;
}
body {
background: #f5f5f5;
font-family: Arial, "Microsoft Yahei";
@@ -175,105 +175,105 @@
background: #ffe5e5;
color: #ff4d4d;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
<script> // 连接到服务端
const socket = io("/", { transports: ["websocket"] });
socket.on('connect', () => {
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
<script> // 连接到服务端
const socket = io("/", { transports: ["websocket"] });
socket.on('connect', () => {
console.log('Connected to the server');
socket.emit('my_event', 'Hello, server!');
});
// 接收广播消息
socket.on("response", (data) => { const div = document.createElement("div");
div.textContent = `${data.action}: ${data.status}`;
if( data.action=='play')
{
playlist[0].src=data.args;
playlist[0].artist="小爱在线"
playlist[0].title="小爱在线"
console.log(data.args)
playSong(0) ;
}
if( data.action=='stop')
{
togglePlay()
}
const audio = document.getElementById('audio');
const volumeSlider = document.getElementById('volumeSlider');
if( data.action=='volume')
{
// 接收广播消息
socket.on("response", (data) => { const div = document.createElement("div");
div.textContent = `${data.action}: ${data.status}`;
audio.volume=data.args/100
volumeSlider.value=audio.volume
}
if( data.action=='down')
{
audio.volume=audio.volume-0.2
volumeSlider.value=audio.volume
}
if( data.action=='up')
{
audio.volume=audio.volume+0.2
volumeSlider.value=audio.volume
}
document.getElementById("messages").appendChild(div); });
</script>
if( data.action=='play')
{
playlist[0].src=data.args;
playlist[0].artist="小爱在线"
playlist[0].title="小爱在线"
console.log(data.args)
playSong(0) ;
}
if( data.action=='stop')
{
togglePlay()
}
const audio = document.getElementById('audio');
const volumeSlider = document.getElementById('volumeSlider');
if( data.action=='volume')
{
<div class="player">
<div class="cover">
<img id="cover-image" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.ou-y715Of4TbrAPN-j96sQHaEj?w=309&h=188&c=7&r=0&o=5&pid=1.7" alt="封面">
</div>
<div class="song-info">
<h2 class="song-title" id="song-title">Let Go</h2>
<p class="artist" id="artist">Avril Lavigne</p>
audio.volume=data.args/100
volumeSlider.value=audio.volume
}
if( data.action=='down')
{
audio.volume=audio.volume-0.2
volumeSlider.value=audio.volume
}
if( data.action=='up')
{
audio.volume=audio.volume+0.2
volumeSlider.value=audio.volume
}
document.getElementById("messages").appendChild(div); });
</script>
<div class="player">
<div class="cover">
<img id="cover-image" src="https://tse4-mm.cn.bing.net/th/id/OIP-C.ou-y715Of4TbrAPN-j96sQHaEj?w=309&h=188&c=7&r=0&o=5&pid=1.7" alt="封面">
</div>
<div class="song-info">
<h2 class="song-title" id="song-title">Let Go</h2>
<p class="artist" id="artist">Avril Lavigne</p>
</div>
<div class="progress-container" id="progress-container">
<div class="progress" id="progress"></div>
</div>
<div class="time">
<span id="current-time">00:00</span>
<span id="duration">00:00</span>
</div>
<div class="controls">
<div class="btn btn-prev" onclick="prevSong()"></div>
<div class="btn btn-play" id="playBtn" onclick="togglePlay()"></div>
<div class="btn btn-next" onclick="nextSong()"></div>
</div>
<br>
<!-- 新增音量控制 -->
<div class="volume-control">
<button class="btn-volume" onclick="toggleMute()">🔊</button>
<input type="range"
id="volumeSlider"
class="volume-slider"
min="0"
max="1"
step="0.1"
value="1">
</div>
<ul class="playlist" id="playlist"></ul>
</div>
<div class="progress-container" id="progress-container">
<div class="progress" id="progress"></div>
</div>
<div class="time">
<span id="current-time">00:00</span>
<span id="duration">00:00</span>
</div>
<div class="controls">
<div class="btn btn-prev" onclick="prevSong()"></div>
<div class="btn btn-play" id="playBtn" onclick="togglePlay()"></div>
<div class="btn btn-next" onclick="nextSong()"></div>
</div>
<br>
<!-- 新增音量控制 -->
<div class="volume-control">
<button class="btn-volume" onclick="toggleMute()">🔊</button>
<input type="range"
id="volumeSlider"
class="volume-slider"
min="0"
max="1"
step="0.1"
value="1">
</div>
<ul class="playlist" id="playlist"></ul>
</div>
<audio id="audio" src="" ></audio>
<audio id="audio" src="" ></audio>
<script>
<script>
// 在初始化代码中添加
document.body.addEventListener('touchstart', function initAudio() {
audio.load(); // iOS需要预加载
document.body.removeEventListener('touchstart', initAudio);
});
document.body.addEventListener('touchstart', function initAudio() {
audio.load(); // iOS需要预加载
document.body.removeEventListener('touchstart', initAudio);
});
// 音乐数据
const playlist = [
{
@@ -290,39 +290,39 @@ document.body.addEventListener('touchstart', function initAudio() {
let isPlaying = false;
let wakeLock = null;
let audioContext = null;
// 在播放前预处理链接
function ensureRangeSupport(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('HEAD', url);
xhr.onload = () => {
if (xhr.getResponseHeader('Accept-Ranges')) {
resolve(url);
} else {
resolve(`${url}?force_range=true`); // 兼容不支持的服务端
}
};
xhr.send();
});
}
// 在播放前预处理链接
function ensureRangeSupport(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('HEAD', url);
xhr.onload = () => {
if (xhr.getResponseHeader('Accept-Ranges')) {
resolve(url);
} else {
resolve(`${url}?force_range=true`); // 兼容不支持的服务端
}
};
xhr.send();
});
}
// 初始化播放列表
function initPlaylist() {
const playlistEl = document.getElementById('playlist');
playlist.forEach((song, index) => {
const li = document.createElement('li');
li.innerHTML = `${song.title} - ${song.artist}`;
li.onclick = () => playSong(index);
playlistEl.appendChild(li);
});
playlist.forEach((song, index) => {
const li = document.createElement('li');
li.innerHTML = `${song.title} - ${song.artist}`;
li.onclick = () => playSong(index);
playlistEl.appendChild(li);
});
// 仅加载第一首但不播放
// 仅加载第一首但不播放
const firstSong = playlist[0];
document.getElementById('song-title').textContent = firstSong.title;
document.getElementById('artist').textContent = firstSong.artist;
document.getElementById('cover-image').src = firstSong.cover;
audio.src = firstSong.src;
}
const firstSong = playlist[0];
document.getElementById('song-title').textContent = firstSong.title;
document.getElementById('artist').textContent = firstSong.artist;
document.getElementById('cover-image').src = firstSong.cover;
audio.src = firstSong.src;
}
let isFirstPlay = true;
// 播放歌曲
async function playSong(index) {
@@ -338,53 +338,53 @@ function ensureRangeSupport(url) {
document.querySelectorAll('#playlist li').forEach((li, i) => {
li.classList.toggle('playing', i === index);
});
// 只有当不是首次播放时才自动播放
if (!isFirstPlay) {
audio.play().catch(error => {
console.log('播放请求被阻止:', error);
});
playBtn.textContent = '⏸';
isPlaying = true;
}
// 只有当不是首次播放时才自动播放
if (!isFirstPlay) {
audio.play().catch(error => {
console.log('播放请求被阻止:', error);
});
playBtn.textContent = '⏸';
isPlaying = true;
}
}
// 切换播放/暂停
async function togglePlay() {
async function togglePlay() {
if (isFirstPlay) {
// 第一次点击时触发用户手势
console.log("---firest play-----")
await audio.play()
.then(() => {
isFirstPlay = false;
isPlaying = true;
playBtn.textContent = '⏸';
// audio.play(); // 暂停等待用户操作
})
.catch(error => {
alert('请点击此处开始播放');
});
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
const source = audioContext.createMediaElementSource(audio);
source.connect(audioContext.destination);
// 请求保持唤醒
console.log('---------init audiocontest')
}
// 第一次点击时触发用户手势
console.log("---firest play-----")
await audio.play()
.then(() => {
isFirstPlay = false;
isPlaying = true;
playBtn.textContent = '⏸';
// audio.play(); // 暂停等待用户操作
})
.catch(error => {
alert('请点击此处开始播放');
});
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
const source = audioContext.createMediaElementSource(audio);
source.connect(audioContext.destination);
// 请求保持唤醒
console.log('---------init audiocontest')
}
await requestWakeLock();
return;
}
return;
}
if (isPlaying) {
audio.pause();
playBtn.textContent = '▶';
} else {
await audio.play();
playBtn.textContent = '⏸';
}
isPlaying = !isPlaying;
if (isPlaying) {
audio.pause();
playBtn.textContent = '▶';
} else {
await audio.play();
playBtn.textContent = '⏸';
}
isPlaying = !isPlaying;
}
// 下一首
@@ -403,7 +403,7 @@ function ensureRangeSupport(url) {
audio.addEventListener('timeupdate', function() {
const progress = (audio.currentTime / audio.duration) * 100;
document.getElementById('progress').style.width = `${progress}%`;
document.getElementById('current-time').textContent =
formatTime(audio.currentTime);
});
@@ -428,39 +428,39 @@ function ensureRangeSupport(url) {
seconds = Math.floor(seconds % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// 在 JavaScript 中新增以下代码
const volumeSlider = document.getElementById('volumeSlider');
let isMuted = false;
let lastVolume = 1;
// 在 JavaScript 中新增以下代码
const volumeSlider = document.getElementById('volumeSlider');
let isMuted = false;
let lastVolume = 1;
// 音量滑动条控制
volumeSlider.addEventListener('input', (e) => {
audio.volume = e.target.value;
if (audio.volume === '0') {
document.querySelector('.btn-volume').textContent = '🔇';
} else {
document.querySelector('.btn-volume').textContent =
audio.volume > 0.5 ? '🔊' : '🔉';
}
isMuted = false; // 取消静音状态
});
// 音量滑动条控制
volumeSlider.addEventListener('input', (e) => {
audio.volume = e.target.value;
if (audio.volume === '0') {
document.querySelector('.btn-volume').textContent = '🔇';
} else {
document.querySelector('.btn-volume').textContent =
audio.volume > 0.5 ? '🔊' : '🔉';
}
isMuted = false; // 取消静音状态
});
// 静音切换
function toggleMute() {
isMuted = !isMuted;
if (isMuted) {
lastVolume = audio.volume;
audio.volume = 0;
volumeSlider.value = 0;
document.querySelector('.btn-volume').textContent = '🔇';
} else {
audio.volume = lastVolume;
volumeSlider.value = lastVolume;
document.querySelector('.btn-volume').textContent =
lastVolume > 0.5 ? '🔊' : '🔉';
// 静音切换
function toggleMute() {
isMuted = !isMuted;
if (isMuted) {
lastVolume = audio.volume;
audio.volume = 0;
volumeSlider.value = 0;
document.querySelector('.btn-volume').textContent = '🔇';
} else {
audio.volume = lastVolume;
volumeSlider.value = lastVolume;
document.querySelector('.btn-volume').textContent =
lastVolume > 0.5 ? '🔊' : '🔉';
}
}
}
async function requestWakeLock() {
async function requestWakeLock() {
try {
if ('wakeLock' in navigator) {
wakeLock = await navigator.wakeLock.request('screen');
@@ -478,22 +478,22 @@ const volumeSlider = document.getElementById('volumeSlider');
audio.setAttribute('webkit-playsinline', 'true');
}
}
// 初始化Service Worker
// 初始化Service Worker
//console.log('Service Worker 注册成功');
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service Worker 注册成功');
});
}
// 初始化音量默认1
audio.volume = 1;
// 初始化音量默认1
audio.volume = 1;
// 自动播放下一首
audio.addEventListener('ended', nextSong);
audio.addEventListener('ended', nextSong);
// 初始化
initPlaylist();
// playSong(0);
</script>
<div id="messages"></div>
</body>
// playSong(0);
</script>
<div id="messages"></div>
</body>
</html>

View File

@@ -237,7 +237,7 @@ def traverse_music_directory(directory, depth, exclude_dirs, support_extension):
# 发送给网页3thplay用于三者设备播放
async def thdplay(
action, args="/static/3thdplay.mp3", target="HTTP://192.168.1.10:58091/items/"
action, args="/static/3thdplay.mp3", target="HTTP://192.168.1.10:58090/thdaction"
):
# 接口地址 target,在参数文件指定
data = {"action": action, "args": args}

View File

@@ -135,7 +135,7 @@ class XiaoMusic:
if self.public_port == 0:
self.public_port = self.port
# 自动3thplay生成播放 post url
self.thdtarget = f"{self.hostname}:{self.public_port}/items/" # "HTTP://192.168.1.10:58091/items/"
self.thdtarget = f"{self.hostname}:{self.public_port}/thdaction" # "HTTP://192.168.1.10:58090/thdaction"
self.active_cmd = self.config.active_cmd.split(",")
self.exclude_dirs = set(self.config.exclude_dirs.split(","))
@@ -2097,8 +2097,7 @@ class XiaoMusicDevice:
await self.do_tts(self.config.stop_tts_msg)
await asyncio.sleep(3) # 等它说完
# 取消组内所有的下一首歌曲的定时器
if await thdplay("stop", "", self.xiaomusic.thdtarget):
return
await thdplay("stop", "", self.xiaomusic.thdtarget)
self.cancel_group_next_timer()
await self.group_force_stop_xiaoai()
self.log.info("stop now")