File size: 3,586 Bytes
b89b569
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

import gradio as gr
import os
import shutil
import uuid
import subprocess
from threading import Timer

from gradio_motioncanvasplayer import MotionCanvasPlayer

example_project_path = "https://prathje-gradio-motioncanvasplayer.hf.space/gradio_api/file=/home/user/app/public/project-3.17.2.js"

loading_project_path = ""
failed_project_path = ""

BUILD_TIMEOUT = 60

gr.set_static_paths(paths=[os.path.join(os.path.dirname(__file__), "public")])

def get_local_path(project_id):
    return os.path.join(os.path.dirname(__file__), "public", "project-" + project_id + ".js")

def get_public_path(project_id):
    return "/gradio_api/file=" + get_local_path(project_id)


def build_project(code):
    # TODO: as soon as gradio supports states, we should keep the project_id for some time...
    yield loading_project_path, "Preparing project...", 

    # generate a random uuid for the project
    project_id = str(uuid.uuid4())

    tmp_dir = os.path.join("/tmp/", project_id)

    shutil.copytree(os.environ['MC_PROJECT_DIR'], tmp_dir, dirs_exist_ok=False)
    acc_logs = ""
    
    try:
        yield loading_project_path, "Building project...", 

        process = subprocess.Popen(
            "npm install && npm run build",
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            shell=True,
            cwd=tmp_dir
        )

        timer = Timer(BUILD_TIMEOUT, process.kill)
        timer.start()
        
        while True:
            line = process.stdout.readline()
            if line:
                acc_logs += line.rstrip() + "\n"
                yield loading_project_path, acc_logs
            elif process.poll() is not None:
                break
        timer.cancel()
        
        # Check for errors
        stderr_output = process.stderr.read()
        if stderr_output:
            acc_logs += "\n" + "Error building project: " + stderr_output
            
        # check if the build was successful
        if process.returncode != 0:
            yield failed_project_path, acc_logs
        else:
            # copy dist/project.js to get_local_path(id)
            shutil.copy(os.path.join(tmp_dir, "dist", "project.js"), get_local_path(project_id))
            yield get_public_path(project_id), acc_logs       

    except Exception as e:
        yield failed_project_path, acc_logs + "\n" + "Error building project: " + str(e)
        
    finally:
         # cleanup tmp dir
        shutil.rmtree(tmp_dir)

    

    # generate a random uuid for the project
    # copy whole MC_PROJECT_DIR to a random tmp dir
    # write code to scene.ts
    # run npm run build
    # return [logs, project_api_path]
    # if successful copy MC_PROJECT_DIR/dist/project.js to get_local_path(id)
    # always: log the output to logs
    # always delete the tmp dir after the build is done

    

with gr.Blocks() as demo:
    gr.Markdown("# Motion Canvas MCP Server")
    with gr.Row():
        with gr.Column():
            gr.Markdown("## TS Input for scene.ts")
            code = gr.Code(language="typescript")
            submit = gr.Button("Build")
            logs = gr.Textbox(value="", label="Build Logs", interactive=False)
        with gr.Column():
            gr.Markdown("## Preview")
            player = MotionCanvasPlayer(example_project_path, auto=True, quality=0.5, width=1920, height=1080, variables="{}")
            

    submit.click(build_project, inputs=[code], outputs=[player, logs], api_name="build_project")

if __name__ == "__main__":
    demo.launch(mcp_server=True, strict_cors=False)