新增 migration 0021(background_key/url)、playlist/CRUD 返回 backgroundUrl、
POST .../music/{id}/background,并热更 popadmin 音乐列表背景上传;公读策略放行 perm/music/*。
Co-authored-by: Cursor <cursoragent@cursor.com>
91 lines
4.3 KiB
Python
91 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Patch popadmin.js music() UI to support background image upload/display."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
p = Path(__file__).with_name("popadmin.js")
|
|
c = p.read_text(encoding="utf-8")
|
|
|
|
replacements = [
|
|
(
|
|
"· 封面/mp3 存 sparkr",
|
|
"· 封面/背景图/mp3 存 sparkr",
|
|
),
|
|
(
|
|
"<thead><tr><th>封面</th><th>ID</th><th>曲名</th><th>会员</th><th>上架</th><th>排序</th><th>音频</th><th>操作</th></tr></thead>",
|
|
"<thead><tr><th>封面</th><th>背景</th><th>ID</th><th>曲名</th><th>会员</th><th>上架</th><th>排序</th><th>音频</th><th>操作</th></tr></thead>",
|
|
),
|
|
(
|
|
"${t.coverUrl ? `<img src=\"${esc(t.coverUrl)}\" alt=\"\" style=\"width:48px;height:48px;object-fit:cover;border-radius:6px\">` : '—'}</td>\n"
|
|
" <td>${esc(t.id)}</td>",
|
|
"${t.coverUrl ? `<img src=\"${esc(t.coverUrl)}\" alt=\"\" style=\"width:48px;height:48px;object-fit:cover;border-radius:6px\">` : '—'}</td>\n"
|
|
" <td>${t.backgroundUrl ? `<img src=\"${esc(t.backgroundUrl)}\" alt=\"\" style=\"width:48px;height:48px;object-fit:cover;border-radius:6px\">` : '—'}</td>\n"
|
|
" <td>${esc(t.id)}</td>",
|
|
),
|
|
(
|
|
'<label class="secondary btn-sm" style="display:inline-block;cursor:pointer;padding:4px 8px">封面<input type="file" accept="image/*" data-mcover="${esc(t.id)}" hidden></label>\n'
|
|
' <label class="secondary btn-sm" style="display:inline-block;cursor:pointer;padding:4px 8px">mp3<input type="file" accept="audio/mpeg,.mp3" data-maudio="${esc(t.id)}" hidden></label>',
|
|
'<label class="secondary btn-sm" style="display:inline-block;cursor:pointer;padding:4px 8px">封面<input type="file" accept="image/*" data-mcover="${esc(t.id)}" hidden></label>\n'
|
|
' <label class="secondary btn-sm" style="display:inline-block;cursor:pointer;padding:4px 8px">背景<input type="file" accept="image/*" data-mbg="${esc(t.id)}" hidden></label>\n'
|
|
' <label class="secondary btn-sm" style="display:inline-block;cursor:pointer;padding:4px 8px">mp3<input type="file" accept="audio/mpeg,.mp3" data-maudio="${esc(t.id)}" hidden></label>',
|
|
),
|
|
(
|
|
": '<tr><td colspan=\"8\">暂无曲目</td></tr>'",
|
|
": '<tr><td colspan=\"9\">暂无曲目</td></tr>'",
|
|
),
|
|
]
|
|
|
|
for old, new in replacements:
|
|
if old not in c:
|
|
raise SystemExit(f"pattern not found:\n{old[:120]!r}")
|
|
if old.count(old) != 1 and c.count(old) != 1:
|
|
raise SystemExit(f"pattern not unique ({c.count(old)}):\n{old[:80]!r}")
|
|
c = c.replace(old, new, 1)
|
|
|
|
handler_old = """ $('content').querySelectorAll('[data-mcover]').forEach((inp) => {
|
|
inp.onchange = async () => {
|
|
if (!inp.files?.[0]) return;
|
|
const fd = new FormData();
|
|
fd.append('file', inp.files[0]);
|
|
await apiUpload('/spark/music/' + inp.dataset.mcover + '/cover', fd);
|
|
pages.music();
|
|
};
|
|
});
|
|
$('content').querySelectorAll('[data-maudio]').forEach((inp) => {"""
|
|
|
|
handler_new = """ $('content').querySelectorAll('[data-mcover]').forEach((inp) => {
|
|
inp.onchange = async () => {
|
|
if (!inp.files?.[0]) return;
|
|
const fd = new FormData();
|
|
fd.append('file', inp.files[0]);
|
|
await apiUpload('/spark/music/' + inp.dataset.mcover + '/cover', fd);
|
|
pages.music();
|
|
};
|
|
});
|
|
$('content').querySelectorAll('[data-mbg]').forEach((inp) => {
|
|
inp.onchange = async () => {
|
|
if (!inp.files?.[0]) return;
|
|
const fd = new FormData();
|
|
fd.append('file', inp.files[0]);
|
|
await apiUpload('/spark/music/' + inp.dataset.mbg + '/background', fd);
|
|
pages.music();
|
|
};
|
|
});
|
|
$('content').querySelectorAll('[data-maudio]').forEach((inp) => {"""
|
|
|
|
if handler_old not in c:
|
|
raise SystemExit("cover/audio handlers not found")
|
|
if "data-mbg" in c and "/background" in c[c.find("async music()") : c.find("async novelCms()")]:
|
|
print("already patched?")
|
|
else:
|
|
c = c.replace(handler_old, handler_new, 1)
|
|
|
|
p.write_text(c, encoding="utf-8")
|
|
music = c[c.find("async music()") : c.find("async novelCms()")]
|
|
assert "backgroundUrl" in music
|
|
assert "data-mbg" in music
|
|
assert "/background" in music
|
|
print("patched ok", p, "bytes", p.stat().st_size)
|