|
|
import os |
|
|
import threading |
|
|
import subprocess |
|
|
import requests |
|
|
import time |
|
|
from flask import Flask, render_template, request, jsonify |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
|
HOME_DIR = os.path.expanduser('~') |
|
|
BASE_PICTURES = os.path.join(HOME_DIR, 'Pictures') |
|
|
|
|
|
|
|
|
status_data = { |
|
|
'text': 'Siap...', |
|
|
'percent': 0, |
|
|
'status': 'idle' |
|
|
} |
|
|
|
|
|
|
|
|
@app.route('/scan', methods=['POST']) |
|
|
def scan_url(): |
|
|
raw_url = request.json.get('url') |
|
|
if not raw_url: return jsonify({'status': 'error', 'message': 'URL kosong'}) |
|
|
|
|
|
|
|
|
if "tiktok.com" in raw_url: |
|
|
return jsonify({ |
|
|
'status': 'success', |
|
|
'mode': 'bulk_tiktok', |
|
|
'message': 'TikTok Slide Terdeteksi (Mode API)' |
|
|
}) |
|
|
|
|
|
|
|
|
try: |
|
|
command = ["gallery-dl", "-g", "--cookies-from-browser", "brave", raw_url] |
|
|
result = subprocess.run(command, capture_output=True, text=True) |
|
|
|
|
|
if result.returncode == 0: |
|
|
raw_links = result.stdout.strip().split('\n') |
|
|
valid_links = [link for link in raw_links if link.startswith('http')] |
|
|
if not valid_links: |
|
|
return jsonify({'status': 'error', 'message': 'Tidak ada gambar ditemukan.'}) |
|
|
return jsonify({ |
|
|
'status': 'success', |
|
|
'mode': 'select_ig', |
|
|
'images': valid_links, |
|
|
'count': len(valid_links) |
|
|
}) |
|
|
else: |
|
|
return jsonify({'status': 'error', 'message': "Gagal Scan IG (Cek Login Brave)"}) |
|
|
|
|
|
except Exception as e: |
|
|
return jsonify({'status': 'error', 'message': str(e)}) |
|
|
|
|
|
|
|
|
|
|
|
def run_tiktok_api(url): |
|
|
global status_data |
|
|
status_data['status'] = 'downloading' |
|
|
|
|
|
TARGET_FOLDER = os.path.join(BASE_PICTURES, 'TikTok') |
|
|
if not os.path.exists(TARGET_FOLDER): os.makedirs(TARGET_FOLDER) |
|
|
|
|
|
try: |
|
|
status_data['percent'] = 10 |
|
|
status_data['text'] = "Menghubungi Server TikWM..." |
|
|
|
|
|
|
|
|
|
|
|
api_url = "https://www.tikwm.com/api/" |
|
|
payload = {'url': url, 'hd': 1} |
|
|
|
|
|
req = requests.post(api_url, data=payload) |
|
|
resp = req.json() |
|
|
|
|
|
image_urls = [] |
|
|
|
|
|
|
|
|
if resp.get('code') == 0: |
|
|
data = resp.get('data', {}) |
|
|
if 'images' in data: |
|
|
image_urls = data['images'] |
|
|
else: |
|
|
|
|
|
if 'cover' in data: image_urls.append(data['cover']) |
|
|
else: |
|
|
raise Exception("Gagal mengambil data dari API (Link Private/Salah).") |
|
|
|
|
|
if not image_urls: |
|
|
raise Exception("Tidak ada gambar ditemukan.") |
|
|
|
|
|
|
|
|
total = len(image_urls) |
|
|
print(f"API Berhasil: Ditemukan {total} slide.") |
|
|
|
|
|
|
|
|
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" |
|
|
} |
|
|
|
|
|
success_count = 0 |
|
|
for i, img_url in enumerate(image_urls): |
|
|
current = i + 1 |
|
|
status_data['percent'] = (current / total) * 100 |
|
|
status_data['text'] = f"Menyimpan Slide {current} dari {total}..." |
|
|
|
|
|
try: |
|
|
response = requests.get(img_url, headers=headers, stream=True) |
|
|
if response.status_code == 200: |
|
|
filename = f"TK_{int(time.time())}_{current}.jpg" |
|
|
full_path = os.path.join(TARGET_FOLDER, filename) |
|
|
|
|
|
with open(full_path, 'wb') as f: |
|
|
for chunk in response.iter_content(1024): |
|
|
f.write(chunk) |
|
|
success_count += 1 |
|
|
except: pass |
|
|
|
|
|
status_data['status'] = 'finished' |
|
|
status_data['percent'] = 100 |
|
|
status_data['text'] = f"Selesai! {success_count} slide tersimpan." |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error TikTok API: {e}") |
|
|
status_data['status'] = 'error' |
|
|
status_data['text'] = "Gagal memproses (Server API sibuk/Link salah)" |
|
|
|
|
|
|
|
|
|
|
|
def run_ig_manual(selected_urls): |
|
|
global status_data |
|
|
status_data['status'] = 'downloading' |
|
|
total = len(selected_urls) |
|
|
TARGET_FOLDER = os.path.join(BASE_PICTURES, 'Instagram') |
|
|
if not os.path.exists(TARGET_FOLDER): os.makedirs(TARGET_FOLDER) |
|
|
|
|
|
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"} |
|
|
|
|
|
for i, img_url in enumerate(selected_urls): |
|
|
try: |
|
|
current = i + 1 |
|
|
status_data['percent'] = (current / total) * 100 |
|
|
status_data['text'] = f"Menyimpan ke Instagram ({current}/{total})..." |
|
|
response = requests.get(img_url, headers=headers, stream=True) |
|
|
if response.status_code == 200: |
|
|
filename = f"IG_{int(time.time())}_{current}.jpg" |
|
|
full_path = os.path.join(TARGET_FOLDER, filename) |
|
|
with open(full_path, 'wb') as f: |
|
|
for chunk in response.iter_content(1024): |
|
|
f.write(chunk) |
|
|
except: pass |
|
|
|
|
|
status_data['status'] = 'finished' |
|
|
status_data['percent'] = 100 |
|
|
status_data['text'] = "Selesai! Cek folder Pictures/Instagram" |
|
|
|
|
|
|
|
|
@app.route('/') |
|
|
def home(): |
|
|
return render_template('index.html') |
|
|
|
|
|
@app.route('/action', methods=['POST']) |
|
|
def action(): |
|
|
data = request.json |
|
|
mode = data.get('mode') |
|
|
|
|
|
if mode == 'tiktok': |
|
|
url = data.get('url') |
|
|
t = threading.Thread(target=run_tiktok_api, args=(url,)) |
|
|
t.start() |
|
|
elif mode == 'instagram': |
|
|
urls = data.get('urls') |
|
|
t = threading.Thread(target=run_ig_manual, args=(urls,)) |
|
|
t.start() |
|
|
|
|
|
return jsonify({'status': 'started'}) |
|
|
|
|
|
@app.route('/status') |
|
|
def status(): |
|
|
return jsonify(status_data) |
|
|
|
|
|
if __name__ == '__main__': |
|
|
app.run(debug=True, port=5001, host='0.0.0.0') |
|
|
|