'commit'
This commit is contained in:
256
dsLightRag/Routes/SunoRoute.py
Normal file
256
dsLightRag/Routes/SunoRoute.py
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import fastapi
|
||||||
|
from fastapi import APIRouter, HTTPException, Query
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from Routes.suno_music_generator import SunoMusicGenerator
|
||||||
|
from Config import Config
|
||||||
|
|
||||||
|
# 创建路由路由器
|
||||||
|
router = APIRouter(prefix="/api/suno", tags=["音乐"])
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 获取API密钥
|
||||||
|
AK = Config.GPTNB_API_KEY
|
||||||
|
|
||||||
|
# 请求模型
|
||||||
|
class MusicGenerateRequest(BaseModel):
|
||||||
|
prompt: str
|
||||||
|
make_instrumental: Optional[bool] = True
|
||||||
|
|
||||||
|
# 初始化音乐生成器
|
||||||
|
music_generator = SunoMusicGenerator(AK)
|
||||||
|
|
||||||
|
# 任务状态存储(实际应用中可使用数据库)
|
||||||
|
music_tasks = {}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/prompt_input")
|
||||||
|
async def prompt_input(request: MusicGenerateRequest):
|
||||||
|
"""
|
||||||
|
生成音乐任务接口
|
||||||
|
:param request: 包含提示词和是否纯音乐的请求体
|
||||||
|
:return: 任务ID和状态信息
|
||||||
|
"""
|
||||||
|
prompt = request.prompt
|
||||||
|
make_instrumental = request.make_instrumental
|
||||||
|
|
||||||
|
if not prompt:
|
||||||
|
raise HTTPException(status_code=400, detail="缺少提示词参数")
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"开始生成音乐任务,提示词: {prompt}")
|
||||||
|
|
||||||
|
# 调用音乐生成器生成音乐
|
||||||
|
# 注意:我们只执行生成请求,不等待结果,因为这是一个异步过程
|
||||||
|
# 构建JSON请求体
|
||||||
|
request_json = {
|
||||||
|
"gpt_description_prompt": prompt,
|
||||||
|
"mv": "chirp-v3-5",
|
||||||
|
"prompt": "",
|
||||||
|
"make_instrumental": make_instrumental
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置请求头
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": f"Bearer {AK}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行生成请求
|
||||||
|
response = requests.post(
|
||||||
|
SunoMusicGenerator.GENERATE_URL,
|
||||||
|
headers=headers,
|
||||||
|
json=request_json,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# 解析响应
|
||||||
|
generate_json = response.json()
|
||||||
|
logger.info(f"音乐生成响应: {generate_json}")
|
||||||
|
|
||||||
|
# 提取任务ID
|
||||||
|
task_id = None
|
||||||
|
if "id" in generate_json:
|
||||||
|
task_id = generate_json["id"]
|
||||||
|
elif "task_id" in generate_json:
|
||||||
|
task_id = generate_json["task_id"]
|
||||||
|
elif "clip_id" in generate_json:
|
||||||
|
task_id = generate_json["clip_id"]
|
||||||
|
|
||||||
|
if task_id is None:
|
||||||
|
raise HTTPException(status_code=500, detail="无法从响应中提取任务ID")
|
||||||
|
|
||||||
|
# 存储任务信息
|
||||||
|
music_tasks[task_id] = {
|
||||||
|
"status": "processing",
|
||||||
|
"prompt": prompt,
|
||||||
|
"create_time": datetime.datetime.now().isoformat(),
|
||||||
|
"response": generate_json
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"音乐生成任务已提交,任务ID: {task_id}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"code": 200,
|
||||||
|
"message": "音乐生成任务已提交",
|
||||||
|
"data": {
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "processing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error(f"解析音乐生成响应失败: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"解析音乐生成响应失败: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"音乐生成过程中发生错误: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"音乐生成过程中发生错误: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/check_task_status")
|
||||||
|
async def check_task_status(task_id: str = Query(..., description="音乐生成任务ID")):
|
||||||
|
"""
|
||||||
|
检查音乐生成任务状态接口
|
||||||
|
:param task_id: 音乐生成任务ID
|
||||||
|
:return: 任务状态信息
|
||||||
|
"""
|
||||||
|
if not task_id:
|
||||||
|
raise HTTPException(status_code=400, detail="缺少任务ID参数")
|
||||||
|
|
||||||
|
# 检查任务是否存在
|
||||||
|
if task_id not in music_tasks:
|
||||||
|
raise HTTPException(status_code=404, detail=f"任务ID不存在: {task_id}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"检查任务状态,任务ID: {task_id}")
|
||||||
|
|
||||||
|
# 获取任务信息
|
||||||
|
task_info = music_tasks[task_id]
|
||||||
|
generate_json = task_info["response"]
|
||||||
|
|
||||||
|
# 构建查询URL
|
||||||
|
url_builder = [SunoMusicGenerator.FEED_URL, "?"]
|
||||||
|
|
||||||
|
# 尝试从生成响应中获取clips的ID
|
||||||
|
clip_ids = []
|
||||||
|
if "clips" in generate_json:
|
||||||
|
clips_array = generate_json["clips"]
|
||||||
|
for clip in clips_array:
|
||||||
|
if "id" in clip:
|
||||||
|
clip_ids.append(clip["id"])
|
||||||
|
|
||||||
|
# 添加ids参数
|
||||||
|
if clip_ids:
|
||||||
|
ids_param = ",".join(clip_ids)
|
||||||
|
url_builder.append(f"ids={ids_param}")
|
||||||
|
logger.info(f"使用clips ID查询: {ids_param}")
|
||||||
|
else:
|
||||||
|
url_builder.append(f"ids={task_id}")
|
||||||
|
logger.info(f"使用任务ID查询: {task_id}")
|
||||||
|
|
||||||
|
url = "".join(url_builder)
|
||||||
|
logger.info(f"查询URL: {url}")
|
||||||
|
|
||||||
|
# 设置请求头
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {AK}",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行查询请求
|
||||||
|
response = requests.get(url, headers=headers, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# 解析查询响应
|
||||||
|
json_response = response.json()
|
||||||
|
clips = json_response.get("clips", [])
|
||||||
|
|
||||||
|
if clips:
|
||||||
|
# 遍历所有返回的音乐片段
|
||||||
|
for clip in clips:
|
||||||
|
clip_id = clip.get("id")
|
||||||
|
status = clip.get("status")
|
||||||
|
title = clip.get("title")
|
||||||
|
audio_url = clip.get("audio_url")
|
||||||
|
|
||||||
|
logger.info(f"查询结果:")
|
||||||
|
logger.info(f"ID: {clip_id}")
|
||||||
|
logger.info(f"标题: {title}")
|
||||||
|
logger.info(f"状态: {status}")
|
||||||
|
|
||||||
|
# 更新任务状态
|
||||||
|
task_info["status"] = status
|
||||||
|
task_info["last_check_time"] = datetime.datetime.now().isoformat()
|
||||||
|
task_info["title"] = title
|
||||||
|
|
||||||
|
if status == "complete":
|
||||||
|
# 确保audio_url字段存在
|
||||||
|
if audio_url:
|
||||||
|
# 移除URL中可能存在的反引号
|
||||||
|
audio_url = audio_url.replace("`", "").strip()
|
||||||
|
task_info["audio_url"] = audio_url
|
||||||
|
logger.info("音乐生成已完成!")
|
||||||
|
logger.info(f"音频URL: {audio_url}")
|
||||||
|
|
||||||
|
# 下载音频文件
|
||||||
|
file_name = f"suno_music_{int(time.time())}.mp3"
|
||||||
|
save_path = music_generator.base_path / file_name
|
||||||
|
if music_generator.download_audio(audio_url, str(save_path)):
|
||||||
|
task_info["local_path"] = str(save_path)
|
||||||
|
else:
|
||||||
|
logger.warning("音乐生成已完成,但未找到音频URL!")
|
||||||
|
|
||||||
|
elif status == "failed":
|
||||||
|
logger.error("音乐生成失败!")
|
||||||
|
task_info["error_message"] = clip.get("error_message", "未知错误")
|
||||||
|
|
||||||
|
# 更新任务存储
|
||||||
|
music_tasks[task_id] = task_info
|
||||||
|
|
||||||
|
return {
|
||||||
|
"code": 200,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": {
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": status,
|
||||||
|
"title": title,
|
||||||
|
"audio_url": audio_url if status == "complete" else None,
|
||||||
|
"local_path": task_info.get("local_path") if status == "complete" else None,
|
||||||
|
"error_message": task_info.get("error_message") if status == "failed" else None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
logger.info("未找到音乐片段")
|
||||||
|
return {
|
||||||
|
"code": 200,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": {
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "processing",
|
||||||
|
"title": None,
|
||||||
|
"audio_url": None,
|
||||||
|
"local_path": None,
|
||||||
|
"error_message": None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except fastapi.Requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"查询任务状态失败: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"查询任务状态失败: {str(e)}")
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error(f"解析查询响应失败: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"解析查询响应失败: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"查询任务状态过程中发生错误: {e}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"查询任务状态过程中发生错误: {str(e)}")
|
||||||
|
|
BIN
dsLightRag/Routes/__pycache__/SunoRoute.cpython-310.pyc
Normal file
BIN
dsLightRag/Routes/__pycache__/SunoRoute.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -20,6 +20,7 @@ from Routes.TeachingModel.api.DocumentController import router as document_route
|
|||||||
from Routes.TeachingModel.api.TeachingModelController import router as teaching_model_router
|
from Routes.TeachingModel.api.TeachingModelController import router as teaching_model_router
|
||||||
from Routes.QA import router as qa_router
|
from Routes.QA import router as qa_router
|
||||||
from Routes.JiMengRoute import router as jimeng_router
|
from Routes.JiMengRoute import router as jimeng_router
|
||||||
|
from Routes.SunoRoute import router as suno_router
|
||||||
|
|
||||||
from Util.LightRagUtil import *
|
from Util.LightRagUtil import *
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
@@ -33,17 +34,19 @@ logger.addHandler(handler)
|
|||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(_: FastAPI):
|
async def lifespan(_: FastAPI):
|
||||||
pool = await init_postgres_pool()
|
pool = await init_postgres_pool()
|
||||||
app.state.pool = pool
|
app.state.pool = pool
|
||||||
|
|
||||||
asyncio.create_task(train_document_task())
|
asyncio.create_task(train_document_task())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
# 应用关闭时销毁连接池
|
# 应用关闭时销毁连接池
|
||||||
await close_postgres_pool(pool)
|
await close_postgres_pool(pool)
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
# 挂载静态文件目录
|
# 挂载静态文件目录
|
||||||
@@ -56,8 +59,9 @@ app.include_router(rag_router) # LightRAG路由
|
|||||||
app.include_router(knowledge_router) # 知识图谱路由
|
app.include_router(knowledge_router) # 知识图谱路由
|
||||||
app.include_router(oss_router) # 阿里云OSS路由
|
app.include_router(oss_router) # 阿里云OSS路由
|
||||||
app.include_router(llm_router) # 大模型路由
|
app.include_router(llm_router) # 大模型路由
|
||||||
app.include_router(qa_router) # 答疑路由
|
app.include_router(qa_router) # 答疑路由
|
||||||
app.include_router(jimeng_router) # 即梦路由
|
app.include_router(jimeng_router) # 即梦路由
|
||||||
|
app.include_router(suno_router) # Suno路由
|
||||||
|
|
||||||
# Teaching Model 相关路由
|
# Teaching Model 相关路由
|
||||||
# 登录相关(不用登录)
|
# 登录相关(不用登录)
|
||||||
@@ -75,6 +79,5 @@ app.include_router(teaching_model_router, prefix="/api/teaching/model", tags=["t
|
|||||||
# 教学答疑
|
# 教学答疑
|
||||||
app.include_router(teaching_model_router, prefix="/api/teaching/model", tags=["teacher_model"])
|
app.include_router(teaching_model_router, prefix="/api/teaching/model", tags=["teacher_model"])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8100)
|
uvicorn.run(app, host="0.0.0.0", port=8100)
|
||||||
|
@@ -134,7 +134,7 @@
|
|||||||
<option value="安静学习">安静学习</option>
|
<option value="安静学习">安静学习</option>
|
||||||
<option value="欢快活泼">欢快活泼</option>
|
<option value="欢快活泼">欢快活泼</option>
|
||||||
<option value="庄重严肃">庄重严肃</option>
|
<option value="庄重严肃">庄重严肃</option>
|
||||||
<option value="温馨感人">温馨感人</option>
|
<option value="温馨感动">温馨感动</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="form-helper">示例:「夏日海边」「深夜治愈」「热血励志」</div>
|
<div class="form-helper">示例:「夏日海边」「深夜治愈」「热血励志」</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -199,6 +199,7 @@
|
|||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-primary"><i class="fas fa-magic"></i> 生成音乐提示词</button>
|
<button type="submit" class="btn btn-primary"><i class="fas fa-magic"></i> 生成音乐提示词</button>
|
||||||
|
<button type="button" id="randomSampleBtn" class="btn btn-info"><i class="fas fa-random"></i> 随机示例</button>
|
||||||
<button type="button" id="resetForm" class="btn btn-secondary"><i class="fas fa-undo"></i> 重置</button>
|
<button type="button" id="resetForm" class="btn btn-secondary"><i class="fas fa-undo"></i> 重置</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -220,93 +221,195 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// 表单提交事件
|
// 音乐示例数据
|
||||||
const form = document.getElementById('musicPromptForm');
|
const musicSamples = [
|
||||||
const resultContainer = document.getElementById('resultContainer');
|
{
|
||||||
const resultContent = document.getElementById('resultContent');
|
style: "华语流行",
|
||||||
const resetFormBtn = document.getElementById('resetForm');
|
mood: "欢快活泼",
|
||||||
const copyResultBtn = document.getElementById('copyResult');
|
vocal: "甜美女声",
|
||||||
const generateMusicBtn = document.getElementById('generateMusic');
|
tempo: "4/4 拍 120 BPM",
|
||||||
|
instruments: "吉他+贝斯+鼓",
|
||||||
form.addEventListener('submit', function(e) {
|
theme: "快乐学习",
|
||||||
e.preventDefault();
|
teachingSubject: "英语"
|
||||||
|
},
|
||||||
// 获取表单数据
|
{
|
||||||
const style = document.getElementById('style').value;
|
style: "古风",
|
||||||
const mood = document.getElementById('mood').value;
|
mood: "国风江湖",
|
||||||
const vocal = document.getElementById('vocal').value;
|
vocal: "清澈男声",
|
||||||
const tempo = document.getElementById('tempo').value;
|
tempo: "4/4 拍 80 BPM",
|
||||||
const instruments = document.getElementById('instruments').value;
|
instruments: "笛子+古筝",
|
||||||
const theme = document.getElementById('theme').value;
|
theme: "诗词歌赋",
|
||||||
const teachingSubject = document.getElementById('teachingSubject').value;
|
teachingSubject: "语文"
|
||||||
|
},
|
||||||
// 生成提示词
|
{
|
||||||
let prompt = `${style} ${mood},${vocal},${tempo},配${instruments},主题围绕'${theme}'`;
|
style: "Classical",
|
||||||
if (teachingSubject) {
|
mood: "安静学习",
|
||||||
prompt += `,适合${teachingSubject}教学使用`;
|
vocal: "钢琴弦乐",
|
||||||
}
|
tempo: "3/4 拍 90 BPM",
|
||||||
prompt += `,时长 2 分钟。`;
|
instruments: "钢琴弦乐",
|
||||||
|
theme: "数学的旋律",
|
||||||
// 显示结果
|
teachingSubject: "数学"
|
||||||
resultContent.textContent = prompt;
|
},
|
||||||
resultContainer.style.display = 'block';
|
{
|
||||||
|
style: "民谣",
|
||||||
// 滚动到结果区域
|
mood: "温馨感动",
|
||||||
resultContainer.scrollIntoView({ behavior: 'smooth' });
|
vocal: "温柔女声",
|
||||||
});
|
tempo: "4/4 拍 100 BPM",
|
||||||
|
instruments: "木吉他主奏",
|
||||||
// 重置表单
|
theme: "自然科学的奥秘",
|
||||||
resetFormBtn.addEventListener('click', function() {
|
teachingSubject: "科学"
|
||||||
form.reset();
|
},
|
||||||
resultContainer.style.display = 'none';
|
{
|
||||||
});
|
style: "City Pop",
|
||||||
|
mood: "夏日海边",
|
||||||
// 复制结果
|
vocal: "中性嗓音",
|
||||||
copyResultBtn.addEventListener('click', function() {
|
tempo: "4/4 拍 120 BPM",
|
||||||
const textToCopy = resultContent.textContent;
|
instruments: "电子合成器",
|
||||||
navigator.clipboard.writeText(textToCopy).then(function() {
|
theme: "阳光沙滩海浪",
|
||||||
// 显示复制成功提示
|
teachingSubject: "地理"
|
||||||
const originalText = copyResultBtn.innerHTML;
|
},
|
||||||
copyResultBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
|
{
|
||||||
setTimeout(function() {
|
style: "电子 R&B",
|
||||||
copyResultBtn.innerHTML = originalText;
|
mood: "深夜治愈",
|
||||||
}, 2000);
|
vocal: "低沉男声",
|
||||||
});
|
tempo: "6/8 拍 85 BPM 慢摇",
|
||||||
});
|
instruments: "808 鼓点+萨克斯",
|
||||||
|
theme: "历史的回声",
|
||||||
// 生成音乐
|
teachingSubject: "历史"
|
||||||
generateMusicBtn.addEventListener('click', function() {
|
},
|
||||||
alert('音乐生成功能已触发,正在处理您的请求...');
|
{
|
||||||
// 这里可以添加实际的音乐生成逻辑
|
style: "摇滚",
|
||||||
});
|
mood: "热血励志",
|
||||||
|
vocal: "高亢女声",
|
||||||
// 添加页面加载动画
|
tempo: "4/4 拍 140 BPM",
|
||||||
const featureCards = document.querySelectorAll('.feature-card');
|
instruments: "吉他+贝斯+鼓",
|
||||||
|
theme: "运动精神",
|
||||||
function checkElements() {
|
teachingSubject: "体育"
|
||||||
const triggerBottom = window.innerHeight * 0.8;
|
},
|
||||||
|
{
|
||||||
featureCards.forEach(card => {
|
style: "爵士",
|
||||||
const cardTop = card.getBoundingClientRect().top;
|
mood: "庄重严肃",
|
||||||
if (cardTop < triggerBottom) {
|
vocal: "沙哑烟嗓",
|
||||||
card.style.opacity = '1';
|
tempo: "4/4 拍 100 BPM",
|
||||||
card.style.transform = 'translateY(0)';
|
instruments: "钢琴弦乐",
|
||||||
|
theme: "艺术欣赏",
|
||||||
|
teachingSubject: "美术"
|
||||||
}
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 获取DOM元素
|
||||||
|
const form = document.getElementById('musicPromptForm');
|
||||||
|
const resultContainer = document.getElementById('resultContainer');
|
||||||
|
const resultContent = document.getElementById('resultContent');
|
||||||
|
const resetFormBtn = document.getElementById('resetForm');
|
||||||
|
const copyResultBtn = document.getElementById('copyResult');
|
||||||
|
const generateMusicBtn = document.getElementById('generateMusic');
|
||||||
|
const randomSampleBtn = document.getElementById('randomSampleBtn');
|
||||||
|
|
||||||
|
// 随机示例按钮点击事件
|
||||||
|
randomSampleBtn.addEventListener('click', function() {
|
||||||
|
// 随机选择一个示例
|
||||||
|
const randomIndex = Math.floor(Math.random() * musicSamples.length);
|
||||||
|
const sample = musicSamples[randomIndex];
|
||||||
|
|
||||||
|
// 填充表单
|
||||||
|
document.getElementById('style').value = sample.style;
|
||||||
|
document.getElementById('mood').value = sample.mood;
|
||||||
|
document.getElementById('vocal').value = sample.vocal;
|
||||||
|
document.getElementById('tempo').value = sample.tempo;
|
||||||
|
document.getElementById('instruments').value = sample.instruments;
|
||||||
|
document.getElementById('theme').value = sample.theme;
|
||||||
|
document.getElementById('teachingSubject').value = sample.teachingSubject;
|
||||||
|
|
||||||
|
// 隐藏结果区域
|
||||||
|
resultContainer.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// 表单提交事件
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 获取表单数据
|
||||||
|
const style = document.getElementById('style').value;
|
||||||
|
const mood = document.getElementById('mood').value;
|
||||||
|
const vocal = document.getElementById('vocal').value;
|
||||||
|
const tempo = document.getElementById('tempo').value;
|
||||||
|
const instruments = document.getElementById('instruments').value;
|
||||||
|
const theme = document.getElementById('theme').value;
|
||||||
|
const teachingSubject = document.getElementById('teachingSubject').value;
|
||||||
|
|
||||||
|
// 生成提示词
|
||||||
|
let prompt = `${style} ${mood},${vocal},${tempo},配${instruments},主题围绕'${theme}'`;
|
||||||
|
if (teachingSubject) {
|
||||||
|
prompt += `,适合${teachingSubject}教学使用`;
|
||||||
|
}
|
||||||
|
prompt += `,时长 2 分钟。`;
|
||||||
|
|
||||||
|
// 显示结果
|
||||||
|
resultContent.textContent = prompt;
|
||||||
|
resultContainer.style.display = 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置表单按钮点击事件
|
||||||
|
resetFormBtn.addEventListener('click', function() {
|
||||||
|
form.reset();
|
||||||
|
resultContainer.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 复制结果按钮点击事件
|
||||||
|
copyResultBtn.addEventListener('click', function() {
|
||||||
|
const textToCopy = resultContent.textContent;
|
||||||
|
navigator.clipboard.writeText(textToCopy).then(function() {
|
||||||
|
alert('提示词已复制到剪贴板!');
|
||||||
|
}).catch(function(err) {
|
||||||
|
alert('复制失败: ' + err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成音乐按钮点击事件
|
||||||
|
generateMusicBtn.addEventListener('click', function() {
|
||||||
|
const prompt = resultContent.textContent;
|
||||||
|
if (!prompt) {
|
||||||
|
alert('请先生成音乐提示词');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化样式
|
// 显示加载状态
|
||||||
featureCards.forEach(card => {
|
generateMusicBtn.disabled = true;
|
||||||
card.style.opacity = '0';
|
generateMusicBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 提交中...';
|
||||||
card.style.transform = 'translateY(20px)';
|
|
||||||
card.style.transition = 'opacity 0.5s ease, transform 0.5s ease, delay 0.1s';
|
// 调用后端API生成音乐任务
|
||||||
|
fetch('/api/suno/prompt_input', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: prompt,
|
||||||
|
is_instrumental: false // 默认为带 vocals 的音乐
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
generateMusicBtn.disabled = false;
|
||||||
|
generateMusicBtn.innerHTML = '<i class="fas fa-play"></i> 生成音乐';
|
||||||
|
|
||||||
|
if (data.task_id) {
|
||||||
|
// 跳转到进度页面
|
||||||
|
window.location.href = `/static/Suno/music_progress.html?task_id=${data.task_id}`;
|
||||||
|
} else {
|
||||||
|
alert('生成音乐任务失败: ' + (data.error || '未知错误'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
generateMusicBtn.disabled = false;
|
||||||
|
generateMusicBtn.innerHTML = '<i class="fas fa-play"></i> 生成音乐';
|
||||||
|
alert('请求失败: ' + error.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
</script>
|
||||||
// 检查可见性
|
|
||||||
checkElements();
|
|
||||||
window.addEventListener('scroll', checkElements);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Reference in New Issue
Block a user