1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2026-03-15 08:13:16 +08:00

增加音乐文件上传按钮 (#624)

This commit is contained in:
TAnsz
2025-12-22 17:16:08 +08:00
committed by GitHub
parent 26888b2441
commit dcb9724c45
3 changed files with 137 additions and 26 deletions

View File

@@ -29,6 +29,7 @@ from fastapi import (
Depends,
FastAPI,
File,
Form,
HTTPException,
Query,
Request,
@@ -632,6 +633,67 @@ async def upload_yt_dlp_cookie(file: UploadFile = File(...)):
}
@app.post("/uploadmusic")
async def upload_music(playlist: str = Form(...), file: UploadFile = File(...)):
"""上传音乐文件到当前播放列表对应的目录。"""
try:
# 选择目标目录:优先尝试由播放列表中已有歌曲推断目录
dest_dir = xiaomusic.music_path
# 特殊歌单映射
if playlist == "下载":
dest_dir = xiaomusic.download_path
elif playlist == "其他":
dest_dir = xiaomusic.music_path
else:
# 如果播放列表中存在歌曲,从其中任意一首推断目录
musics = xiaomusic.music_list.get(playlist, [])
if musics and len(musics) > 0:
first = musics[0]
filepath = xiaomusic.all_music.get(first, "")
if filepath:
dest_dir = os.path.dirname(filepath)
# 确保目录存在
if not os.path.exists(dest_dir):
os.makedirs(dest_dir, exist_ok=True)
# 保存文件,避免路径穿越
filename = os.path.basename(file.filename)
if filename == "":
raise HTTPException(status_code=400, detail="Invalid filename")
dest_path = os.path.join(dest_dir, filename)
# 避免覆盖已有文件,简单地添加序号后缀
base, ext = os.path.splitext(filename)
counter = 1
while os.path.exists(dest_path):
filename = f"{base}_{counter}{ext}"
dest_path = os.path.join(dest_dir, filename)
counter += 1
with open(dest_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
# 修复权限并刷新列表索引
try:
chmoddir(dest_dir)
except Exception:
pass
# 重新生成音乐列表索引
try:
xiaomusic._gen_all_music_list()
except Exception:
pass
return {"ret": "OK", "filename": filename}
except HTTPException:
raise
except Exception as e:
log.exception(f"upload music failed: {e}")
raise HTTPException(status_code=500, detail="Upload failed") from e
class PlayListObj(BaseModel):
name: str = "" # 歌单名

View File

@@ -12,10 +12,10 @@
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments) };
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
<!-- umami -->
@@ -25,7 +25,8 @@
<body class="index_page">
<div class="player">
<h1>小爱音箱播放器
<a id="version" href="https://xdocs.hanxi.cc/issues/changelog.html" target="_blank">0.3.0</a><span id="versionnew" class="new-badge"></span>
<a id="version" href="https://xdocs.hanxi.cc/issues/changelog.html" target="_blank">0.3.0</a><span
id="versionnew" class="new-badge"></span>
</h1>
<label for="did">选择播放设备:</label>
@@ -33,7 +34,7 @@
<option value="default">默认设备</option>
</select>
<label for="music_list" style="display: flex;align-items: center;">选择播放列表:
<label for="music_list" style="display: flex;align-items: center;">选择播放列表:
<div class="option-inline" onclick="sendcmd('刷新列表')">
<span class="material-icons">refresh</span>
<span class="tooltip">刷新列表</span>
@@ -43,21 +44,29 @@
</select>
<label for="music_name" style="display: flex;align-items: center;">选择歌曲:
<div class="option-inline" onclick="toggleDelete()">
<span class="material-icons">delete</span>
<span class="tooltip">删除歌曲</span>
<div style="display: flex;align-items: center;margin-left: auto;">
<div class="option-inline" onclick="triggerUpload()">
<span class="material-icons">file_upload</span>
<span class="tooltip">上传歌曲</span>
</div>
<div class="option-inline" onclick="toggleDelete()">
<span class="material-icons">delete</span>
<span class="tooltip">删除歌曲</span>
</div>
</div>
</label>
<select id="music_name" class="song-selector">
</select>
<input id="upload-file" type="file" accept=".mp3,.m4a,.wav,.flac,.aac,.ogg,.ape" style="display:none" />
<div id="device-audio">
<progress class="progress" id="progress" value="0" max="100"></progress>
<div style="display: flex; justify-content: space-between; width: 100%;">
<span class="current-time" id="current-time">0:00</span>
<div class="current-song" id="playering-music">当前播放歌曲:无</div>
<span class="duration" id="duration">00:00</span>
</div>
<progress class="progress" id="progress" value="0" max="100"></progress>
<div style="display: flex; justify-content: space-between; width: 100%;">
<span class="current-time" id="current-time">0:00</span>
<div class="current-song" id="playering-music">当前播放歌曲:无</div>
<span class="duration" id="duration">00:00</span>
</div>
</div>
<audio id="audio" controls src="" autoplay></audio>
@@ -148,16 +157,15 @@
<input type="text" id="music-url" class="search-input" placeholder="请输入播放链接"
value="https://lhttp.qtfm.cn/live/4915/64k.mp3">
<h2>播放文字</h2>
<input type="text" id="text-tts" class="search-input" placeholder="请输入文字"
value="播放文字测试">
<input type="text" id="text-tts" class="search-input" placeholder="请输入文字" value="播放文字测试">
<div class="component-button-group">
<button onclick="playUrl()">播放链接</button>
<button onclick="playProxyUrl()">代理播放链接</button>
<button onclick="playUrl()">播放链接</button>
<button onclick="playProxyUrl()">代理播放链接</button>
</div>
<div class="component-button-group">
<button onclick="playTts()">播放文字</button>
<button onclick="togglePlayLink()">关闭</button>
<button onclick="playTts()">播放文字</button>
<button onclick="togglePlayLink()">关闭</button>
</div>
</div>
@@ -187,17 +195,17 @@
<p>当前HOST: <span id="local-host"></span></p>
<p>设置中的HOST: <span id="setting-host"></span></p>
<div class="component-button-group">
<a href="./setting.html" target="_blank"><button>立即修改</button></a>
<button onclick="nowarning()">继续并不再显示</button>
<button onclick="toggleWarning()">取消</button>
<a href="./setting.html" target="_blank"><button>立即修改</button></a>
<button onclick="nowarning()">继续并不再显示</button>
<button onclick="toggleWarning()">取消</button>
</div>
</div>
<div class="footer">
Powered by XiaoMusic
Powered by XiaoMusic
</div>
<script src="./md.js?version=1766282679">
</script>
</body>
</html>
</html>

View File

@@ -420,6 +420,47 @@ function do_play_music(musicname, searchkey) {
});
}
// 上传功能:触发文件选择并提交到后端
function triggerUpload() {
const uploadInput = document.getElementById("upload-file");
if (uploadInput) {
uploadInput.value = null;
uploadInput.click();
}
}
document.addEventListener("DOMContentLoaded", function () {
const uploadInput = document.getElementById("upload-file");
if (uploadInput) {
uploadInput.addEventListener("change", async function (e) {
const files = e.target.files;
if (!files || files.length === 0) return;
const file = files[0];
const playlist = $("#music_list").val();
const form = new FormData();
form.append("playlist", playlist);
form.append("file", file);
try {
const resp = await fetch('/uploadmusic', {
method: 'POST',
body: form,
});
if (!resp.ok) throw new Error('网络错误');
const data = await resp.json();
if (data && data.ret === 'OK') {
alert('上传成功: ' + data.filename);
refresh_music_list();
} else {
alert('上传失败');
}
} catch (err) {
console.error(err);
alert('上传失败');
}
});
}
});
$("#play").on("click", () => {
var search_key = $("#music-name").val();
if (search_key == null) {