arcacolab commited on
Commit
6ca9461
ยท
verified ยท
1 Parent(s): cb309be

Upload 2 files

Browse files
Files changed (2) hide show
  1. gradio-tunnel.py +210 -0
  2. runcomfy.py +397 -0
gradio-tunnel.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import atexit
2
+ import os
3
+ import platform
4
+ import re
5
+ import stat
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+ from typing import List, Optional
11
+
12
+ import requests
13
+
14
+ VERSION = "0.2"
15
+ CURRENT_TUNNELS: List["Tunnel"] = []
16
+
17
+ machine = platform.machine()
18
+ if machine == "x86_64":
19
+ machine = "amd64"
20
+
21
+ BINARY_REMOTE_NAME = f"frpc_{platform.system().lower()}_{machine.lower()}"
22
+ EXTENSION = ".exe" if os.name == "nt" else ""
23
+ BINARY_URL = f"https://cdn-media.huggingface.co/frpc-gradio-{VERSION}/{BINARY_REMOTE_NAME}{EXTENSION}"
24
+
25
+ BINARY_FILENAME = f"{BINARY_REMOTE_NAME}_v{VERSION}"
26
+ BINARY_FOLDER = Path(__file__).parent.absolute()
27
+ BINARY_PATH = f"{BINARY_FOLDER / BINARY_FILENAME}"
28
+
29
+ TUNNEL_TIMEOUT_SECONDS = 30
30
+ TUNNEL_ERROR_MESSAGE = (
31
+ "Could not create share URL. "
32
+ "Please check the appended log from frpc for more information:"
33
+ )
34
+
35
+ GRADIO_API_SERVER = "https://api.gradio.app/v2/tunnel-request"
36
+ GRADIO_SHARE_SERVER_ADDRESS = None
37
+
38
+
39
+ class Tunnel:
40
+ def __init__(self, remote_host, remote_port, local_host, local_port, share_token):
41
+ self.proc = None
42
+ self.url = None
43
+ self.remote_host = remote_host
44
+ self.remote_port = remote_port
45
+ self.local_host = local_host
46
+ self.local_port = local_port
47
+ self.share_token = share_token
48
+
49
+ @staticmethod
50
+ def download_binary():
51
+ if not Path(BINARY_PATH).exists():
52
+ resp = requests.get(BINARY_URL)
53
+
54
+ if resp.status_code == 403:
55
+ raise OSError(
56
+ f"Cannot set up a share link as this platform is incompatible. Please "
57
+ f"create a GitHub issue with information about your platform: {platform.uname()}"
58
+ )
59
+
60
+ resp.raise_for_status()
61
+
62
+ # Save file data to local copy
63
+ with open(BINARY_PATH, "wb") as file:
64
+ file.write(resp.content)
65
+ st = os.stat(BINARY_PATH)
66
+ os.chmod(BINARY_PATH, st.st_mode | stat.S_IEXEC)
67
+
68
+ def start_tunnel(self) -> str:
69
+ self.download_binary()
70
+ self.url = self._start_tunnel(BINARY_PATH)
71
+ return self.url
72
+
73
+ def kill(self):
74
+ if self.proc is not None:
75
+ print(f"Killing tunnel {self.local_host}:{self.local_port} <> {self.url}")
76
+ self.proc.terminate()
77
+ self.proc = None
78
+
79
+ def _start_tunnel(self, binary: str) -> str:
80
+ CURRENT_TUNNELS.append(self)
81
+ command = [
82
+ binary,
83
+ "http",
84
+ "-n",
85
+ self.share_token,
86
+ "-l",
87
+ str(self.local_port),
88
+ "-i",
89
+ self.local_host,
90
+ "--uc",
91
+ "--sd",
92
+ "random",
93
+ "--ue",
94
+ "--server_addr",
95
+ f"{self.remote_host}:{self.remote_port}",
96
+ "--disable_log_color",
97
+ ]
98
+ self.proc = subprocess.Popen(
99
+ command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
100
+ )
101
+ atexit.register(self.kill)
102
+ return self._read_url_from_tunnel_stream()
103
+
104
+ def _read_url_from_tunnel_stream(self) -> str:
105
+ start_timestamp = time.time()
106
+
107
+ log = []
108
+ url = ""
109
+
110
+ def _raise_tunnel_error():
111
+ log_text = "\n".join(log)
112
+ print(log_text, file=sys.stderr)
113
+ raise ValueError(f"{TUNNEL_ERROR_MESSAGE}\n{log_text}")
114
+
115
+ while url == "":
116
+ # check for timeout and log
117
+ if time.time() - start_timestamp >= TUNNEL_TIMEOUT_SECONDS:
118
+ _raise_tunnel_error()
119
+
120
+ assert self.proc is not None
121
+ if self.proc.stdout is None:
122
+ continue
123
+
124
+ line = self.proc.stdout.readline()
125
+ line = line.decode("utf-8")
126
+
127
+ if line == "":
128
+ continue
129
+
130
+ log.append(line.strip())
131
+
132
+ if "start proxy success" in line:
133
+ result = re.search("start proxy success: (.+)\n", line)
134
+ if result is None:
135
+ _raise_tunnel_error()
136
+ else:
137
+ url = result.group(1)
138
+ elif "login to server failed" in line:
139
+ _raise_tunnel_error()
140
+
141
+ return url
142
+
143
+
144
+ def setup_tunnel(
145
+ local_host: str,
146
+ local_port: int,
147
+ share_token: str,
148
+ share_server_address: Optional[str],
149
+ ) -> str:
150
+ share_server_address = (
151
+ GRADIO_SHARE_SERVER_ADDRESS
152
+ if share_server_address is None
153
+ else share_server_address
154
+ )
155
+ if share_server_address is None:
156
+ response = requests.get(GRADIO_API_SERVER)
157
+ if not (response and response.status_code == 200):
158
+ raise RuntimeError("Could not get share link from Gradio API Server.")
159
+ payload = response.json()[0]
160
+ remote_host, remote_port = payload["host"], int(payload["port"])
161
+ else:
162
+ remote_host, remote_port = share_server_address.split(":")
163
+ remote_port = int(remote_port)
164
+ try:
165
+ tunnel = Tunnel(remote_host, remote_port, local_host, local_port, share_token)
166
+ address = tunnel.start_tunnel()
167
+ return address
168
+ except Exception as e:
169
+ raise RuntimeError(str(e)) from e
170
+
171
+
172
+ def main():
173
+ import argparse
174
+ import secrets
175
+ import time
176
+
177
+ parser = argparse.ArgumentParser(description="Set up a tunnel.")
178
+ parser.add_argument(
179
+ "-p",
180
+ "--port",
181
+ type=int,
182
+ default=8080,
183
+ help="the port number to use for the tunnel.",
184
+ )
185
+ parser.add_argument(
186
+ "port_positional",
187
+ type=int,
188
+ nargs="?",
189
+ help="the port number to use for the tunnel.",
190
+ )
191
+ parser.add_argument(
192
+ "--sd", "--subdomain", type=str, help="the subdomain to use for the tunnel."
193
+ )
194
+ args = parser.parse_args()
195
+
196
+ if args.port_positional is not None:
197
+ args.port = args.port_positional
198
+
199
+ address = setup_tunnel(
200
+ "127.0.0.1",
201
+ args.port,
202
+ secrets.token_urlsafe(32) if args.sd is None else args.sd,
203
+ None,
204
+ )
205
+
206
+ print(address, flush=True)
207
+ time.sleep(3600 * 24 * 3)
208
+
209
+ if __name__ == "__main__":
210
+ main()
runcomfy.py ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import subprocess
4
+ import time
5
+ import atexit
6
+ import sys
7
+ import re
8
+ import argparse
9
+ import shlex
10
+ import io
11
+ import select
12
+
13
+ # --- ์„ค์ • ๋ณ€์ˆ˜ ---
14
+ COMFYUI_PORT = 8188
15
+ # ํ„ฐ๋„ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ (ComfyUI ๋””๋ ‰ํ† ๋ฆฌ ๋ฐ–, ์ฆ‰ ๋ถ€๋ชจ ๋””๋ ‰ํ† ๋ฆฌ)
16
+ TUNNEL_SCRIPT_PATH = "../gradio-tunnel.py"
17
+
18
+ # Gradio Tunnel Script ๋‚ด์šฉ (์ด์ „ ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ)
19
+ GRADIO_TUNNEL_SCRIPT_CONTENT = r"""
20
+ import atexit
21
+ import os
22
+ import platform
23
+ import re
24
+ import stat
25
+ import subprocess
26
+ import sys
27
+ import time
28
+ import secrets
29
+ from pathlib import Path
30
+ from typing import List, Optional
31
+
32
+ import requests
33
+
34
+ VERSION = "0.2"
35
+ CURRENT_TUNNELS: List["Tunnel"] = []
36
+
37
+ machine = platform.machine()
38
+ if machine == "x86_64":
39
+ machine = "amd64"
40
+
41
+ BINARY_REMOTE_NAME = f"frpc_{platform.system().lower()}_{machine.lower()}"
42
+ EXTENSION = ".exe" if os.name == "nt" else ""
43
+ BINARY_URL = f"https://cdn-media.huggingface.co/frpc-gradio-{VERSION}/{BINARY_REMOTE_NAME}{EXTENSION}"
44
+
45
+ BINARY_FILENAME = f"{BINARY_REMOTE_NAME}_v{VERSION}"
46
+ BINARY_FOLDER = Path(__file__).parent.absolute()
47
+ BINARY_PATH = f"{BINARY_FOLDER / BINARY_FILENAME}"
48
+
49
+ TUNNEL_TIMEOUT_SECONDS = 30
50
+ TUNNEL_ERROR_MESSAGE = (
51
+ "Could not create share URL. "
52
+ "Please check the appended log from frpc for more information:"
53
+ )
54
+
55
+ GRADIO_API_SERVER = "https://api.gradio.app/v2/tunnel-request"
56
+ GRADIO_SHARE_SERVER_ADDRESS = None
57
+
58
+
59
+ class Tunnel:
60
+ def __init__(self, remote_host, remote_port, local_host, local_port, share_token):
61
+ self.proc = None
62
+ self.url = None
63
+ self.remote_host = remote_host
64
+ self.remote_port = remote_port
65
+ self.local_host = local_host
66
+ self.local_port = local_port
67
+ self.share_token = share_token
68
+
69
+ @staticmethod
70
+ def download_binary():
71
+ if not Path(BINARY_PATH).exists():
72
+ resp = requests.get(BINARY_URL)
73
+
74
+ if resp.status_code == 403:
75
+ raise OSError(
76
+ f"Cannot set up a share link as this platform is incompatible. Please "
77
+ f"create a GitHub issue with information about your platform: {platform.uname()}"
78
+ )
79
+
80
+ resp.raise_for_status()
81
+
82
+ # Save file data to local copy
83
+ with open(BINARY_PATH, "wb") as file:
84
+ file.write(resp.content)
85
+ st = os.stat(BINARY_PATH)
86
+ os.chmod(BINARY_PATH, st.st_mode | stat.S_IEXEC)
87
+
88
+ def start_tunnel(self) -> str:
89
+ self.download_binary()
90
+ self.url = self._start_tunnel(BINARY_PATH)
91
+ return self.url
92
+
93
+ def kill(self):
94
+ if self.proc is not None:
95
+ print(f"ํ„ฐ๋„ ์ข…๋ฃŒ ์ค‘: {self.local_host}:{self.local_port} <> {self.url}")
96
+ self.proc.terminate()
97
+ self.proc = None
98
+
99
+ def _start_tunnel(self, binary: str) -> str:
100
+ CURRENT_TUNNELS.append(self)
101
+ command = [
102
+ binary,
103
+ "http",
104
+ "-n",
105
+ self.share_token,
106
+ "-l",
107
+ str(self.local_port),
108
+ "-i",
109
+ self.local_host,
110
+ "--uc",
111
+ "--sd",
112
+ "random",
113
+ "--ue",
114
+ "--server_addr",
115
+ f"{self.remote_host}:{self.remote_port}",
116
+ "--disable_log_color",
117
+ ]
118
+ self.proc = subprocess.Popen(
119
+ command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
120
+ )
121
+ atexit.register(self.kill)
122
+ return self._read_url_from_tunnel_stream()
123
+
124
+ def _read_url_from_tunnel_stream(self) -> str:
125
+ start_timestamp = time.time()
126
+
127
+ log = []
128
+ url = ""
129
+
130
+ def _raise_tunnel_error():
131
+ log_text = "\\n".join(log)
132
+ print(log_text, file=sys.stderr)
133
+ raise ValueError(f"{TUNNEL_ERROR_MESSAGE}\\n{log_text}")
134
+
135
+ while url == "":
136
+ if time.time() - start_timestamp >= TUNNEL_TIMEOUT_SECONDS:
137
+ _raise_tunnel_error()
138
+
139
+ assert self.proc is not None
140
+ if self.proc.stdout is None:
141
+ continue
142
+
143
+ line = self.proc.stdout.readline()
144
+ try:
145
+ line = line.decode("utf-8")
146
+ except UnicodeDecodeError:
147
+ continue
148
+
149
+ if line == "":
150
+ time.sleep(0.01)
151
+ continue
152
+
153
+ log.append(line.strip())
154
+
155
+ if "start proxy success" in line:
156
+ result = re.search("start proxy success: (.+)\\n", line)
157
+ if result is None:
158
+ _raise_tunnel_error()
159
+ else:
160
+ url = result.group(1)
161
+ elif "login to server failed" in line:
162
+ _raise_tunnel_error()
163
+
164
+ if self.proc.poll() is not None and url == "":
165
+ _raise_tunnel_error()
166
+
167
+ return url
168
+
169
+
170
+ def setup_tunnel(
171
+ local_host: str,
172
+ local_port: int,
173
+ share_token: str,
174
+ share_server_address: Optional[str],
175
+ ) -> str:
176
+ share_server_address = (
177
+ GRADIO_SHARE_SERVER_ADDRESS
178
+ if share_server_address is None
179
+ else share_server_address
180
+ )
181
+ if share_server_address is None:
182
+ response = requests.get(GRADIO_API_SERVER)
183
+ if not (response and response.status_code == 200):
184
+ raise RuntimeError("Could not get share link from Gradio API Server.")
185
+ payload = response.json()[0]
186
+ remote_host, remote_port = payload["host"], int(payload["port"])
187
+ else:
188
+ remote_host, remote_port = share_server_address.split(":")
189
+ remote_port = int(remote_port)
190
+ try:
191
+ tunnel = Tunnel(remote_host, remote_port, local_host, local_port, share_token)
192
+ address = tunnel.start_tunnel()
193
+ return address
194
+ except Exception as e:
195
+ raise RuntimeError(str(e)) from e
196
+
197
+ # ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ Gradio ํ„ฐ๋„๋ง์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
198
+ if __name__ == "__main__":
199
+ if len(sys.argv) < 2:
200
+ print("์‚ฌ์šฉ๋ฒ•: python gradio-tunnel.py <port_number>", file=sys.stderr)
201
+ sys.exit(1)
202
+
203
+ try:
204
+ port = int(sys.argv[1])
205
+ except ValueError:
206
+ print("์˜ค๋ฅ˜: ํฌํŠธ ๋ฒˆํ˜ธ๋Š” ์ •์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.", file=sys.stderr)
207
+ sys.exit(1)
208
+
209
+ try:
210
+ address = setup_tunnel(
211
+ "127.0.0.1",
212
+ port,
213
+ secrets.token_urlsafe(32),
214
+ None,
215
+ )
216
+ print(address, flush=True)
217
+ # ํ„ฐ๋„ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋„๋ก ๋Œ€๊ธฐ
218
+ time.sleep(3600 * 24 * 3)
219
+ except Exception as e:
220
+ print(f"ํ„ฐ๋„ ์‹คํ–‰ ์ค‘ ์น˜๋ช…์ ์ธ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {{e}}", file=sys.stderr)
221
+ sys.exit(1)
222
+
223
+ """
224
+
225
+ # --- 1. main ํ•จ์ˆ˜ (์ธ์ž ์ฒ˜๋ฆฌ ๊ฐ„์†Œํ™”) ---
226
+ def main():
227
+ # requests ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
228
+ try:
229
+ import requests
230
+ except ImportError:
231
+ print("ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ 'requests'๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„ค์น˜๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.")
232
+ try:
233
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"])
234
+ # import requests # ์žฌ์„ค์น˜ ํ›„ ์žฌ์ž„ํฌํŠธ๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, main ํ•จ์ˆ˜ ๋ฐ–์—์„œ ์ด๋ฏธ ์ž„ํฌํŠธ ์‹œ๋„๋˜์—ˆ์œผ๋ฏ€๋กœ ์ƒ๋žต
235
+ except Exception as e:
236
+ print(f"๊ฒฝ๊ณ : 'requests' ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ์‹คํŒจ. ํ„ฐ๋„๋ง ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜: {e}")
237
+
238
+ # runcomfy.py์— ์ „๋‹ฌ๋œ ๋ชจ๋“  ์ธ์ž(์Šคํฌ๋ฆฝํŠธ ์ด๋ฆ„ ์ œ์™ธ)๋ฅผ ComfyUI์— ์ „๋‹ฌํ•  ์ธ์ž๋กœ ์‚ฌ์šฉ
239
+ comfyui_args = sys.argv[1:]
240
+
241
+ # --- ํ•„์ˆ˜ ๊ฒฝ๋กœ ์„ค์ • ๋ฐ ์ด๋™ ---
242
+ comfyui_dir = os.environ.get('WS', '/content') + '/ComfyUI'
243
+ print(f"ComfyUI ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ด๋™ ์ค‘: {comfyui_dir}")
244
+ try:
245
+ os.chdir(comfyui_dir)
246
+ except FileNotFoundError:
247
+ print(f"์˜ค๋ฅ˜: ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค: {comfyui_dir}. ๊ฒฝ๋กœ๋ฅผ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.")
248
+ sys.exit(1)
249
+
250
+ # ํ„ฐ๋„ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์ €์žฅ
251
+ print("ํ„ฐ๋„ ์Šคํฌ๋ฆฝํŠธ ์ €์žฅ ์ค‘...")
252
+ try:
253
+ # TUNNEL_SCRIPT_PATH๋Š” '../gradio-tunnel.py'์ด๋ฏ€๋กœ, CWD์˜ ๋ถ€๋ชจ์— ์ €์žฅ๋จ
254
+ with open(TUNNEL_SCRIPT_PATH, "w") as f:
255
+ f.write(GRADIO_TUNNEL_SCRIPT_CONTENT)
256
+ except Exception as e:
257
+ print(f"์˜ค๋ฅ˜: ํ„ฐ๋„ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ ์ €์žฅ ์‹คํŒจ: {e}")
258
+ sys.exit(1)
259
+
260
+ # --- 2. ComfyUI ์„œ๋ฒ„ ์‹คํ–‰ (๋ฐฑ๊ทธ๋ผ์šด๋“œ) ---
261
+ print("\nComfyUI ์„œ๋ฒ„๋ฅผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
262
+ comfyui_base_args = [
263
+ "./main.py",
264
+ "--listen", "127.0.0.1",
265
+ "--port", str(COMFYUI_PORT),
266
+ ]
267
+ comfyui_command = ["python"] + comfyui_base_args + comfyui_args
268
+
269
+ comfyui_proc = subprocess.Popen(
270
+ comfyui_command,
271
+ stdout=subprocess.PIPE,
272
+ stderr=subprocess.STDOUT,
273
+ text=True
274
+ )
275
+ atexit.register(comfyui_proc.terminate)
276
+
277
+ # --- 3. Gradio ํ„ฐ๋„๋ง ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ (๋ฐฑ๊ทธ๋ผ์šด๋“œ) ---
278
+ print(f"์„œ๋ฒ„๊ฐ€ ํฌํŠธ {COMFYUI_PORT}๋ฅผ ์—ด ๋•Œ๊นŒ์ง€ 15์ดˆ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค...")
279
+ time.sleep(15)
280
+
281
+ print(f"\nGradio ํ„ฐ๋„ ํด๋ผ์ด์–ธํŠธ ์‹œ์ž‘ ์ค‘ (ํฌํŠธ {COMFYUI_PORT})...")
282
+ tunnel_command = ["python", TUNNEL_SCRIPT_PATH, str(COMFYUI_PORT)]
283
+
284
+ tunnel_proc = subprocess.Popen(
285
+ tunnel_command,
286
+ stdout=subprocess.PIPE,
287
+ stderr=subprocess.STDOUT,
288
+ text=True
289
+ )
290
+ atexit.register(tunnel_proc.terminate)
291
+
292
+ # --- 4. ํ„ฐ๋„ ์ฃผ์†Œ ์ถœ๋ ฅ ๋ฐ ํ”„๋กœ์„ธ์Šค ์œ ์ง€ ---
293
+ share_url = None
294
+ start_time = time.time()
295
+ print("\n[ํ„ฐ๋„ ์ถœ๋ ฅ - Gradio ๋กœ๊ทธ]")
296
+
297
+ # ํ„ฐ๋„ URL์„ ์ฐพ๋Š” ๋ฃจํ”„
298
+ while time.time() - start_time < 60 and tunnel_proc.poll() is None:
299
+ line = tunnel_proc.stdout.readline()
300
+
301
+ if line:
302
+ line_stripped = line.strip()
303
+ print(f"[TUNNEL] {line_stripped}")
304
+
305
+ # URL ํŒจํ„ด ์ฐพ๊ธฐ
306
+ if line_stripped.startswith(("https://", "http://")):
307
+ share_url = line_stripped
308
+ break
309
+
310
+ time.sleep(0.01)
311
+
312
+ if share_url:
313
+ print("\n" + "="*50)
314
+ print(f"๐ŸŽ‰ **ComfyUI ๊ณต์œ  URL์ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:** {share_url}")
315
+ print("์ด ์ฃผ์†Œ๋กœ ์ ‘์†ํ•˜์„ธ์š”.")
316
+ print("="*50)
317
+
318
+ else:
319
+ # --- ํ„ฐ๋„๋ง ์‹คํŒจ ์‹œ ์˜ค๋ฅ˜ ์ง„๋‹จ ๋กœ์ง (communicate() ์ œ๊ฑฐ) ---
320
+ print("\n" + "="*50)
321
+ print("๐Ÿšจ **ํ„ฐ๋„๋ง ํด๋ผ์ด์–ธํŠธ ์‹คํ–‰์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค!**")
322
+ print("์•„๋ž˜ ๋กœ๊ทธ์—์„œ ์˜ค๋ฅ˜ ์›์ธ์„ ํ™•์ธํ•˜์„ธ์š”.")
323
+ print("="*50)
324
+
325
+ # 1. ํ„ฐ๋„ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ ๋‚จ์€ ๋กœ๊ทธ ํ™•์ธ ๋ฐ ์ถœ๋ ฅ
326
+ print("\n--- ํ„ฐ๋„ ํด๋ผ์ด์–ธํŠธ ์ตœ์ข… ๋กœ๊ทธ ---")
327
+ tunnel_proc.stdout.seek(0) # ํ„ฐ๋„ stdout์˜ ์ฒ˜์Œ์œผ๋กœ ์ด๋™
328
+ tunnel_logs = tunnel_proc.stdout.read()
329
+ if tunnel_logs:
330
+ print(tunnel_logs.strip())
331
+ else:
332
+ print("ํ„ฐ๋„ ๋กœ๊ทธ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ฆ‰์‹œ ์ข…๋ฃŒ๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.")
333
+
334
+ # 2. ComfyUI ํ”„๋กœ์„ธ์Šค ๋กœ๊ทธ ํ™•์ธ (communicate ๋Œ€์‹  read())
335
+ # ComfyUI์˜ stdout์ด ๋‹ซํžˆ์ง€ ์•Š๋„๋ก, ๋น„์ฐจ๋‹จ ๋ฐฉ์‹์œผ๋กœ ํ˜„์žฌ๊นŒ์ง€์˜ ๋กœ๊ทธ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค.
336
+ print("\n--- ComfyUI ์ดˆ๊ธฐ ๋กœ๊ทธ ํ™•์ธ (ComfyUI๊ฐ€ ํฌํŠธ๋ฅผ ์—ด์—ˆ๋Š”์ง€ ํ™•์ธ) ---")
337
+ # subprocess.PIPE๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฐจ๋‹จ ๋ชจ๋“œ์ด๋ฏ€๋กœ, read()๋ฅผ ์“ฐ๊ธฐ ์ „์—
338
+ # ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ํ˜„์žฌ๊นŒ์ง€ ๋ฒ„ํผ์— ์Œ“์ธ ๋กœ๊ทธ๋ฅผ ์ตœ๋Œ€ํ•œ ์ถœ๋ ฅํ•ด ๋ด…๋‹ˆ๋‹ค.
339
+ # ์ด ์‹œ์ ์—์„œ๋Š” 15์ดˆ ๋™์•ˆ ๋Œ€๊ธฐํ–ˆ์œผ๋ฏ€๋กœ, ComfyUI๊ฐ€ ์‹œ์ž‘ ์‹œ ์ถœ๋ ฅํ•˜๋Š” ๋กœ๊ทธ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
340
+ try:
341
+ # select๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์ฐจ๋‹จ ๋ฐฉ์‹์œผ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์‹œ๋„ (Colab ํ™˜๊ฒฝ์—์„œ ํ•ญ์ƒ ์ž‘๋™ํ•˜์ง€๋Š” ์•Š์„ ์ˆ˜ ์žˆ์Œ)
342
+ if comfyui_proc.stdout is not None:
343
+ ready_to_read, _, _ = select.select([comfyui_proc.stdout], [], [], 0.5)
344
+ if ready_to_read:
345
+ comfy_logs = comfyui_proc.stdout.read()
346
+ print(comfy_logs.strip() if comfy_logs else "ComfyUI ๋กœ๊ทธ ๋ฒ„ํผ์— ๋‚ด์šฉ ์—†์Œ.")
347
+ else:
348
+ print("ComfyUI ๋กœ๊ทธ ๋ฒ„ํผ์— ๋‚ด์šฉ ์—†์Œ (500ms ๋Œ€๊ธฐ ํ›„).")
349
+ else:
350
+ print("ComfyUI stdout์ด None์ž…๋‹ˆ๋‹ค.")
351
+ except Exception as e:
352
+ print(f"ComfyUI ๋กœ๊ทธ ์ฝ๊ธฐ ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ: {e}")
353
+
354
+ raise RuntimeError("Gradio ํ„ฐ๋„๋ง์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์ƒ์„ธ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด ์ฃผ์‹ญ์‹œ์˜ค.")
355
+
356
+
357
+ # ํ”„๋กœ์„ธ์Šค ์œ ์ง€๋ฅผ ์œ„ํ•œ ๋ฉ”์ธ ๋ฃจํ”„ (์ถฉ๋Œ ๋กœ๊ทธ ์ถœ๋ ฅ ๊ฐ•ํ™”)
358
+ try:
359
+ print("\n**ํ”„๋กœ์„ธ์Šค๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. Colab ์„ธ์…˜์ด ์—ฐ๊ฒฐ๋œ ๋™์•ˆ ํ„ฐ๋„์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.**")
360
+ print("์ค‘์ง€ํ•˜๋ ค๋ฉด ์ด ์…€์˜ ์‹คํ–‰์„ ๋ฉˆ์ถ”์„ธ์š”.")
361
+ while True:
362
+ # ComfyUI ๋กœ๊ทธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‘ ์ถœ๋ ฅ
363
+ comfy_line = comfyui_proc.stdout.readline()
364
+ if comfy_line:
365
+ print(comfy_line.strip())
366
+
367
+ if comfyui_proc.poll() is not None:
368
+ print("\n[์˜ค๋ฅ˜] ComfyUI ํ”„๋กœ์„ธ์Šค๊ฐ€ ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ์ค‘์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„ธ์…˜์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")
369
+ # ์ข…๋ฃŒ ์‹œ ๋‚จ์•„์žˆ๋Š” ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜์—ฌ ์›์ธ ํŒŒ์•…
370
+ print("\n--- ComfyUI ์ถฉ๋Œ ๋กœ๊ทธ (์›์ธ ๋ถ„์„์šฉ) ---")
371
+ print(comfyui_proc.stdout.read())
372
+ break
373
+
374
+ if tunnel_proc.poll() is not None:
375
+ print("\n[์˜ค๋ฅ˜] ํ„ฐ๋„ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ์ค‘์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„ธ์…˜์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")
376
+ # ํ„ฐ๋„ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ ๋‚จ์€ ๋กœ๊ทธ ํ™•์ธ ๋ฐ ์ถœ๋ ฅ
377
+ print("\n--- ํ„ฐ๋„ ์ถฉ๋Œ ๋กœ๊ทธ (์›์ธ ๋ถ„์„์šฉ) ---")
378
+ print(tunnel_proc.stdout.read())
379
+ break
380
+
381
+ time.sleep(0.01)
382
+
383
+ except KeyboardInterrupt:
384
+ print("\n์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ค‘์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. (KeyboardInterrupt)")
385
+
386
+ finally:
387
+ print("\n๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค...")
388
+
389
+ if comfyui_proc.poll() is None:
390
+ comfyui_proc.terminate()
391
+ if tunnel_proc.poll() is None:
392
+ tunnel_proc.terminate()
393
+
394
+ print("๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
395
+
396
+ if __name__ == '__main__':
397
+ main()