File size: 9,660 Bytes
d2ea8a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
import sys, os, json, time, threading, re
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

# Cấu hình chung
CURRENT_VERSION = "1.0.3"
UPDATE_CHECK_URL = "https://drive.google.com/uc?export=download&id=1hkRegTsVohMNxM7o33Jj3FRix0GyEhUW"
TARGET_URL = "https://hailuoai.video/create"
COOKIES_FILE = "cookies.json"
IMAGE_FOLDER = "images"
INPUT_TEXT_FILE = "input.txt"
DOWNLOAD_TIMEOUT = 60  # giây

def normalize_text(text):
    replacements = {
        "‘": "'", "’": "'", "“": '"', "”": '"', "…": "..."
    }
    for old, new in replacements.items():
        text = text.replace(old, new)
    return re.sub(r'\s+', ' ', text).strip().lower()

class AutomationRunner:
    def __init__(self, headless, wait_time, avatar_image_path=None, video_folder="outputs", mode="subject", log_callback=None):
        self.headless = headless
        self.wait_time = wait_time
        self.avatar_image_path = avatar_image_path
        self.video_folder = video_folder
        self.mode = mode
        self.log = log_callback or (lambda msg: None)

    def setup_driver(self):
        self.log("🔧 Khởi tạo trình duyệt...")
        options = uc.ChromeOptions()
        if self.headless:
            options.add_argument('--headless=new')
            options.add_argument('--window-size=1920,1080')
        os.makedirs(self.video_folder, exist_ok=True)
        prefs = {
            "download.default_directory": os.path.abspath(self.video_folder),
            "download.prompt_for_download": False,
            "download.directory_upgrade": True,
        }
        options.add_experimental_option("prefs", prefs)
        driver = uc.Chrome(options=options)
        if not self.headless:
            driver.maximize_window()
        time.sleep(2)
        return driver

    def save_cookies(self, driver):
        with open(COOKIES_FILE, 'w', encoding='utf-8') as f:
            json.dump(driver.get_cookies(), f, ensure_ascii=False, indent=2)
        self.log(f"✅ Lưu cookies vào {COOKIES_FILE}")

    def load_cookies(self, driver):
        try:
            cookies = json.load(open(COOKIES_FILE, 'r', encoding='utf-8'))
            for c in cookies:
                # adjust SameSite
                if c.get('sameSite') == 'None':
                    c['sameSite'] = 'Strict'
                try:
                    driver.add_cookie(c)
                except Exception:
                    pass
            self.log("✅ Nạp cookies thành công")
        except FileNotFoundError:
            self.log("⚠️ Chưa có file cookies.json, sẽ login thủ công")

    def click_tab(self, driver, xpath, success_msg, fail_msg):
        try:
            el = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, xpath)))
            el.click()
            self.log(success_msg)
        except Exception as e:
            self.log(f"{fail_msg}: {e}")

    def click_confirm(self, driver):
        self.click_tab(
            driver,
            "//*[contains(text(),'Confirm')]",
            "✅ Nhấn Confirm",
            "❌ Không tìm thấy Confirm"
        )

    def upload_images_silently(self, driver):
        try:
            btn = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((
                    By.XPATH,
                    "//div[contains(text(),'Add reference character') and contains(@class,'hover:text-white')]"
                ))
            )
            btn.click()
            time.sleep(2)
            inp = driver.find_element(By.XPATH, "//input[@type='file']")
            driver.execute_script("arguments[0].style.display='block';", inp)
            paths = []
            if self.avatar_image_path:
                paths = [os.path.abspath(self.avatar_image_path)]
            else:
                imgs = [f for f in os.listdir(IMAGE_FOLDER) if f.lower().endswith((".png",".jpg",".jpeg"))]
                paths = [os.path.abspath(os.path.join(IMAGE_FOLDER,f)) for f in imgs]
            inp.send_keys("\n".join(paths))
            time.sleep(1)
            self.click_confirm(driver)  # modal upload
            self.log(f"✅ Upload ảnh: {paths}")
        except Exception as e:
            self.log(f"⚠️ Upload ảnh thất bại: {e}")

    def load_scenes(self):
        out = []
        with open(INPUT_TEXT_FILE, 'r', encoding='utf-8') as f:
            for line in f:
                line=line.strip()
                if not line: continue
                content = line.split(":",1)[1].strip() if ":" in line else line
                out.append(normalize_text(content))
        return out

    def process_prompts(self, driver, prompts):
        sent = []
        for p in prompts:
            if p in sent: 
                self.log(f"ℹ️ Bỏ qua prompt đã gửi: {p}")
                continue
            # nhập prompt
            tb = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "video-create-textarea")))
            tb.click(); tb.send_keys(Keys.CONTROL, "a", Keys.BACKSPACE)
            tb.send_keys(p, Keys.ENTER)
            self.log(f"✍️ Nhập prompt: {p}")
            # click Create
            btn = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((
                By.XPATH,
                "//button[.//img[@alt='AI Video create png by Hailuo AI Video Generator']]"
            )))
            btn.click()
            self.log("▶️ Click Create")
            # kiểm tra queue
            try:
                que = WebDriverWait(driver, 10).until(
                    EC.visibility_of_element_located((By.CSS_SELECTOR,"div.adm-auto-center-content"))
                )
                if "full" in que.text.lower():
                    self.log("⏳ Queue đầy, sẽ thử lại sau 3s")
                    time.sleep(3)
                    continue
            except TimeoutException:
                pass
            sent.append(p)
            self.log(f"✅ Prompt chấp nhận: {p}")
        return sent

    def download_all(self, driver, prompts):
        # đơn giản: load mine page, scroll, download
        driver.get("https://hailuoai.video/mine")
        time.sleep(5)
        files_before = set(os.listdir(self.video_folder))
        for _ in range(3):
            driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")
            time.sleep(2)
        items = driver.find_elements(By.XPATH,"//div[contains(@class,'mine-video-item')]")
        for it in items:
            try:
                ActionChains(driver).move_to_element(it).perform()
                p = normalize_text(it.find_element(By.CSS_SELECTOR,".prompt-plain-span").text)
                if p in prompts:
                    btns = it.find_elements(By.TAG_NAME,"button")
                    for b in btns:
                        if "Download" in b.text or b.get_attribute("innerHTML").lower().find("recreate")<0:
                            b.click()
                            self.log(f"⬇️ Download prompt: {p}")
                            break
            except:
                pass
        # chờ download
        t0 = time.time()
        while time.time()-t0 < self.wait_time:
            if any(f.endswith(".crdownload") for f in os.listdir(self.video_folder)):
                time.sleep(1)
            else:
                break
        # đổi tên theo thứ tự
        for idx,p in enumerate(prompts,1):
            matches = [f for f in os.listdir(self.video_folder) if f.endswith(".mp4")]
            if matches:
                src = max(matches, key=lambda f:os.path.getctime(os.path.join(self.video_folder,f)))
                dst = os.path.join(self.video_folder, f"Prompt {idx}.mp4")
                os.rename(os.path.join(self.video_folder,src), dst)
                self.log(f"🔄 Đổi tên {src} → Prompt {idx}.mp4")

    def run(self):
        driver = self.setup_driver()
        driver.get(TARGET_URL)
        time.sleep(5)

        # cookies / login
        if os.path.exists(COOKIES_FILE):
            self.load_cookies(driver)
            driver.refresh()
            time.sleep(5)
        else:
            self.log("❗ Vui lòng login thủ công để lưu cookies rồi deploy lại.")
            driver.quit()
            return

        # chọn mode
        if self.mode=="subject":
            self.click_tab(driver,
                           "//*[contains(text(),'Subject Reference')]",
                           "✅ Chuyển Subject Reference",
                           "❌ Không tìm Subject Reference")
            time.sleep(2)
            self.click_confirm(driver); time.sleep(2)
            self.upload_images_silently(driver)
        else:
            self.click_tab(driver,
                           "//div[span[text()='Text to Video']]",
                           "✅ Chuyển Text to Video",
                           "❌ Không tìm Text to Video")
            time.sleep(2)
            self.click_confirm(driver)

        scenes = self.load_scenes()
        self.log(f"📋 Prompts: {scenes}")
        sent = self.process_prompts(driver, scenes)
        self.log(f"⏳ Chờ {self.wait_time}s để xử lý video...")
        time.sleep(self.wait_time)
        self.download_all(driver, sent)

        driver.quit()
        self.log("🏁 Automation hoàn tất.")