mirror of
https://github.com/hanxi/xiaomusic.git
synced 2026-03-15 08:13:16 +08:00
增加音乐文件上传按钮 (#624)
This commit is contained in:
@@ -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 = "" # 歌单名
|
||||
|
||||
|
||||
60
xiaomusic/static/default/index.html
vendored
60
xiaomusic/static/default/index.html
vendored
@@ -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>
|
||||
41
xiaomusic/static/default/md.js
vendored
41
xiaomusic/static/default/md.js
vendored
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user