Back to Home
Gistify Snippet
kyiov
Sep 20·1 file(s)
Public

wink.js

wink.js

#github-gist#javascript#gist:b93823925123b214fb61e54b7c1d304d#gistsync:2026-09-18T02:41:44Z
3
Raw
wink.jsjavascript
182 lines
1
2import axios from "axios";
3import FormData from "form-data";
4import crypto from "node:crypto";
5import fs from "node:fs";
6import fsp from "node:fs/promises";
7import path from "node:path";
8import { CookieJar } from "tough-cookie";
9import { wrapper } from "axios-cookiejar-support";
10
11const BASE_URL = "https://wink.ai";
12const STRATEGY_URL = "https://strategy.app.meitudata.com";
13
14const CLIENT_ID = "1189857605";
15const VERSION = "5.1.2";
16const COUNTRY_CODE = "ID";
17const CLIENT_LANGUAGE = "en_US";
18const CLIENT_TIMEZONE = "Asia/Jakarta";
19
20const TASK_TYPE = "12";
21const CONTENT_TYPE = "1";
22
23const UA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Mobile Safari/537.36";
24
25function sleep(ms) {
26 return new Promise(resolve => setTimeout(resolve, ms));
27}
28
29function extToMime(file) {
30 const ext = path.extname(file).toLowerCase();
31 if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
32 if (ext === ".png") return "image/png";
33 if (ext === ".webp") return "image/webp";
34 return "application/octet-stream";
35}
36
37function fileSuffix(file) {
38 const ext = path.extname(file).toLowerCase();
39 if (ext === ".jpeg") return ".jpg";
40 if (ext) return ext;
41 return ".jpg";
42}
43
44function makeTrace() {
45 return `${crypto.randomBytes(16).toString("hex")}-${crypto.randomBytes(8).toString("hex")}-1`;
46}
47
48function traceHeaders(transaction = "GET%20%2F%5Blocale%5D%2Fimage-enhancer%2Fupload") {
49 const trace = makeTrace();
50 return {
51 "sentry-trace": trace,
52 baggage: [
53 "sentry-environment=release",
54 "sentry-release=5.1.2",
55 "sentry-public_key=e1bf914f3448d9bc8a10c7e499d17d54",
56 `sentry-trace_id=${trace.split("-")[0]}`,
57 `sentry-transaction=${transaction}`,
58 "sentry-sampled=true",
59 "sentry-sample_rate=0.75"
60 ].join(",")
61 };
62}
63
64function baseParams(GNUM, extra = {}) {
65 return new URLSearchParams({
66 client_id: CLIENT_ID,
67 version: VERSION,
68 country_code: COUNTRY_CODE,
69 gnum: GNUM,
70 client_language: CLIENT_LANGUAGE,
71 client_channel_id: "",
72 client_timezone: CLIENT_TIMEZONE,
73 ...extra
74 });
75}
76
77export async function wink(IMAGE_PATH) {
78 try {
79 const GNUM = crypto.randomUUID();
80 const jar = new CookieJar();
81 await jar.setCookie(`_sm=${GNUM}; Path=/; Domain=wink.ai`, BASE_URL);
82 await jar.setCookie(`meitustat=${encodeURIComponent(JSON.stringify({ wgid: GNUM }))}; Path=/; Domain=wink.ai`, BASE_URL);
83
84 const api = wrapper(axios.create({
85 baseURL: BASE_URL,
86 jar,
87 withCredentials: true,
88 validateStatus: () => true,
89 headers: {
90 accept: "*/*",
91 origin: BASE_URL,
92 referer: `${BASE_URL}/image-enhancer/upload`,
93 "user-agent": UA,
94 "sec-ch-ua": "\"Google Chrome\";v=\"147\", \"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"147\"",
95 "sec-ch-ua-mobile": "?1",
96 "sec-ch-ua-platform": "\"Android\"",
97 ab_info: JSON.stringify({ ab_codes: [], version: "1.4.4" })
98 }
99 }));
100
101 if (!fs.existsSync(IMAGE_PATH)) throw new Error(`File tidak ditemukan: ${IMAGE_PATH}`);
102
103 const getMaatSign = async () => {
104 const params = baseParams(GNUM, { suffix: fileSuffix(IMAGE_PATH), type: "temp", count: "1" });
105 const res = await api.get(`/api/file/get_maat_sign.json?${params.toString()}`, { headers: traceHeaders() });
106 if (res.status >= 400 || res.data?.code !== 0) throw new Error(`get_maat_sign gagal: ${JSON.stringify(res.data)}`);
107 return res.data.data;
108 };
109
110 const getUploadPolicy = async (sign) => {
111 const params = new URLSearchParams({ app: sign.app, count: String(sign.count), sig: sign.sig, sigTime: sign.sig_time, sigVersion: sign.sig_version, suffix: sign.suffix, type: sign.type });
112 const res = await axios.get(`${STRATEGY_URL}/upload/policy?${params.toString()}`, {
113 headers: { accept: "*/*", origin: BASE_URL, referer: `${BASE_URL}/`, "user-agent": UA },
114 validateStatus: () => true
115 });
116 if (res.status >= 400 || !Array.isArray(res.data) || !res.data[0]?.qiniu) throw new Error(`upload policy gagal: ${JSON.stringify(res.data)}`);
117 return res.data[0].qiniu;
118 };
119
120 const uploadToQiniu = async (policy, filePath) => {
121 const form = new FormData();
122 form.append("file", fs.createReadStream(filePath), { filename: path.basename(filePath), contentType: extToMime(filePath) });
123 form.append("token", policy.token);
124 form.append("key", policy.key);
125 form.append("fname", path.basename(filePath));
126 const res = await axios.post(policy.url, form, {
127 headers: form.getHeaders({ origin: BASE_URL, referer: `${BASE_URL}/`, "user-agent": UA, accept: "*/*" }),
128 maxBodyLength: Infinity, maxContentLength: Infinity, validateStatus: () => true
129 });
130 if (res.status >= 400) throw new Error(`upload qiniu gagal HTTP ${res.status}`);
131 return { file_key: policy.key, source_url: res.data.url || res.data.data || policy.data };
132 };
133
134 const sign = await getMaatSign();
135 const policy = await getUploadPolicy(sign);
136 const uploaded = await uploadToQiniu(policy, IMAGE_PATH);
137
138 const TASK_NAME = `Enhancer-Ultra HD-${path.parse(IMAGE_PATH).name}`;
139 const delivery = async (sourceUrl) => {
140 const body = baseParams(GNUM, {
141 type: TASK_TYPE, content_type: CONTENT_TYPE, source_url: sourceUrl,
142 type_params: JSON.stringify({ is_mirror: 0, orientation_tag: 1, j_420_trans: "1", return_ext: "2" }),
143 right_detail: JSON.stringify({ source: "1", touch_type: "4", function_id: "630", material_id: "63011", url: "https://wink.ai/image-enhancer/upload" }),
144 ext_params: JSON.stringify({ task_name: TASK_NAME, records: TASK_TYPE }),
145 with_prepare: "1"
146 });
147 const res = await api.post("/api/meitu_ai/delivery.json", body.toString(), {
148 headers: { ...traceHeaders(), "content-type": "application/x-www-form-urlencoded;charset=UTF-8" }
149 });
150 if (res.status >= 400 || res.data?.code !== 0) throw new Error(`delivery gagal: ${JSON.stringify(res.data)}`);
151 return res.data.data;
152 };
153
154 const task = await delivery(uploaded.source_url);
155 const firstMsgId = task.msg_id || task.prepare_msg_id;
156 if (!firstMsgId) throw new Error(`delivery tidak mengembalikan msg_id`);
157
158 const waitResult = async (msgId) => {
159 for (let i = 1; i <= 80; i++) {
160 const params = baseParams(GNUM, { msg_ids: msgId });
161 const res = await api.get(`/api/meitu_ai/query_batch.json?${params.toString()}`, {
162 headers: { ...traceHeaders("%2F%3Alocale%2Feditor%2Frecent-task"), referer: `${BASE_URL}/image-enhancer/upload` }
163 });
164 if (res.status >= 400 || res.data?.code !== 0) throw new Error(`query batch gagal`);
165 const item = res.data.data?.item_list?.[0];
166 const resultValue = item?.result?.result || "";
167 const realMsgId = item?.result?.msg_id || item?.msg_id || "";
168 if (resultValue && resultValue !== msgId && !resultValue.startsWith("http")) { msgId = resultValue; continue; }
169 if (realMsgId && realMsgId !== msgId && !realMsgId.startsWith("wpr_")) { msgId = realMsgId; continue; }
170 const url = item?.result?.media_info_list?.[0]?.media_data;
171 if (url && url.startsWith("http") && item?.result?.error_code === 0) return url;
172 await sleep(3000);
173 }
174 throw new Error("Timeout");
175 };
176
177 const resultUrl = await waitResult(firstMsgId);
178 return { status: true, resultUrl };
179 } catch (e) {
180 return { status: false, error: e.message };
181 }
182}
Discussion & Comments0

No comments yet — be the first to leave feedback or start a code discussion.

Sign in with your GitHub account to write comments and join the discussion.