data / runcomfy.py
arcacolab's picture
Update runcomfy.py
b17fd97 verified
import os
import subprocess
import time
import atexit
import sys
import re
import argparse
import shlex
import io
import select
# --- ์„ค์ • ๋ณ€์ˆ˜ ---
COMFYUI_PORT = 8188
# ๐Ÿšจ ์‚ฌ์šฉ์ž ์ง€์ •: ๋ชจ๋“  .py ํŒŒ์ผ์„ ์ €์žฅํ•  ์ ˆ๋Œ€ ๊ฒฝ๋กœ
# ์ด ๊ฒฝ๋กœ์— gradio-tunnel.py๊ฐ€ ์ €์žฅ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (Colab ์…€ ์‹คํ–‰ ์ „์— /content/py ํด๋”๊ฐ€ ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.)
PYTHON_SCRIPT_DIR = "/content/py"
# ํ„ฐ๋„ ์Šคํฌ๋ฆฝํŠธ์˜ ์ตœ์ข… ์ ˆ๋Œ€ ๊ฒฝ๋กœ
TUNNEL_SCRIPT_PATH = os.path.join(PYTHON_SCRIPT_DIR, "gradio-tunnel.py")
# ์˜ˆ: "/content/py/gradio-tunnel.py"
# Gradio Tunnel Script ๋‚ด์šฉ (ํ„ฐ๋„๋ง ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” Python ์ฝ”๋“œ)
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)
"""
# --- 1. main ํ•จ์ˆ˜ ---
def main():
# requests ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
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}")
# runcomfy.py์— ์ „๋‹ฌ๋œ ๋ชจ๋“  ์ธ์ž(์Šคํฌ๋ฆฝํŠธ ์ด๋ฆ„ ์ œ์™ธ)๋ฅผ ComfyUI์— ์ „๋‹ฌํ•  ์ธ์ž๋กœ ์‚ฌ์šฉ
comfyui_args = sys.argv[1:]
# --- ํ•„์ˆ˜ ๊ฒฝ๋กœ ์„ค์ • ๋ฐ ์ด๋™ ---
# WS ๋ณ€์ˆ˜๋Š” ์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์™ธ๋ถ€ Colab ์…€์—์„œ ๊ฒฐ์ •๋˜๊ฑฐ๋‚˜,
# ํ˜น์€ ์ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ DRIVE ๋ชจ๋“œ(/content/drive/MyDrive/ONECLICK) ์ „์šฉ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
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:
# TUNNEL_SCRIPT_PATH์— ์ €์žฅ (์ ˆ๋Œ€ ๊ฒฝ๋กœ ์‚ฌ์šฉ)
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)
# --- 2. ComfyUI ์„œ๋ฒ„ ์‹คํ–‰ (๋ฐฑ๊ทธ๋ผ์šด๋“œ) ---
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)
# --- 3. Gradio ํ„ฐ๋„๋ง ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ (๋ฐฑ๊ทธ๋ผ์šด๋“œ) ---
print(f"์„œ๋ฒ„๊ฐ€ ํฌํŠธ {COMFYUI_PORT}๋ฅผ ์—ด ๋•Œ๊นŒ์ง€ 15์ดˆ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค...")
time.sleep(15)
print(f"\nGradio ํ„ฐ๋„ ํด๋ผ์ด์–ธํŠธ ์‹œ์ž‘ ์ค‘ (ํฌํŠธ {COMFYUI_PORT})...")
# ๐Ÿšจ ์ˆ˜์ •: TUNNEL_SCRIPT_PATH ๋ณ€์ˆ˜(์ ˆ๋Œ€ ๊ฒฝ๋กœ) ์‚ฌ์šฉ
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)
# --- 4. ํ„ฐ๋„ ์ฃผ์†Œ ์ถœ๋ ฅ ๋ฐ ํ”„๋กœ์„ธ์Šค ์œ ์ง€ ---
share_url = None
start_time = time.time()
print("\n[ํ„ฐ๋„ ์ถœ๋ ฅ - Gradio ๋กœ๊ทธ]")
# ํ„ฐ๋„ URL์„ ์ฐพ๋Š” ๋ฃจํ”„
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}")
# URL ํŒจํ„ด ์ฐพ๊ธฐ
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)
# 1. ํ„ฐ๋„ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ ๋‚จ์€ ๋กœ๊ทธ ํ™•์ธ ๋ฐ ์ถœ๋ ฅ
print("\n--- ํ„ฐ๋„ ํด๋ผ์ด์–ธํŠธ ์ตœ์ข… ๋กœ๊ทธ ---")
# ํ„ฐ๋„ stdout์˜ ์ฒ˜์Œ์œผ๋กœ ์ด๋™ (์ด๋ฏธ ์ฝ์€ ๋‚ด์šฉ ํฌํ•จ)
try:
tunnel_logs = tunnel_proc.stdout.read()
if tunnel_logs:
print(tunnel_logs.strip())
else:
print("ํ„ฐ๋„ ๋กœ๊ทธ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ฆ‰์‹œ ์ข…๋ฃŒ๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.")
except Exception as e:
print(f"ํ„ฐ๋„ ๋กœ๊ทธ๋ฅผ ์ฝ๋Š” ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ: {e}")
# 2. ComfyUI ํ”„๋กœ์„ธ์Šค ๋กœ๊ทธ ํ™•์ธ
print("\n--- ComfyUI ์ดˆ๊ธฐ ๋กœ๊ทธ ํ™•์ธ (ComfyUI๊ฐ€ ํฌํŠธ๋ฅผ ์—ด์—ˆ๋Š”์ง€ ํ™•์ธ) ---")
try:
if comfyui_proc.stdout is not None:
# select๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์ฐจ๋‹จ ๋ฐฉ์‹์œผ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์‹œ๋„
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:
# ComfyUI ๋กœ๊ทธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‘ ์ถœ๋ ฅ
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()