MusicVisionAI
MUSICVISIONAI·CONCEPT
Tamusique.Tonhistoire.
Transformeunechansonenhistoirecinématographique.
🎭Tonpersonnage
Taphotosertderéférencevisuellepourlepersonnage
principaldesscènes.
🎵Tamusique
Enattentedelamusique...
📝Paroles
🎬Créerlestoryboard
Enattente...
Wan2.1prêt.
/*=====================================================
IMPORTGRADIO
=====================================================*/
import{
Client,
handle_file
}from"https://cdn.jsdelivr.net/npm/@gradio/client/+esm";
/*=====================================================
SPACES
=====================================================*/
constWHISPER_SPACE=
"hf-audio/whisper-large-v3";
constWAN_SPACE=
"Wan-AI/Wan2.1";
/*=====================================================
ELEMENTS
=====================================================*/
constphotoInput=
document.getElementById("photo");
constphotoPreview=
document.getElementById("photoPreview");
constaudioInput=
document.getElementById("audio");
constyoutubeInput=
document.getElementById("youtube");
constlyricsInput=
document.getElementById("lyrics");
consttranscribeBtn=
document.getElementById("transcribeBtn");
consttranscribeStatus=
document.getElementById("transcribeStatus");
conststoryboardBtn=
document.getElementById("storyboardBtn");
conststoryboardStatus=
document.getElementById("storyboardStatus");
conststoryboard=
document.getElementById("storyboard");
constscenesContainer=
document.getElementById("scenes");
constgenerateSceneBtn=
document.getElementById("generateSceneBtn");
constwanStatus=
document.getElementById("wanStatus");
constvideoContainer=
document.getElementById("videoContainer");
/*=====================================================
STATUS
=====================================================*/
functionsetStatus(
element,
text,
loading=false
){
element.textContent=
text;
element.classList.toggle(
"loading",
loading
);
}
/*=====================================================
PHOTOPREVIEW
=====================================================*/
photoInput.addEventListener(
"change",
()=>{
constfile=
photoInput.files[0];
if(!file){
photoPreview.classList.add(
"hidden"
);
return;
}
photoPreview.src=
URL.createObjectURL(file);
photoPreview.classList.remove(
"hidden"
);
}
);
/*=====================================================
TRANSCRIPTION
=====================================================*/
functionextractText(
value
){
if(!value){
return"";
}
if(
typeofvalue==="string"
){
returnvalue;
}
if(
Array.isArray(value)
){
for(
constitemofvalue
){
constresult=
extractText(item);
if(result){
returnresult;
}
}
}
if(
typeofvalue==="object"
){
if(
typeofvalue.text==="string"
){
returnvalue.text;
}
if(
typeofvalue.value==="string"
){
returnvalue.value;
}
if(
typeofvalue.data==="string"
){
returnvalue.data;
}
}
return"";
}
transcribeBtn.addEventListener(
"click",
async()=>{
constaudio=
audioInput.files[0];
constyoutube=
youtubeInput.value.trim();
if(!audio&&!youtube){
setStatus(
transcribeStatus,
"⚠️AjouteunfichieraudioouunlienYouTube."
);
return;
}
transcribeBtn.disabled=
true;
try{
setStatus(
transcribeStatus,
"🔌ConnexionàWhisper...",
true
);
constwhisper=
awaitClient.connect(
WHISPER_SPACE
);
letresult;
if(audio){
setStatus(
transcribeStatus,
"🎙️Transcriptiondelamusique...",
true
);
constfile=
awaithandle_file(
audio
);
result=
awaitwhisper.predict(
"/transcribe",
[
file,
"transcribe"
]
);
}else{
setStatus(
transcribeStatus,
"📺TranscriptionYouTube...",
true
);
result=
awaitwhisper.predict(
"/yt_transcribe",
[
youtube,
"transcribe"
]
);
}
consttext=
extractText(
result
);
if(!text){
thrownewError(
"Whispern'aretournéaucuntexte."
);
}
lyricsInput.value=
text;
setStatus(
transcribeStatus,
"✅Transcriptionterminée."
);
}
catch(error){
console.error(
"WHISPERERROR",
error
);
setStatus(
transcribeStatus,
"❌ErreurWhisper:\n\n"+
error.message
);
}
finally{
transcribeBtn.disabled=
false;
}
}
);
/*=====================================================
STORYBOARD
=====================================================*/
storyboardBtn.addEventListener(
"click",
()=>{
constlyrics=
lyricsInput.value.trim();
if(!lyrics){
setStatus(
storyboardStatus,
"⚠️Ajouted'abordlesparoles."
);
return;
}
createStoryboard(
lyrics
);
storyboard.style.display=
"block";
setStatus(
storyboardStatus,
"✅Storyboardcréé."
);
}
);
functioncreateStoryboard(
lyrics
){
scenesContainer.innerHTML=
"";
constparts=
lyrics
.split(/[.!?\n]+/)
.map(
x=>x.trim()
)
.filter(Boolean);
if(!parts.length){
addScene(
"INTRO",
"Introductioncinématographique."
);
return;
}
consttotal=
Math.min(
parts.length,
6
);
for(
leti=0;
i
${escapeHTML(title)}
${escapeHTML(description)}
`;
scenesContainer.appendChild(
scene
);
}
functionescapeHTML(
text
){
returnString(text)
.replace(
/&/g,
"&"
)
.replace(
//g,
">"
)
.replace(
/"/g,
"""
)
.replace(
/'/g,
"'"
);
}
/*=====================================================
WAN:EXTRACTIONTASKID
=====================================================*/
/*
Leendpointofficielrenvoie:
[task_id,
status,
button,
refresh_button,
cost_time,
waiting_time]
Lepremierélémentestdoncceluiquinousintéresse.
*/
functionextractWanTaskId(
message
){
if(!message){
return"";
}
console.log(
"WANMESSAGE:",
message
);
/*
Cas:
{
type:"data",
data:[...]
}
*/
if(
message.type==="data"&&
Array.isArray(message.data)
){
constdata=
message.data;
/*
Lepremierrésultatdoitêtre
leStatei2v_task_id.
*/
constfirst=
data[0];
if(
typeoffirst==="string"&&
first.trim()
){
returnfirst.trim();
}
/*
Certainsretourspeuventemballer
lavaleur.
*/
if(
first&&
typeoffirst==="object"
){
if(
first.task_id
){
returnString(
first.task_id
);
}
if(
first.value
){
returnString(
first.value
);
}
}
}
return"";
}
/*=====================================================
WAN:URLVIDÉO
=====================================================*/
functionextractVideo(
value
){
if(!value){
return"";
}
if(
typeofvalue==="string"
){
if(
value.startsWith("http://")||
value.startsWith("https://")
){
returnvalue;
}
return"";
}
if(
Array.isArray(value)
){
for(
constitemofvalue
){
constresult=
extractVideo(item);
if(result){
returnresult;
}
}
}
if(
typeofvalue==="object"
){
constcandidates=[
value.url,
value.video_url,
value.path,
value.value,
value.data
];
for(
constcandidate
ofcandidates
){
constresult=
extractVideo(
candidate
);
if(result){
returnresult;
}
}
}
return"";
}
/*=====================================================
GÉNÉRATIONWAN
=====================================================*/
generateSceneBtn.addEventListener(
"click",
generateScene
);
asyncfunctiongenerateScene(){
constphoto=
photoInput.files[0];
if(!photo){
setStatus(
wanStatus,
"⚠️Ajouted'abordunephoto."
);
return;
}
generateSceneBtn.disabled=
true;
videoContainer.innerHTML=
"";
try{
setStatus(
wanStatus,
"🔌ConnexionàWan2.1...",
true
);
constwan=
awaitClient.connect(
WAN_SPACE
);
/*
Onrécupèrelesinformations
del'APIréelle.
*/
letapiInfo=null;
try{
apiInfo=
awaitwan.view_api();
console.log(
"WANAPI:",
apiInfo
);
}catch(e){
console.warn(
"Impossibledelireview_api()",
e
);
}
setStatus(
wanStatus,
"📸Envoidelaphoto...",
true
);
constimage=
awaithandle_file(
photo
);
/*
====================================================
PROMPTCINÉMATOGRAPHIQUE
====================================================
*/
constprompt=`
Createarealisticcinematicmusicvideoscene.
Theuploadedimageisthevisualidentityreference
forthemainmaleprotagonist.
IMPORTANT:
Theprotagonistisanactorinsidethestory.
HeisNOTthesinger.
HedoesNOTlip-sync.
HedoesNOTperformthesong.
Preservehisidentityandfacialappearance.
Story:
Theprotagonistisaloneatnightafteranemotional
separation.
Heslowlywalksthroughaquietcitystreet.
Helooksthoughtfulandemotionallyaffected.
Hetakeshisphonefromhispocketandlooksatit.
Hecontinueswalkingalone.
Emotion:
loneliness,sadness,introspection.
Visualstyle:
photorealistic,
cinematicmusicvideo,
realistichumanmovement,
naturalbodylanguage,
naturalfacialexpression,
cinematiccameramovement,
realisticnightlighting,
detailedenvironment,
professionalcinematography.
Donotchangetheidentityofthecharacter.
Donotcreateanotherperson.
Donotdistorttheface.
Verticalcinematiccomposition.
`.trim();
setStatus(
wanStatus,
"🎬EnvoidelascèneàWan2.1...",
true
);
/*
====================================================
APPELDUVRAIENDPOINTASYNC
====================================================
*/
constjob=
wan.submit(
"/i2v_generation_async",
[
prompt,
image,
true,
-1
]
);
lettaskId=
"";
letfinalMessage=
null;
/*
====================================================
LECTUREDESÉVÉNEMENTS
====================================================
*/
forawait(
constmessageofjob
){
console.log(
"WANEVENT:",
message
);
/*
-------------------------------
STATUSGRADIO
-------------------------------
*/
if(
message.type==="status"
){
console.log(
"WANSTATUS:",
message
);
if(
message.stage==="error"
){
thrownewError(
message.message||
"Wan2.1aretournéuneerreur."
);
}
setStatus(
wanStatus,
"⏳Wan2.1traitelademande...\n\n"+
(
message.stage||
"processing"
),
true
);
}
/*
-------------------------------
DATAGRADIO
-------------------------------
*/
if(
message.type==="data"
){
finalMessage=
message;
constid=
extractWanTaskId(
message
);
if(id){
taskId=
id;
break;
}
}
}
/*
====================================================
SIPASDETASKID
====================================================
*/
if(!taskId){
console.error(
"DERNIERMESSAGEWAN:",
finalMessage
);
/*
IMPORTANT:
Sileserveuraréellementrefusé
latâche,lepremierrésultatestvide.
*/
thrownewError(
"Wan2.1n'apasfournidetask_id.\n\n"+
"LeSpaceaprobablementrefusélatâche"+
"oun'apastransmislasortieState."
);
}
console.log(
"TASKIDWAN:",
taskId
);
setStatus(
wanStatus,
"⏳Vidéoengénération...\n\n"+
"TaskID:"+
taskId,
true
);
/*
====================================================
ATTENTEDURÉSULTAT
====================================================
*/
awaitwaitForWanVideo(
wan,
taskId
);
}
catch(error){
console.error(
"WANERROR:",
error
);
setStatus(
wanStatus,
"❌Générationimpossible.\n\n"+
error.message
);
}
finally{
generateSceneBtn.disabled=
false;
}
}
/*=====================================================
ATTENTEVIDÉO
=====================================================*/
asyncfunctionwaitForWanVideo(
wan,
taskId
){
constmax=
120;
for(
letattempt=1;
attempt<=max;
attempt++
){
setStatus(
wanStatus,
"⏳Wan2.1génèrelascène...\n\n"+
"Vérification:"+
attempt+
"/"+
max,
true
);
letresult=
null;
try{
/*
LecodeofficielduSpaceappelle
status_refreshavec:
task_id
task
status
*/
result=
awaitwan.predict(
"/status_refresh",
[
taskId,
"i2v",
false
]
);
console.log(
"WANREFRESH:",
result
);
constvideo=
extractVideo(
result
);
if(video){
displayVideo(
video
);
setStatus(
wanStatus,
"✅Scènegénéréeavecsuccès."
);
return;
}
}
catch(error){
console.warn(
"STATUSREFRESHERROR",
error
);
}
awaitsleep(
5000
);
}
thrownewError(
"Lagénérationadépasséledélaid'attente."
);
}
/*=====================================================
AFFICHAGEVIDÉO
=====================================================*/
functiondisplayVideo(
url
){
videoContainer.innerHTML=
"";
consttitle=
document.createElement(
"div"
);
title.style.marginTop=
"20px";
title.style.fontWeight=
"bold";
title.textContent=
"🎥Premièrescènegénérée";
constvideo=
document.createElement(
"video"
);
video.controls=
true;
video.playsInline=
true;
video.preload=
"metadata";
video.src=
url;
videoContainer.appendChild(
title
);
videoContainer.appendChild(
video
);
}
/*=====================================================
SLEEP
=====================================================*/
functionsleep(
milliseconds
){
returnnewPromise(
resolve=>
setTimeout(
resolve,
milliseconds
)
);
}
console.log(
"MusicVisionAIchargé—Wan2.1async."
);