kavion commited on
Commit
9b5e11b
·
unverified ·
1 Parent(s): 0daf705

Fix: Support TikTok photo posts by resolving redirects and normalizing /photo/ to /video/

Browse files
Files changed (2) hide show
  1. app.py +27 -10
  2. utils.py +15 -2
app.py CHANGED
@@ -4,7 +4,7 @@ import time
4
  from flask import Flask, request, send_file
5
 
6
  from config import EXPIRE_SECONDS, API_LIST
7
- from utils import jsonify, validate_tiktok_url
8
  from cache import video_path, save_meta, load_meta, cleanup_expired
9
  from extractor import extract_media_info, download_video
10
 
@@ -23,6 +23,7 @@ def tiktok():
23
  cleanup_expired()
24
 
25
  url = request.args.get("url")
 
26
 
27
  if not url:
28
  return jsonify({
@@ -31,6 +32,13 @@ def tiktok():
31
  "example": "/api/tiktok?url=https://www.tiktok.com/@user/video/1234567890"
32
  }), 400
33
 
 
 
 
 
 
 
 
34
  if not validate_tiktok_url(url):
35
  return jsonify({
36
  "status": "error",
@@ -43,19 +51,23 @@ def tiktok():
43
  ]
44
  }), 400
45
 
46
- # 1. Ambil metadata
47
  info = extract_media_info(url, platform="tiktok")
48
  if info.get("error"):
49
  return jsonify({"status": "error", "message": info["message"]}), 500
50
 
51
- # 2. Download video ke temp file
 
 
 
 
 
 
52
  try:
53
  file_id, file_path = download_video(url)
54
  except Exception as e:
55
  return jsonify({"status": "error", "message": str(e)}), 500
56
 
57
- raw = info["raw_data"]
58
-
59
  # Deteksi author & nickname
60
  author = raw.get("uploader_id")
61
  if not author:
@@ -83,9 +95,14 @@ def tiktok():
83
  audio_url = fmt.get("url")
84
  break
85
 
86
- # Deteksi slideshow / photo
87
- images = raw.get("images", [])
88
- photo = images if images else False
 
 
 
 
 
89
 
90
  # Konversi durasi ke format M:SS atau H:MM:SS
91
  duration_secs = raw.get("duration")
@@ -104,14 +121,14 @@ def tiktok():
104
  except Exception:
105
  duration_str = str(duration_secs)
106
 
107
- # 3. Simpan metadata ke disk (filesystem — aman lintas worker)
108
  title = raw.get("title") or raw.get("description") or "video"
109
  safe_title = re.sub(r'[^\w\s-]', '', title).strip()[:60] or "video"
110
  filename = f"{safe_title}.mp4"
111
  expires_at = time.time() + EXPIRE_SECONDS
112
  save_meta(file_id, filename, expires_at)
113
 
114
- # 4. Susun response
115
  base_url = request.host_url.rstrip("/")
116
  video_url = f"{base_url}/api/file/{file_id}"
117
 
 
4
  from flask import Flask, request, send_file
5
 
6
  from config import EXPIRE_SECONDS, API_LIST
7
+ from utils import jsonify, validate_tiktok_url, resolve_url
8
  from cache import video_path, save_meta, load_meta, cleanup_expired
9
  from extractor import extract_media_info, download_video
10
 
 
23
  cleanup_expired()
24
 
25
  url = request.args.get("url")
26
+ debug_mode = request.args.get("debug")
27
 
28
  if not url:
29
  return jsonify({
 
32
  "example": "/api/tiktok?url=https://www.tiktok.com/@user/video/1234567890"
33
  }), 400
34
 
35
+ # 1. Resolve redirect URL pendek (seperti vt.tiktok.com)
36
+ url = resolve_url(url)
37
+
38
+ # 2. Normalisasi URL photo ke video agar didukung oleh yt-dlp
39
+ if "/photo/" in url:
40
+ url = url.replace("/photo/", "/video/")
41
+
42
  if not validate_tiktok_url(url):
43
  return jsonify({
44
  "status": "error",
 
51
  ]
52
  }), 400
53
 
54
+ # 3. Ambil metadata
55
  info = extract_media_info(url, platform="tiktok")
56
  if info.get("error"):
57
  return jsonify({"status": "error", "message": info["message"]}), 500
58
 
59
+ raw = info["raw_data"]
60
+
61
+ # Endpoint debug untuk melihat raw data yt-dlp langsung
62
+ if debug_mode in ["1", "true"]:
63
+ return jsonify(raw), 200
64
+
65
+ # 4. Download video/slideshow mp4 ke temp file
66
  try:
67
  file_id, file_path = download_video(url)
68
  except Exception as e:
69
  return jsonify({"status": "error", "message": str(e)}), 500
70
 
 
 
71
  # Deteksi author & nickname
72
  author = raw.get("uploader_id")
73
  if not author:
 
95
  audio_url = fmt.get("url")
96
  break
97
 
98
+ # Deteksi slideshow / photo dari thumbnails
99
+ photo = False
100
+ thumbnails = raw.get("thumbnails", [])
101
+ if thumbnails:
102
+ photo_urls = [t.get("url") for t in thumbnails if t.get("url")]
103
+ # Jika ada lebih dari 1 thumbnail, biasanya itu adalah slide gambarnya
104
+ if len(photo_urls) > 1:
105
+ photo = photo_urls
106
 
107
  # Konversi durasi ke format M:SS atau H:MM:SS
108
  duration_secs = raw.get("duration")
 
121
  except Exception:
122
  duration_str = str(duration_secs)
123
 
124
+ # 5. Simpan metadata ke disk (filesystem — aman lintas worker)
125
  title = raw.get("title") or raw.get("description") or "video"
126
  safe_title = re.sub(r'[^\w\s-]', '', title).strip()[:60] or "video"
127
  filename = f"{safe_title}.mp4"
128
  expires_at = time.time() + EXPIRE_SECONDS
129
  save_meta(file_id, filename, expires_at)
130
 
131
+ # 6. Susun response
132
  base_url = request.host_url.rstrip("/")
133
  video_url = f"{base_url}/api/file/{file_id}"
134
 
utils.py CHANGED
@@ -1,5 +1,6 @@
1
  import json
2
  import re
 
3
  from flask import make_response
4
 
5
  def jsonify(data, status=200):
@@ -9,10 +10,22 @@ def jsonify(data, status=200):
9
  return response
10
 
11
  def validate_tiktok_url(url):
12
- """Validasi apakah URL adalah URL TikTok yang valid."""
13
  patterns = [
14
- r'https?://(www\.)?tiktok\.com/@[\w.]+/video/\d+/?',
15
  r'https?://(vm|vt)\.tiktok\.com/[\w]+/?',
16
  r'https?://(www\.)?tiktok\.com/t/[\w]+/?',
17
  ]
18
  return any(re.match(pattern, url) for pattern in patterns)
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import re
3
+ import urllib.request
4
  from flask import make_response
5
 
6
  def jsonify(data, status=200):
 
10
  return response
11
 
12
  def validate_tiktok_url(url):
13
+ """Validasi apakah URL adalah URL TikTok yang valid (video atau photo)."""
14
  patterns = [
15
+ r'https?://(www\.)?tiktok\.com/@[\w.]+/(video|photo)/\d+/?',
16
  r'https?://(vm|vt)\.tiktok\.com/[\w]+/?',
17
  r'https?://(www\.)?tiktok\.com/t/[\w]+/?',
18
  ]
19
  return any(re.match(pattern, url) for pattern in patterns)
20
+
21
+ def resolve_url(url):
22
+ """Mengikuti redirect URL untuk mendapatkan URL final (misal dari vt.tiktok ke tiktok.com/@user/video/id)."""
23
+ try:
24
+ req = urllib.request.Request(
25
+ url,
26
+ headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
27
+ )
28
+ with urllib.request.urlopen(req, timeout=15) as response:
29
+ return response.geturl()
30
+ except Exception:
31
+ return url