GitHub Actions commited on
Commit
30fc7a6
·
1 Parent(s): 7e213c1

chore(hf): loại file chỉ-để-deploy khỏi bản đẩy Space

Browse files
Files changed (1) hide show
  1. deploy/tiktok_relay_worker.js +0 -208
deploy/tiktok_relay_worker.js DELETED
@@ -1,208 +0,0 @@
1
- /**
2
- * TikTok relay — "server sạch" FREE để bóc video TikTok (kể cả quảng cáo) mà
3
- * KHÔNG dính chặn IP datacenter của server chính (HF Space chạy trên AWS -> bị
4
- * tikwm/anti-bot chặn 403). Worker chạy trên IP sạch của Cloudflare nên gọi được.
5
- *
6
- * Thử NHIỀU nguồn theo thứ tự (nguồn nào sống thì lấy):
7
- * 1) ssstik.io — hạ tầng riêng, KHÔNG dính quota tikwm
8
- * 2) tikwm.com — mạnh nhất nhưng giới hạn ~10k request/ngày/IP (IP Cloudflare
9
- * dùng chung nên có thể hết quota -> mới cần nguồn (1))
10
- * Trả JSON dạng tikwm { code:0, data:{ play, hdplay, title, id, images, music,
11
- * duration } } để backend dùng NGUYÊN logic cũ. Có field _via cho biết nguồn nào.
12
- *
13
- * ─────────────────── DEPLOY (Cloudflare Workers, FREE) ───────────────────
14
- * 1. dash.cloudflare.com -> Workers & Pages -> Create -> Start with Hello World.
15
- * 2. Đặt tên -> Deploy -> Edit code -> DÁN ĐÈ toàn bộ file này -> Deploy.
16
- * 3. Copy URL https://<tên>.<acc>.workers.dev
17
- * 4. HF Space -> Settings -> Secrets: TIKTOK_RELAY_URL = <URL đó>
18
- *
19
- * DEBUG nhanh trên trình duyệt:
20
- * <URL>/?url=<link tiktok> -> JSON (xem code, _via)
21
- * <URL>/?url=<link>&only=ssstik -> ép chỉ thử ssstik
22
- * <URL>/?url=<link>&only=tikwm -> ép chỉ thử tikwm
23
- * <URL>/?form=<api site>&ref=<home> -> POST hộ tới site tải hộ (savetik...)
24
- * <URL>/?proxy=<url media> -> tải hộ file; chuyển tiếp cả header Range
25
- */
26
-
27
- const UA_MOBILE =
28
- "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) " +
29
- "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
30
- const UA_DESKTOP =
31
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
32
- "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
33
- const CORS = { "Access-Control-Allow-Origin": "*" };
34
-
35
- function awemeId(url) {
36
- const m = url.match(/\/(?:video|photo)\/(\d{6,})/) ||
37
- url.match(/item_id=(\d{6,})/) || url.match(/(\d{15,})/);
38
- return m ? m[1] : "tiktok";
39
- }
40
- function jsonResp(obj, status = 200) {
41
- return new Response(JSON.stringify(obj), {
42
- status, headers: { ...CORS, "Content-Type": "application/json" },
43
- });
44
- }
45
-
46
- // ── Nguồn 1: ssstik.io (token + POST, trả HTML fragment) ─────────────────
47
- async function viaSsstik(target) {
48
- const home = await fetch("https://ssstik.io/en", {
49
- headers: { "User-Agent": UA_DESKTOP, "Accept": "text/html" },
50
- });
51
- const html = await home.text();
52
- const tm = html.match(/name="tt"\s+value="([^"]+)"/) ||
53
- html.match(/tt:\s*"([^"]+)"/) ||
54
- html.match(/\btt=([A-Za-z0-9_-]{6,})/);
55
- const tt = tm ? tm[1] : "";
56
- const body = "id=" + encodeURIComponent(target) + "&locale=en&tt=" + encodeURIComponent(tt);
57
- const r = await fetch("https://ssstik.io/abc?url=dl", {
58
- method: "POST",
59
- headers: {
60
- "User-Agent": UA_DESKTOP,
61
- "Content-Type": "application/x-www-form-urlencoded",
62
- "HX-Request": "true",
63
- "HX-Current-URL": "https://ssstik.io/en",
64
- "Referer": "https://ssstik.io/en",
65
- "Origin": "https://ssstik.io",
66
- },
67
- body,
68
- });
69
- const frag = await r.text();
70
- // Gom mọi href, chọn link tải video (không watermark) đầu tiên.
71
- const hrefs = [...frag.matchAll(/href="(https?:\/\/[^"]+)"/g)].map((m) => m[1]);
72
- const vid = hrefs.find((h) => /tikcdn|\.mp4|tikwm|no_watermark|type=video/i.test(h));
73
- if (!vid) throw new Error("ssstik: không thấy link video");
74
- const tm2 = frag.match(/<h2[^>]*>([^<]+)<\/h2>/) || frag.match(/class="maintext"[^>]*>([^<]+)</);
75
- return {
76
- code: 0, _via: "ssstik",
77
- data: { id: awemeId(target), title: (tm2 ? tm2[1].trim() : ""),
78
- play: vid, hdplay: vid, images: [], music: "", duration: 0 },
79
- };
80
- }
81
-
82
- // ── Nguồn 2: tikwm.com (nhiều host/method) ───────────────────────────────
83
- async function viaTikwm(target) {
84
- const attempts = [
85
- { method: "GET", url: "https://www.tikwm.com/api/?hd=1&url=" + encodeURIComponent(target) },
86
- { method: "GET", url: "https://tikwm.com/api/?hd=1&url=" + encodeURIComponent(target) },
87
- { method: "POST", url: "https://www.tikwm.com/api/", body: "url=" + encodeURIComponent(target) + "&hd=1" },
88
- ];
89
- let last = "";
90
- for (const a of attempts) {
91
- try {
92
- const init = {
93
- method: a.method,
94
- headers: {
95
- "User-Agent": UA_MOBILE, "Referer": "https://www.tikwm.com/",
96
- "Accept": "application/json, text/plain, */*",
97
- },
98
- };
99
- if (a.body) {
100
- init.body = a.body;
101
- init.headers["Content-Type"] = "application/x-www-form-urlencoded";
102
- }
103
- const r = await fetch(a.url, init);
104
- const text = await r.text();
105
- let j = null;
106
- try { j = JSON.parse(text); } catch (_) {}
107
- if (j && j.code === 0 && j.data) { j._via = "tikwm"; return j; }
108
- last = j ? (j.msg || ("code " + j.code)) : ("http " + r.status);
109
- } catch (e) { last = String(e); }
110
- }
111
- throw new Error("tikwm: " + last);
112
- }
113
-
114
- export default {
115
- async fetch(request) {
116
- const u = new URL(request.url);
117
- const target = u.searchParams.get("url");
118
- const proxy = u.searchParams.get("proxy");
119
- const form = u.searchParams.get("form"); // POST hộ tới site tải hộ
120
- const resolve = u.searchParams.get("resolve"); // giải link rút gọn -> URL cuối
121
- const only = u.searchParams.get("only"); // "ssstik" | "tikwm" | null
122
-
123
- try {
124
- // Giải link rút gọn (vt/vm.tiktok.com) -> URL canonical, theo redirect trên
125
- // IP sạch. Trả { code:0, url } để backend không phải gọi thẳng tiktok.com.
126
- if (resolve) {
127
- const r = await fetch(resolve, {
128
- method: "GET", redirect: "follow",
129
- headers: { "User-Agent": UA_MOBILE },
130
- });
131
- return jsonResp({ code: 0, url: r.url || resolve });
132
- }
133
-
134
- // POST hộ tới API của các SITE TẢI HỘ (savetik, tiktokio, snapsave,
135
- // lovetik). Backend không gọi thẳng được vì (a) IP Space bị Cloudflare của
136
- // họ chặn 403, (b) HF từng khoá Space vì "Suspicious domain call" khi máy
137
- // chủ gọi thẳng domain scraper. Mấy API này CHỈ nhận POST form-urlencoded
138
- // (thử GET trả 404), nên phải có endpoint riêng chứ ?proxy= không thay được.
139
- if (form) {
140
- const home = u.searchParams.get("ref") || "";
141
- const origin = home ? new URL(home).origin : "";
142
- const body = await request.text();
143
- const r = await fetch(form, {
144
- method: "POST",
145
- headers: {
146
- "User-Agent": UA_DESKTOP,
147
- "Content-Type": "application/x-www-form-urlencoded",
148
- "X-Requested-With": "XMLHttpRequest",
149
- ...(home ? { Referer: home } : {}),
150
- ...(origin ? { Origin: origin } : {}),
151
- },
152
- body,
153
- });
154
- return new Response(await r.text(), {
155
- status: r.status,
156
- headers: { ...CORS, "Content-Type": r.headers.get("content-type") || "text/plain" },
157
- });
158
- }
159
-
160
- // Tải hộ 1 file media (video/ảnh/nhạc) / trang share qua IP sạch.
161
- if (proxy) {
162
- // Referer PHẢI đúng theo nguồn: CDN Douyin (aweme.snssdk/douyinvod...)
163
- // chống hotlink — sai Referer -> 403. Douyin -> douyin.com; link proxy
164
- // của site tải hộ -> chính site đó; còn lại (tikcdn/tikwm) -> tikwm.com.
165
- const ref = /douyin|snssdk|iesdouyin|zjcdn|bytecdn/i.test(proxy)
166
- ? "https://www.douyin.com/"
167
- : /snapcdn\.app/i.test(proxy)
168
- ? "https://savetik.co/"
169
- : "https://www.tikwm.com/";
170
- // Chuyển tiếp Range: backend đo dung lượng thật bằng 'bytes=0-0' để chọn
171
- // bản nét nhất. Không chuyển tiếp thì mỗi lần đo là tải trọn cả file.
172
- const range = request.headers.get("Range");
173
- const r = await fetch(proxy, {
174
- headers: {
175
- "User-Agent": UA_MOBILE, "Referer": ref,
176
- ...(range ? { Range: range } : {}),
177
- },
178
- });
179
- const pass = { ...CORS,
180
- "Content-Type": r.headers.get("content-type") || "application/octet-stream" };
181
- for (const h of ["content-length", "content-range", "accept-ranges"]) {
182
- const v = r.headers.get(h);
183
- if (v) pass[h] = v;
184
- }
185
- return new Response(r.body, { status: r.status, headers: pass });
186
- }
187
-
188
- if (target) {
189
- const chain = only === "ssstik" ? [viaSsstik]
190
- : only === "tikwm" ? [viaTikwm]
191
- : [viaSsstik, viaTikwm]; // ssstik trước (không dính quota tikwm)
192
- const errs = [];
193
- for (const fn of chain) {
194
- try {
195
- const out = await fn(target);
196
- if (out && out.code === 0 && out.data) return jsonResp(out);
197
- errs.push((out && out.msg) || "no data");
198
- } catch (e) { errs.push(String(e)); }
199
- }
200
- return jsonResp({ code: -1, msg: errs.join(" | ") });
201
- }
202
-
203
- return jsonResp({ code: -1, msg: "thiếu tham số ?url= / ?proxy= / ?form= / ?resolve=" }, 400);
204
- } catch (e) {
205
- return jsonResp({ code: -1, msg: String(e) }, 502);
206
- }
207
- },
208
- };