tiktok_songs / app.py
morso
solov
0104d06
import gradio as gr
import os
import re
import json
import time
from google import genai
# Filter songs based on region and time period
def filter_songs(region, time_period):
time_map = {
"过去7天": 7,
"过去30天": 30
}
region_map = {
"美国": "us",
"英国": "uk",
"韩国": "kr",
"日本": "jp"
}
file_name = f"trend_data_{region_map[region]}_{time_map[time_period]}.json"
with open(file_name, "r", encoding="utf-8") as file:
data = json.load(file) # 解析 JSON 文件
return data
# Initial song data
ALL_SONGS = filter_songs("美国", "过去7天")
# Simulate API call for song analysis
def analyze_song(song_name):
prompt = """
根据我上传的歌曲音频,站在乐评人的角度进行专业点评,要求包含两方面信息:
## 一. 对歌曲的详细解析,必须使用中文描述,需包含以下方面(确保每个部分尽可能详细和具体,以生成精准的音乐评价):
1. 流派/风格 (Genre/Style):{在此处填写一个或多个音乐流派,多个流派可以用逗号或 "和" 连接,例如:classic rock, synthwave 和 nostalgic}
2. 乐器 (Instrumentation)
- 主要乐器:{列出主要乐器,并用形容词修饰其音色或演奏方式,例如:distorted electric guitar, soft piano melody}
- 次要乐器(可选):{列出次要乐器,同样可以添加修饰,例如:pulsating bass, rhythmic percussion}
3. 人声 (Vocal Style) (可选)
- 性别:{male/female/无}
- 音色/风格:{用形容词描述人声的特点,例如:raspy, warm, soulful, slight reverb}
- 演唱方式(可选):{例如:call-and-response vocals, harmonies, spoken word}
4. 情绪/氛围 (Mood/Atmosphere):{使用形容词或短语描述音乐的整体感觉,例如:high-energy, smooth, dreamy, uplifting, melancholic}
- 场景描述(可选):{用简短的场景描述来进一步强化氛围,例如:late-night lounge setting, driving down a desert highway}
5. 具体元素/参考 (Specific Elements/References) (可选):{提供更具体的风格指导、年代参考或特定元素,例如:anthemic chorus, '80s stadium rock, 80s-inspired, reminiscent of Vangelis}
6. 节奏/动态 (Tempo/Dynamics) (可选):使用动词或形容词描述节奏的特点, 或乐器动态, 例如: Pulsating, driving, laid-back, syncopated,乐器动态: Crescendo, diminuendo, staccato 等
基于上述详细解析,总结一句用于音乐生成的英文文本提示词,不要超过180个字符,必须使用英文描述,示例如下:An 80s-inspired synthwave track with analog synthesizers, pulsating bass, and dreamy atmospheric pads. Male vocals with slight reverb for a nostalgic, futuristic feel.
最终使用json返回内容,json格式示例如下,禁止其他多余输出:
{
"chinese_description": "..."
"english_prompt": "..."
}
"""
client = genai.Client(api_key=os.getenv("GOOGLE_GEN_KEY"))
myfile = client.files.upload(file=f'media/{song_name}.mp3')
response = client.models.generate_content(
model='gemini-2.0-flash',
contents=[prompt, myfile]
)
text = response.text
print(text)
match = re.search(r'```json\n(.*?)\n```', text, re.DOTALL)
if match:
json_str = match.group(1)
try:
result = json.loads(json_str)
except json.JSONDecodeError as e:
print(f"JSON parsing error: {e}")
return {
"song_description": result["chinese_description"],
"suno_prompt": result["english_prompt"]
}
# Simulate Suno API call
def generate_similar_song(suno_prompt):
# Simulate API call delay
time.sleep(3)
# Simulated response
return {
"audio_url": "https://sf16-ies-music-sg.tiktokcdn.com/obj/tos-alisg-ve-2774/oYYOWM1aKGyB8Eixn0hiAfhWsAjzswMoIItQMI",
"status": "success"
}
# Create song list HTML
def create_song_list(songs):
html = """
<style>
.song-container {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
}
.song-item {
display: flex;
align-items: center;
gap: 16px;
padding: 12px;
border-radius: 8px;
background-color: #f9f9f9;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.rank {
font-size: 24px;
font-weight: bold;
min-width: 40px;
text-align: center;
}
.cover {
width: 80px;
height: 80px;
border-radius: 4px;
object-fit: cover;
}
.song-info {
flex: 1;
}
.song-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 4px;
}
.video-count {
color: #666;
font-size: 14px;
}
.button-group {
display: flex;
gap: 8px;
}
.button {
padding: 8px 12px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: bold;
}
.tiktok-btn {
color: white;
}
.play-btn {
background-color: #1db954;
color: white;
}
.stats-btn {
background-color: #0077b5;
color: white;
}
.analyze-btn {
background-color: #6c5ce7;
color: white;
}
</style>
<div class="song-container">
"""
# <div class="video-count">{song['video_count']}</div>
# <button class="button play-btn" onclick='playSong("{song['title']}")'>播放</button>
# <button class="button analyze-btn" onclick='analyzeSong("{song['title']}")'>分析歌曲</button>
for song in songs:
html += f"""
<div class="song-item" id="song-{song['rank']}">
<div class="rank">{song['rank']}</div>
<img class="cover" src="{song['cover_url']}" alt="{song['title']}">
<div class="song-info">
<div class="song-name">{song['title']} - {song['author']}</div>
</div>
<div class="button-group">
<a href="{song['link']}" target="_blank" class="button tiktok-btn">TikTok地址</a>
</div>
</div>
"""
html += "</div>"
return html
# Main app function
def app():
# Create the Gradio interface
with gr.Blocks() as demo:
# App title
gr.Markdown("# TikTok Song Trends Analyzer")
# Main tabs
with gr.Tabs():
# Song trends tab
with gr.TabItem("Song Trends"):
with gr.Row():
region_dropdown = gr.Dropdown(
choices=["美国", "英国", "韩国", "日本"],
#choices=["All Regions", "US", "UK", "JP", "KR", "CN", "IN", "BR", "FR", "DE", "ES"],
value="美国",
label="Region"
)
time_period = gr.Dropdown(
choices=["过去7天", "过去30天"],
value="过去7天",
label="Time Period"
)
# Song list container
song_list = gr.HTML(create_song_list(ALL_SONGS))
# Update song list when filters change
def update_song_list(region, time_period):
filtered_songs = filter_songs(region, time_period)
return create_song_list(filtered_songs)
region_dropdown.change(
fn=update_song_list,
inputs=[region_dropdown, time_period],
outputs=song_list
)
time_period.change(
fn=update_song_list,
inputs=[region_dropdown, time_period],
outputs=song_list
)
# Song analysis tab
with gr.TabItem("Song Analysis"):
with gr.Row():
with gr.Column(scale=2):
song_name = gr.Dropdown(
choices=list(map(lambda x: x["title"], ALL_SONGS)),
label="Select a Song",
interactive=True
)
analysis_btn = gr.Button("分析歌曲")
song_description = gr.Markdown(label="Song Description")
suno_prompt = gr.Textbox(label="Suno Prompt", interactive=False, lines=5)
# with gr.Column(scale=2):
# suno_prompt = gr.Textbox(label="Suno Prompt", interactive=False, lines=5)
# generate_btn = gr.Button("生成相似歌曲")
# Audio player for generated song
audio_player = gr.Audio(label="Generated Song", type="filepath", interactive=False)
# Handle song analysis
def do_song_analysis(song_name):
if not song_name:
return "", "", "", "Please select a song from the trends list."
analysis_result = analyze_song(song_name)
return analysis_result["song_description"], analysis_result["suno_prompt"], f'media/{song_name}.mp3'
# Handle similar song generation
def do_generate_similar(suno_prompt):
if not suno_prompt:
return None, "Please analyze a song first to get a Suno prompt."
result = generate_similar_song(suno_prompt)
if result["status"] == "success":
return result["audio_url"]
else:
return None
# generate_btn.click(
# fn=do_generate_similar,
# inputs=suno_prompt,
# outputs=[audio_player]
# )
analysis_btn.click(
fn=do_song_analysis,
inputs=song_name,
outputs=[song_description, suno_prompt, audio_player]
)
# Launch the app
demo.launch()
if __name__ == "__main__":
app()