|
|
import os |
|
|
import subprocess |
|
|
import time |
|
|
import atexit |
|
|
import sys |
|
|
import re |
|
|
import argparse |
|
|
import shlex |
|
|
import io |
|
|
import select |
|
|
|
|
|
|
|
|
COMFYUI_PORT = 8188 |
|
|
|
|
|
|
|
|
|
|
|
PYTHON_SCRIPT_DIR = "/content/py" |
|
|
|
|
|
|
|
|
TUNNEL_SCRIPT_PATH = os.path.join(PYTHON_SCRIPT_DIR, "gradio-tunnel.py") |
|
|
|
|
|
|
|
|
|
|
|
GRADIO_TUNNEL_SCRIPT_CONTENT = r""" |
|
|
import atexit |
|
|
import os |
|
|
import platform |
|
|
import re |
|
|
import stat |
|
|
import subprocess |
|
|
import sys |
|
|
import time |
|
|
import secrets |
|
|
from pathlib import Path |
|
|
from typing import List, Optional |
|
|
|
|
|
import requests |
|
|
|
|
|
VERSION = "0.2" |
|
|
CURRENT_TUNNELS: List["Tunnel"] = [] |
|
|
|
|
|
machine = platform.machine() |
|
|
if machine == "x86_64": |
|
|
machine = "amd64" |
|
|
|
|
|
BINARY_REMOTE_NAME = f"frpc_{platform.system().lower()}_{machine.lower()}" |
|
|
EXTENSION = ".exe" if os.name == "nt" else "" |
|
|
BINARY_URL = f"https://cdn-media.huggingface.co/frpc-gradio-{VERSION}/{BINARY_REMOTE_NAME}{EXTENSION}" |
|
|
|
|
|
BINARY_FILENAME = f"{BINARY_REMOTE_NAME}_v{VERSION}" |
|
|
BINARY_FOLDER = Path(__file__).parent.absolute() |
|
|
BINARY_PATH = f"{BINARY_FOLDER / BINARY_FILENAME}" |
|
|
|
|
|
TUNNEL_TIMEOUT_SECONDS = 30 |
|
|
TUNNEL_ERROR_MESSAGE = ( |
|
|
"Could not create share URL. " |
|
|
"Please check the appended log from frpc for more information:" |
|
|
) |
|
|
|
|
|
GRADIO_API_SERVER = "https://api.gradio.app/v2/tunnel-request" |
|
|
GRADIO_SHARE_SERVER_ADDRESS = None |
|
|
|
|
|
|
|
|
class Tunnel: |
|
|
def __init__(self, remote_host, remote_port, local_host, local_port, share_token): |
|
|
self.proc = None |
|
|
self.url = None |
|
|
self.remote_host = remote_host |
|
|
self.remote_port = remote_port |
|
|
self.local_host = local_host |
|
|
self.local_port = local_port |
|
|
self.share_token = share_token |
|
|
|
|
|
@staticmethod |
|
|
def download_binary(): |
|
|
if not Path(BINARY_PATH).exists(): |
|
|
resp = requests.get(BINARY_URL) |
|
|
|
|
|
if resp.status_code == 403: |
|
|
raise OSError( |
|
|
f"Cannot set up a share link as this platform is incompatible. Please " |
|
|
f"create a GitHub issue with information about your platform: {platform.uname()}" |
|
|
) |
|
|
|
|
|
resp.raise_for_status() |
|
|
|
|
|
# Save file data to local copy |
|
|
with open(BINARY_PATH, "wb") as file: |
|
|
file.write(resp.content) |
|
|
st = os.stat(BINARY_PATH) |
|
|
os.chmod(BINARY_PATH, st.st_mode | stat.S_IEXEC) |
|
|
|
|
|
def start_tunnel(self) -> str: |
|
|
self.download_binary() |
|
|
self.url = self._start_tunnel(BINARY_PATH) |
|
|
return self.url |
|
|
|
|
|
def kill(self): |
|
|
if self.proc is not None: |
|
|
print(f"ํฐ๋ ์ข
๋ฃ ์ค: {self.local_host}:{self.local_port} <> {self.url}") |
|
|
self.proc.terminate() |
|
|
self.proc = None |
|
|
|
|
|
def _start_tunnel(self, binary: str) -> str: |
|
|
CURRENT_TUNNELS.append(self) |
|
|
command = [ |
|
|
binary, |
|
|
"http", |
|
|
"-n", |
|
|
self.share_token, |
|
|
"-l", |
|
|
str(self.local_port), |
|
|
"-i", |
|
|
self.local_host, |
|
|
"--uc", |
|
|
"--sd", |
|
|
"random", |
|
|
"--ue", |
|
|
"--server_addr", |
|
|
f"{self.remote_host}:{self.remote_port}", |
|
|
"--disable_log_color", |
|
|
] |
|
|
self.proc = subprocess.Popen( |
|
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE |
|
|
) |
|
|
atexit.register(self.kill) |
|
|
return self._read_url_from_tunnel_stream() |
|
|
|
|
|
def _read_url_from_tunnel_stream(self) -> str: |
|
|
start_timestamp = time.time() |
|
|
|
|
|
log = [] |
|
|
url = "" |
|
|
|
|
|
def _raise_tunnel_error(): |
|
|
log_text = "\\n".join(log) |
|
|
print(log_text, file=sys.stderr) |
|
|
raise ValueError(f"{TUNNEL_ERROR_MESSAGE}\\n{log_text}") |
|
|
|
|
|
while url == "": |
|
|
if time.time() - start_timestamp >= TUNNEL_TIMEOUT_SECONDS: |
|
|
_raise_tunnel_error() |
|
|
|
|
|
assert self.proc is not None |
|
|
if self.proc.stdout is None: |
|
|
continue |
|
|
|
|
|
line = self.proc.stdout.readline() |
|
|
try: |
|
|
line = line.decode("utf-8") |
|
|
except UnicodeDecodeError: |
|
|
continue |
|
|
|
|
|
if line == "": |
|
|
time.sleep(0.01) |
|
|
continue |
|
|
|
|
|
log.append(line.strip()) |
|
|
|
|
|
if "start proxy success" in line: |
|
|
result = re.search("start proxy success: (.+)\\n", line) |
|
|
if result is None: |
|
|
_raise_tunnel_error() |
|
|
else: |
|
|
url = result.group(1) |
|
|
elif "login to server failed" in line: |
|
|
_raise_tunnel_error() |
|
|
|
|
|
if self.proc.poll() is not None and url == "": |
|
|
_raise_tunnel_error() |
|
|
|
|
|
return url |
|
|
|
|
|
|
|
|
def setup_tunnel( |
|
|
local_host: str, |
|
|
local_port: int, |
|
|
share_token: str, |
|
|
share_server_address: Optional[str], |
|
|
) -> str: |
|
|
share_server_address = ( |
|
|
GRADIO_SHARE_SERVER_ADDRESS |
|
|
if share_server_address is None |
|
|
else share_server_address |
|
|
) |
|
|
if share_server_address is None: |
|
|
response = requests.get(GRADIO_API_SERVER) |
|
|
if not (response and response.status_code == 200): |
|
|
raise RuntimeError("Could not get share link from Gradio API Server.") |
|
|
payload = response.json()[0] |
|
|
remote_host, remote_port = payload["host"], int(payload["port"]) |
|
|
else: |
|
|
remote_host, remote_port = share_server_address.split(":") |
|
|
remote_port = int(remote_port) |
|
|
try: |
|
|
tunnel = Tunnel(remote_host, remote_port, local_host, local_port, share_token) |
|
|
address = tunnel.start_tunnel() |
|
|
return address |
|
|
except Exception as e: |
|
|
raise RuntimeError(str(e)) from e |
|
|
|
|
|
# ์ด ์คํฌ๋ฆฝํธ๋ ํฌํธ ๋ฒํธ๋ฅผ ์ธ์๋ก ๋ฐ์ Gradio ํฐ๋๋ง์ ์์ํฉ๋๋ค. |
|
|
if __name__ == "__main__": |
|
|
if len(sys.argv) < 2: |
|
|
print("์ฌ์ฉ๋ฒ: python gradio-tunnel.py <port_number>", file=sys.stderr) |
|
|
sys.exit(1) |
|
|
|
|
|
try: |
|
|
port = int(sys.argv[1]) |
|
|
except ValueError: |
|
|
print("์ค๋ฅ: ํฌํธ ๋ฒํธ๋ ์ ์์ฌ์ผ ํฉ๋๋ค.", file=sys.stderr) |
|
|
sys.exit(1) |
|
|
|
|
|
# requests ์ํฌํธ ํ์ธ (Colab ํ๊ฒฝ์์ ํ์) |
|
|
try: |
|
|
import requests |
|
|
except ImportError: |
|
|
# ์ด ์ฝ๋๋ runcomfy.py์์ ์คํํ๋ subprocess์ด๋ฏ๋ก, |
|
|
# runcomfy.py์์ requests ์ค์น๋ฅผ ๋ณด์ฅํด์ผ ํจ. |
|
|
pass |
|
|
|
|
|
try: |
|
|
address = setup_tunnel( |
|
|
"127.0.0.1", |
|
|
port, |
|
|
secrets.token_urlsafe(32), |
|
|
None, |
|
|
) |
|
|
print(address, flush=True) |
|
|
# ํฐ๋ ํ๋ก์ธ์ค๊ฐ ์ข
๋ฃ๋์ง ์๋๋ก ๋๊ธฐ |
|
|
time.sleep(3600 * 24 * 3) |
|
|
except Exception as e: |
|
|
print(f"ํฐ๋ ์คํ ์ค ์น๋ช
์ ์ธ ์ค๋ฅ ๋ฐ์: {e}", file=sys.stderr) |
|
|
sys.exit(1) |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
def main(): |
|
|
|
|
|
try: |
|
|
import requests |
|
|
except ImportError: |
|
|
print("ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ 'requests'๊ฐ ์ค์น๋์ด ์์ง ์์ต๋๋ค. ์ค์น๋ฅผ ์๋ํฉ๋๋ค.") |
|
|
try: |
|
|
|
|
|
subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"]) |
|
|
except Exception as e: |
|
|
print(f"๊ฒฝ๊ณ : 'requests' ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น ์คํจ. ํฐ๋๋ง ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ค๋ฅ: {e}") |
|
|
|
|
|
|
|
|
comfyui_args = sys.argv[1:] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfyui_dir = "/content/ComfyUI" |
|
|
|
|
|
print(f"ComfyUI ๋๋ ํ ๋ฆฌ๋ก ์ด๋ ์ค: {comfyui_dir}") |
|
|
try: |
|
|
os.chdir(comfyui_dir) |
|
|
except FileNotFoundError: |
|
|
print(f"์ค๋ฅ: ๋๋ ํ ๋ฆฌ๊ฐ ์์ต๋๋ค: {comfyui_dir}. ๊ฒฝ๋ก๋ฅผ ํ์ธํด ์ฃผ์ธ์.") |
|
|
sys.exit(1) |
|
|
|
|
|
|
|
|
print(f"ํฐ๋ ์คํฌ๋ฆฝํธ ์ ์ฅ ์ค: {TUNNEL_SCRIPT_PATH}") |
|
|
try: |
|
|
|
|
|
with open(TUNNEL_SCRIPT_PATH, "w") as f: |
|
|
f.write(GRADIO_TUNNEL_SCRIPT_CONTENT) |
|
|
except Exception as e: |
|
|
print(f"์ค๋ฅ: ํฐ๋ ์คํฌ๋ฆฝํธ ํ์ผ ์ ์ฅ ์คํจ. {PYTHON_SCRIPT_DIR} ๊ฒฝ๋ก๊ฐ ์กด์ฌํ๋์ง ํ์ธํด ์ฃผ์ธ์. ์ค๋ฅ: {e}") |
|
|
sys.exit(1) |
|
|
|
|
|
|
|
|
|
|
|
print("\nComfyUI ์๋ฒ๋ฅผ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์์ํฉ๋๋ค...") |
|
|
comfyui_base_args = [ |
|
|
"./main.py", |
|
|
"--listen", "127.0.0.1", |
|
|
"--port", str(COMFYUI_PORT), |
|
|
] |
|
|
comfyui_command = ["python"] + comfyui_base_args + comfyui_args |
|
|
|
|
|
comfyui_proc = subprocess.Popen( |
|
|
comfyui_command, |
|
|
stdout=subprocess.PIPE, |
|
|
stderr=subprocess.STDOUT, |
|
|
text=True |
|
|
) |
|
|
atexit.register(comfyui_proc.terminate) |
|
|
|
|
|
|
|
|
print(f"์๋ฒ๊ฐ ํฌํธ {COMFYUI_PORT}๋ฅผ ์ด ๋๊น์ง 15์ด ๋๊ธฐํฉ๋๋ค...") |
|
|
time.sleep(15) |
|
|
|
|
|
print(f"\nGradio ํฐ๋ ํด๋ผ์ด์ธํธ ์์ ์ค (ํฌํธ {COMFYUI_PORT})...") |
|
|
|
|
|
tunnel_command = ["python", TUNNEL_SCRIPT_PATH, str(COMFYUI_PORT)] |
|
|
|
|
|
tunnel_proc = subprocess.Popen( |
|
|
tunnel_command, |
|
|
stdout=subprocess.PIPE, |
|
|
stderr=subprocess.STDOUT, |
|
|
text=True |
|
|
) |
|
|
atexit.register(tunnel_proc.terminate) |
|
|
|
|
|
|
|
|
share_url = None |
|
|
start_time = time.time() |
|
|
print("\n[ํฐ๋ ์ถ๋ ฅ - Gradio ๋ก๊ทธ]") |
|
|
|
|
|
|
|
|
while time.time() - start_time < 60 and tunnel_proc.poll() is None: |
|
|
line = tunnel_proc.stdout.readline() |
|
|
|
|
|
if line: |
|
|
line_stripped = line.strip() |
|
|
print(f"[TUNNEL] {line_stripped}") |
|
|
|
|
|
|
|
|
if line_stripped.startswith(("https://", "http://")): |
|
|
share_url = line_stripped |
|
|
break |
|
|
|
|
|
time.sleep(0.01) |
|
|
|
|
|
if share_url: |
|
|
print("\n" + "="*50) |
|
|
print(f"๐ **ComfyUI ๊ณต์ URL์ด ์์ฑ๋์์ต๋๋ค:** {share_url}") |
|
|
print("์ด ์ฃผ์๋ก ์ ์ํ์ธ์.") |
|
|
print("="*50) |
|
|
|
|
|
else: |
|
|
|
|
|
print("\n" + "="*50) |
|
|
print("๐จ **ํฐ๋๋ง ํด๋ผ์ด์ธํธ ์คํ์ ์คํจํ์ต๋๋ค!**") |
|
|
print("์๋ ๋ก๊ทธ์์ ์ค๋ฅ ์์ธ์ ํ์ธํ์ธ์.") |
|
|
print("="*50) |
|
|
|
|
|
|
|
|
print("\n--- ํฐ๋ ํด๋ผ์ด์ธํธ ์ต์ข
๋ก๊ทธ ---") |
|
|
|
|
|
try: |
|
|
tunnel_logs = tunnel_proc.stdout.read() |
|
|
if tunnel_logs: |
|
|
print(tunnel_logs.strip()) |
|
|
else: |
|
|
print("ํฐ๋ ๋ก๊ทธ๊ฐ ๋ฐ๊ฒฌ๋์ง ์์์ต๋๋ค. ์ฆ์ ์ข
๋ฃ๋ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค.") |
|
|
except Exception as e: |
|
|
print(f"ํฐ๋ ๋ก๊ทธ๋ฅผ ์ฝ๋ ์ค ์์ธ ๋ฐ์: {e}") |
|
|
|
|
|
|
|
|
print("\n--- ComfyUI ์ด๊ธฐ ๋ก๊ทธ ํ์ธ (ComfyUI๊ฐ ํฌํธ๋ฅผ ์ด์๋์ง ํ์ธ) ---") |
|
|
try: |
|
|
if comfyui_proc.stdout is not None: |
|
|
|
|
|
ready_to_read, _, _ = select.select([comfyui_proc.stdout], [], [], 0.5) |
|
|
if ready_to_read: |
|
|
comfy_logs = comfyui_proc.stdout.read() |
|
|
print(comfy_logs.strip() if comfy_logs else "ComfyUI ๋ก๊ทธ ๋ฒํผ์ ๋ด์ฉ ์์.") |
|
|
else: |
|
|
print("ComfyUI ๋ก๊ทธ ๋ฒํผ์ ๋ด์ฉ ์์ (500ms ๋๊ธฐ ํ).") |
|
|
else: |
|
|
print("ComfyUI stdout์ด None์
๋๋ค.") |
|
|
except Exception as e: |
|
|
print(f"ComfyUI ๋ก๊ทธ ์ฝ๊ธฐ ์ค ์์ธ ๋ฐ์: {e}") |
|
|
|
|
|
|
|
|
if comfyui_proc.poll() is None: |
|
|
comfyui_proc.terminate() |
|
|
if tunnel_proc.poll() is None: |
|
|
tunnel_proc.terminate() |
|
|
|
|
|
raise RuntimeError("Gradio ํฐ๋๋ง์ ์คํจํ์ต๋๋ค. ์์ ์์ธ ๋ก๊ทธ๋ฅผ ํ์ธํด ์ฃผ์ญ์์ค.") |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
print("\n**ํ๋ก์ธ์ค๋ฅผ ์ ์งํฉ๋๋ค. Colab ์ธ์
์ด ์ฐ๊ฒฐ๋ ๋์ ํฐ๋์ด ์ ์ง๋ฉ๋๋ค.**") |
|
|
print("์ค์งํ๋ ค๋ฉด ์ด ์
์ ์คํ์ ๋ฉ์ถ์ธ์.") |
|
|
while True: |
|
|
|
|
|
comfy_line = comfyui_proc.stdout.readline() |
|
|
if comfy_line: |
|
|
print(comfy_line.strip()) |
|
|
|
|
|
if comfyui_proc.poll() is not None: |
|
|
print("\n[์ค๋ฅ] ComfyUI ํ๋ก์ธ์ค๊ฐ ์๊ธฐ์น ์๊ฒ ์ค์ง๋์์ต๋๋ค. ์ธ์
์ ์ข
๋ฃํฉ๋๋ค.") |
|
|
|
|
|
print("\n--- ComfyUI ์ถฉ๋ ๋ก๊ทธ (์์ธ ๋ถ์์ฉ) ---") |
|
|
print(comfyui_proc.stdout.read()) |
|
|
break |
|
|
|
|
|
if tunnel_proc.poll() is not None: |
|
|
print("\n[์ค๋ฅ] ํฐ๋ ํ๋ก์ธ์ค๊ฐ ์๊ธฐ์น ์๊ฒ ์ค์ง๋์์ต๋๋ค. ์ธ์
์ ์ข
๋ฃํฉ๋๋ค.") |
|
|
|
|
|
print("\n--- ํฐ๋ ์ถฉ๋ ๋ก๊ทธ (์์ธ ๋ถ์์ฉ) ---") |
|
|
print(tunnel_proc.stdout.read()) |
|
|
break |
|
|
|
|
|
time.sleep(0.01) |
|
|
|
|
|
except KeyboardInterrupt: |
|
|
print("\n์ฌ์ฉ์๊ฐ ํ๋ก์ธ์ค๋ฅผ ์ค์งํ์ต๋๋ค. (KeyboardInterrupt)") |
|
|
|
|
|
finally: |
|
|
print("\n๋ฐฑ๊ทธ๋ผ์ด๋ ํ๋ก์ธ์ค ์ข
๋ฃ๋ฅผ ์๋ํฉ๋๋ค...") |
|
|
|
|
|
if comfyui_proc.poll() is None: |
|
|
comfyui_proc.terminate() |
|
|
if tunnel_proc.poll() is None: |
|
|
tunnel_proc.terminate() |
|
|
|
|
|
print("๋ฐฑ๊ทธ๋ผ์ด๋ ํ๋ก์ธ์ค๊ฐ ์ข
๋ฃ๋์์ต๋๋ค.") |
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |