File size: 9,656 Bytes
25a0fe3
a4319d2
 
25a0fe3
 
 
a4319d2
 
 
 
25a0fe3
a4319d2
25a0fe3
a4319d2
25a0fe3
a4319d2
 
25a0fe3
a4319d2
 
 
 
25a0fe3
a4319d2
 
 
 
 
 
 
25a0fe3
a4319d2
 
 
 
 
25a0fe3
a4319d2
 
25a0fe3
a4319d2
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
a4319d2
 
 
 
25a0fe3
a4319d2
 
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
a4319d2
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
 
 
a4319d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25a0fe3
 
a4319d2
 
 
 
 
25a0fe3
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import os
import logging
import subprocess
import time
import threading
import requests
import json
import zipfile
import shutil
from pathlib import Path
from flask import Flask, request, jsonify
from huggingface_hub import HfApi

# --- AYARLAR ---
app = Flask(__name__)
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
logger = logging.getLogger(__name__)

# Hugging Face Environment Variables
SPACE_ID = os.getenv('SPACE_ID') # Örn: kullaniciadi/space-adi
HF_TOKEN = os.getenv('HF_TOKEN') # Write yetkili token
MEGA_ACCOUNTS_JSON = os.getenv('MEGA_ACCOUNTS_JSON', '[]') # Hesap listesi

# Hesapları Yükle
try:
    MEGA_ACCOUNTS = json.loads(MEGA_ACCOUNTS_JSON)
    logger.info(f"✅ {len(MEGA_ACCOUNTS)} Mega hesabı sisteme yüklendi.")
except Exception as e:
    MEGA_ACCOUNTS = []
    logger.warning(f"⚠️ Hesaplar yüklenemedi: {str(e)}")

current_account_index = 0
current_task = None
task_lock = threading.Lock()
DOWNLOAD_DIR = Path("/tmp/downloads")
DOWNLOAD_DIR.mkdir(exist_ok=True, parents=True)

# 2 Saat Timeout (Büyük dosyalar için)
MEGACMD_TIMEOUT = 7200

# --- YARDIMCI FONKSİYONLAR ---

def run_megacmd(command, timeout=30):
    """MegaCMD komutlarını çalıştırır ve çıktıları analiz eder."""
    try:
        if isinstance(command, str):
            command = command.split()
        
        logger.info(f"🔧 Komut: {' '.join(command)}")
        result = subprocess.run(command, capture_output=True, text=True, timeout=timeout)
        
        output = result.stdout.strip()
        error = result.stderr.strip()
        combined = (output + " " + error).lower()
        
        # IP BAN KONTROLÜ
        if any(k in combined for k in ['api_eblocked', 'temporary ban', 'connection refused', 'blocked', 'bus error']):
            logger.error(f"🚨 IP BAN ALGILANDI: {error}")
            return {'success': False, 'critical': True, 'error': error}
        
        # KOTA KONTROLÜ
        if 'bandwidth quota exceeded' in combined or 'transfer quota exceeded' in combined:
            logger.warning(f"📊 KOTA DOLDU: {error}")
            return {'success': False, 'bandwidth_exceeded': True, 'error': error}
        
        if result.returncode != 0:
            return {'success': False, 'error': error}
        
        return {'success': True, 'output': output}
    
    except subprocess.TimeoutExpired:
        return {'success': False, 'timeout': True, 'error': f'Timeout {timeout}s'}
    except Exception as e:
        return {'success': False, 'error': str(e)}

def init_megacmd_server():
    """Mega sunucusunu arka planda başlatır."""
    try:
        logger.info("🚀 MegaCMD server başlatılıyor...")
        subprocess.Popen(['mega-cmd-server'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        time.sleep(7) # Serverın kendine gelmesi için bekle
        return True
    except Exception as e:
        logger.error(f"❌ MegaCMD server hatası: {str(e)}")
        return False

def mega_login(email, password):
    """Mega hesabına giriş yapar."""
    try:
        logger.info(f"🔐 Giriş yapılıyor: {email}")
        run_megacmd(['mega-logout'], timeout=10)
        time.sleep(2)
        result = run_megacmd(['mega-login', email, password], timeout=45)
        if result['success']:
            logger.info(f"✅ Giriş başarılı: {email}")
            return True
        return False
    except:
        return False

def get_next_account():
    """Sıradaki hesabı getirir (Zombie Mode)."""
    global current_account_index
    if not MEGA_ACCOUNTS: return None
    account = MEGA_ACCOUNTS[current_account_index]
    current_account_index = (current_account_index + 1) % len(MEGA_ACCOUNTS)
    return account

def rotate_account():
    """Hesap değiştirir."""
    logger.warning("🔄 HESAP ROTASYONU BAŞLATILIYOR...")
    account = get_next_account()
    if not account: return False
    return mega_login(account['email'], account['password'])

def phoenix_restart():
    """IP Ban yiyince Space'i yeniden başlatır (Phoenix Mode)."""
    try:
        if not SPACE_ID or not HF_TOKEN:
            logger.error("❌ SPACE_ID veya HF_TOKEN eksik, restart atılamıyor!")
            return False
        
        logger.warning("🔥 PHOENIX MODE: IP Banlandı, Space restart ediliyor...")
        api = HfApi(token=HF_TOKEN)
        api.restart_space(repo_id=SPACE_ID)
        return True
    except Exception as e:
        logger.error(f"❌ Restart hatası: {str(e)}")
        return False

def send_webhook(webhook_url, data):
    """Render tarafına durum bildirir."""
    if not webhook_url: return
    try:
        requests.post(webhook_url, json=data, timeout=10)
    except:
        pass

# --- İŞLEM FONKSİYONU ---

def process_task(task_data):
    global current_task
    task_id = task_data['task_id']
    mega_url = task_data['mega_url']
    webhook_url = task_data.get('webhook_url')
    
    try:
        logger.info(f"🚀 İşlem Başladı: {task_id}")
        send_webhook(webhook_url, {'status': 'started', 'progress': 0})

        # 1. İlk hesapla giriş
        if MEGA_ACCOUNTS:
            acc = MEGA_ACCOUNTS[0]
            mega_login(acc['email'], acc['password'])
        
        # 2. İndirme Klasörü
        target_dir = DOWNLOAD_DIR / task_id
        target_dir.mkdir(exist_ok=True)
        
        # 3. İndirme Döngüsü
        max_retries = 10
        retry_count = 0
        success = False
        
        while retry_count < max_retries:
            send_webhook(webhook_url, {'status': 'downloading', 'progress': 10 + (retry_count * 2)})
            
            # mega-get komutu (Klasörler için recursive çalışır)
            result = run_megacmd(['mega-get', mega_url, str(target_dir)], timeout=MEGACMD_TIMEOUT)
            
            # 3.1 IP Ban Durumu
            if result.get('critical'):
                send_webhook(webhook_url, {'status': 'error', 'error': 'IP Ban - Restarting System'})
                phoenix_restart()
                return # Space kapanacak zaten
            
            # 3.2 Kota Dolma Durumu
            if result.get('bandwidth_exceeded'):
                logger.warning("📊 Kota doldu, hesap değiştiriliyor...")
                if rotate_account():
                    retry_count += 1
                    time.sleep(3)
                    continue # Döngüye devam et, kaldığı yerden indirir
                else:
                    raise Exception("Hesap rotasyonu başarısız, hesap kalmadı!")
            
            # 3.3 Başarılı
            if result['success']:
                success = True
                break
            
            # 3.4 Diğer Hatalar
            logger.error(f"İndirme hatası: {result.get('error')}")
            break # Bilinmeyen hatada döngüden çık
            
        if not success:
            raise Exception("İndirme tamamlanamadı.")

        # 4. ZIPLEME
        logger.info("📦 Zipleniyor...")
        send_webhook(webhook_url, {'status': 'zipping', 'progress': 80})
        
        zip_path = DOWNLOAD_DIR / f"{task_id}.zip"
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(target_dir):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, target_dir)
                    zipf.write(file_path, arcname)
        
        # 5. UPLOAD (BURAYI KENDİNE GÖRE DÜZENLE!)
        # Şu anlık sadece başarılı mesajı dönüyoruz. Dosya /tmp içinde.
        # Buraya Google Drive, Telegram veya AWS S3 upload kodu eklenmeli.
        upload_url = "https://dosya-yuklendi-varsayildi.com" 
        
        send_webhook(webhook_url, {
            'status': 'completed',
            'progress': 100,
            'download_url': upload_url
        })
        logger.info(f"✅ İşlem Tamam: {task_id}")

        # Temizlik
        shutil.rmtree(target_dir, ignore_errors=True)
        if os.path.exists(zip_path):
            os.remove(zip_path)

    except Exception as e:
        logger.error(f"🔥 Kritik Hata: {str(e)}")
        send_webhook(webhook_url, {'status': 'failed', 'error': str(e)})
    finally:
        with task_lock:
            current_task = None

# --- FLASK ENDPOINTS ---

@app.route('/health', methods=['GET'])
def health():
    """Render bu adrese ping atar, worker yaşıyor mu bakar."""
    with task_lock:
        is_busy = current_task is not None
    return jsonify({'status': 'healthy', 'busy': is_busy}), 200

@app.route('/process', methods=['POST'])
def process():
    """Render'dan gelen iş emrini alır."""
    try:
        with task_lock:
            if current_task is not None:
                return jsonify({'error': 'Worker şu an meşgul'}), 503
        
        data = request.get_json()
        task_id = f"task_{int(time.time())}"
        
        task_data = {
            'task_id': task_id,
            'mega_url': data['mega_url'],
            'webhook_url': data.get('webhook_url')
        }
        
        with task_lock:
            current_task = task_data
        
        # Arka planda başlat
        t = threading.Thread(target=process_task, args=(task_data,))
        t.daemon = True
        t.start()
        
        return jsonify({'success': True, 'task_id': task_id}), 200
    
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    # MegaCMD'yi başlat
    if not init_megacmd_server():
        exit(1)
    
    # Flask'ı başlat
    app.run(host='0.0.0.0', port=7860)