142 lines
4.8 KiB
Python
142 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Generate repo assets (README, promo text, tracklist) using Ollama.
|
||
|
||
Input:
|
||
- teaser_report.json from dj_teaser_v3.py
|
||
|
||
Output:
|
||
- README.md
|
||
- PROMO.txt
|
||
- TRACKLIST.md
|
||
|
||
Requirements:
|
||
pip install requests
|
||
"""
|
||
|
||
import argparse
|
||
import json
|
||
from pathlib import Path
|
||
from typing import Dict, Any, List
|
||
|
||
import requests
|
||
|
||
|
||
def ollama_generate(base_url: str, model: str, prompt: str) -> str:
|
||
url = base_url.rstrip("/") + "/api/generate"
|
||
payload = {"model": model, "prompt": prompt, "stream": False}
|
||
r = requests.post(url, json=payload, timeout=120)
|
||
r.raise_for_status()
|
||
return r.json().get("response", "").strip()
|
||
|
||
|
||
def format_timestamps(tracks: List[Dict[str, Any]], crossfade_seconds: float) -> List[Dict[str, Any]]:
|
||
"""
|
||
Approximate teaser timestamps by accumulating clip durations minus crossfades.
|
||
timestamp[i] = sum(durs[0..i-1]) - i*crossfade
|
||
"""
|
||
out = []
|
||
t = 0.0
|
||
for i, tr in enumerate(tracks):
|
||
out.append({
|
||
**tr,
|
||
"teaser_timestamp_seconds": round(t, 2)
|
||
})
|
||
dur = float(tr.get("clip_duration_seconds", 0.0))
|
||
t += max(0.0, dur - crossfade_seconds)
|
||
return out
|
||
|
||
|
||
def seconds_to_mmss(sec: float) -> str:
|
||
sec = max(0.0, float(sec))
|
||
m = int(sec // 60)
|
||
s = int(round(sec - (m * 60)))
|
||
return f"{m:02d}:{s:02d}"
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser(description="Generate README/promo/tracklist via Ollama")
|
||
ap.add_argument("--report", default="./out/teaser_report.json", help="Path to teaser_report.json")
|
||
ap.add_argument("--out-dir", default="./out", help="Output directory for generated assets")
|
||
ap.add_argument("--ollama", default="http://192.168.2.60:11434", help="Ollama base URL")
|
||
ap.add_argument("--model", default="llama3.1:8b-instruct-q4_0", help="Ollama model name")
|
||
ap.add_argument("--project-name", default="DJ Teaser Builder", help="Project/repo name")
|
||
ap.add_argument("--artist", default="DjGulvBasS", help="Artist/DJ name")
|
||
ap.add_argument("--genre", default="old school trance", help="Genre")
|
||
args = ap.parse_args()
|
||
|
||
report_path = Path(args.report)
|
||
out_dir = Path(args.out_dir)
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
data = json.loads(report_path.read_text(encoding="utf-8"))
|
||
tracks = data.get("tracks", [])
|
||
settings = data.get("settings", {})
|
||
|
||
crossfade = float(settings.get("crossfade_seconds", 0.25))
|
||
tracks_ts = format_timestamps(tracks, crossfade_seconds=crossfade)
|
||
|
||
# Build TRACKLIST.md ourselves (deterministic)
|
||
lines = [f"# Tracklist (approx.) — {args.artist}\n"]
|
||
for tr in tracks_ts:
|
||
ts = seconds_to_mmss(tr["teaser_timestamp_seconds"])
|
||
fname = tr.get("filename", "Unknown")
|
||
bpm = tr.get("tempo_bpm_est", "?")
|
||
camelot = tr.get("camelot", "??")
|
||
key = tr.get("key", "")
|
||
lines.append(f"- **{ts}** — {fname} _(BPM ~ {bpm}, {camelot}, {key})_")
|
||
(out_dir / "TRACKLIST.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||
|
||
# README prompt
|
||
readme_prompt = f"""
|
||
You are writing a GitHub README in English for a small local audio tool.
|
||
|
||
Project: {args.project_name}
|
||
Artist use-case: {args.artist} — {args.genre}
|
||
The tool scans a folder of tracks and builds a DJ-style teaser by:
|
||
- detecting highlight segments
|
||
- snapping cuts to bar grid (DJ phrasing)
|
||
- optional harmonic ordering using Camelot keys
|
||
- rendering clips and acrossfading them with FFmpeg
|
||
- exporting WAV + MP3
|
||
It produces a JSON report and a tracklist with timestamps.
|
||
|
||
Please write a README with these sections:
|
||
1) What it does
|
||
2) Requirements (ffmpeg + Python)
|
||
3) Install (venv)
|
||
4) Usage examples (include: select all, select by indices, auto best-of)
|
||
5) Trance/DJ tips (avoid-intro, bars, preroll-bars, harmonic)
|
||
6) Troubleshooting (ffmpeg not found, weird beat detection, key detection limitations)
|
||
Keep it concise and practical.
|
||
|
||
These settings were used in an example run:
|
||
{json.dumps(settings, indent=2)}
|
||
|
||
Do NOT invent features beyond what is described.
|
||
"""
|
||
|
||
readme_text = ollama_generate(args.ollama, args.model, readme_prompt)
|
||
(out_dir / "README.md").write_text(readme_text + "\n", encoding="utf-8")
|
||
|
||
# Promo prompt
|
||
promo_prompt = f"""
|
||
Write 3 short promo text variants (English) for a DJ album teaser for {args.artist} ({args.genre}).
|
||
Constraints:
|
||
- Each variant should be 2–4 lines max
|
||
- Include 4–8 hashtags (trance/electronic)
|
||
- Tone: energetic, DJ/club vibe
|
||
- Do not mention "AI" or "tool" or "script"
|
||
- Do not include any URLs
|
||
"""
|
||
promo_text = ollama_generate(args.ollama, args.model, promo_prompt)
|
||
(out_dir / "PROMO.txt").write_text(promo_text + "\n", encoding="utf-8")
|
||
|
||
print(f"✅ Generated: {out_dir / 'README.md'}")
|
||
print(f"✅ Generated: {out_dir / 'TRACKLIST.md'}")
|
||
print(f"✅ Generated: {out_dir / 'PROMO.txt'}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|