Update List
This commit is contained in:
296
digi_auth_extractor.py
Normal file
296
digi_auth_extractor.py
Normal file
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
DIGI Online Authentication & Playlist Generator
|
||||
Extrage token-uri de autentificare și generează playlist M3U funcțional
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
class DigiOnlineAuth:
|
||||
def __init__(self):
|
||||
self.base_url = "https://www.digionline.ro"
|
||||
self.api_url = "https://www.digionline.ro/api"
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'ro-RO,ro;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Connection': 'keep-alive',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"'
|
||||
})
|
||||
self.token = None
|
||||
self.device_id = None
|
||||
self.channels = []
|
||||
|
||||
def get_homepage(self):
|
||||
"""Accesează homepage-ul pentru a obține cookies"""
|
||||
print(f"🌐 Accesare homepage DIGI Online...")
|
||||
|
||||
try:
|
||||
response = self.session.get(self.base_url, timeout=10)
|
||||
if response.status_code == 200:
|
||||
print("✅ Homepage accesat cu succes!")
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ Status homepage: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Eroare la accesare homepage: {e}")
|
||||
return False
|
||||
|
||||
def login(self, email, password):
|
||||
"""Autentificare pe DIGI Online"""
|
||||
print(f"\n🔐 Autentificare pe DIGI Online...")
|
||||
print(f"📧 Email: {email}")
|
||||
|
||||
# Mai întâi accesează homepage-ul pentru cookies
|
||||
self.get_homepage()
|
||||
|
||||
# Actualizează headers pentru login
|
||||
self.session.headers.update({
|
||||
'Origin': 'https://www.digionline.ro',
|
||||
'Referer': 'https://www.digionline.ro/',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
|
||||
login_url = f"{self.base_url}/api/user/login"
|
||||
|
||||
payload = {
|
||||
"email": email,
|
||||
"password": password
|
||||
}
|
||||
|
||||
try:
|
||||
print(f"📡 Trimitere cerere de autentificare...")
|
||||
response = self.session.post(login_url, json=payload, timeout=15)
|
||||
|
||||
print(f"📊 Status răspuns: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
data = response.json()
|
||||
|
||||
if 'data' in data and 'token' in data['data']:
|
||||
self.token = data['data']['token']
|
||||
print("✅ Autentificare reușită!")
|
||||
print(f"🔑 Token obținut: {self.token[:20]}...")
|
||||
|
||||
# Actualizează header-ul cu token-ul
|
||||
self.session.headers.update({
|
||||
'Authorization': f'Bearer {self.token}'
|
||||
})
|
||||
|
||||
return True
|
||||
else:
|
||||
print("❌ Răspuns invalid de la server")
|
||||
print(f"Răspuns: {data}")
|
||||
return False
|
||||
except json.JSONDecodeError:
|
||||
print("❌ Răspuns nu este JSON valid")
|
||||
print(f"Răspuns: {response.text[:200]}")
|
||||
return False
|
||||
elif response.status_code == 403:
|
||||
print(f"❌ Acces interzis (403)!")
|
||||
print(f"⚠️ API-ul DIGI blochează cererea automată.")
|
||||
print(f"\n💡 SOLUȚIE ALTERNATIVĂ:")
|
||||
print(f" 1. Deschide browser și mergi pe digionline.ro")
|
||||
print(f" 2. Autentifică-te manual")
|
||||
print(f" 3. Deschide Developer Tools (F12)")
|
||||
print(f" 4. Mergi la Network tab")
|
||||
print(f" 5. Caută cererea 'login' și copiază token-ul")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ Autentificare eșuată! Status: {response.status_code}")
|
||||
print(f"Răspuns: {response.text[:500]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Eroare la autentificare: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def get_channels(self):
|
||||
"""Obține lista de canale disponibile"""
|
||||
print("\n📺 Obținere listă canale...")
|
||||
|
||||
channels_url = f"{self.api_url}/channel/list"
|
||||
|
||||
try:
|
||||
response = self.session.get(channels_url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
if 'data' in data:
|
||||
self.channels = data['data']
|
||||
print(f"✅ {len(self.channels)} canale găsite!")
|
||||
return True
|
||||
else:
|
||||
print("❌ Răspuns invalid de la server")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ Eroare la obținere canale! Status: {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Eroare la obținere canale: {e}")
|
||||
return False
|
||||
|
||||
def get_stream_url(self, channel_id):
|
||||
"""Obține URL-ul de stream pentru un canal"""
|
||||
stream_url = f"{self.api_url}/stream/start/{channel_id}"
|
||||
|
||||
try:
|
||||
response = self.session.get(stream_url, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
if 'data' in data and 'stream_url' in data['data']:
|
||||
return data['data']['stream_url']
|
||||
elif 'data' in data and 'url' in data['data']:
|
||||
return data['data']['url']
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Eroare la obținere stream pentru canal {channel_id}: {e}")
|
||||
return None
|
||||
|
||||
def generate_playlist(self, output_file="digi_authenticated.m3u"):
|
||||
"""Generează playlist M3U cu URL-uri autentificate"""
|
||||
print(f"\n📝 Generare playlist: {output_file}")
|
||||
|
||||
if not self.channels:
|
||||
print("❌ Nu există canale disponibile!")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
# Header M3U
|
||||
f.write("#EXTM3U\n\n")
|
||||
f.write("# ========================================\n")
|
||||
f.write("# PLAYLIST DIGI ONLINE - AUTENTIFICAT\n")
|
||||
f.write(f"# Generat: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write(f"# Canale: {len(self.channels)}\n")
|
||||
f.write("# ========================================\n\n")
|
||||
|
||||
# Procesează fiecare canal
|
||||
processed = 0
|
||||
for channel in self.channels:
|
||||
channel_id = channel.get('id')
|
||||
channel_name = channel.get('name', 'Unknown')
|
||||
channel_logo = channel.get('logo', '')
|
||||
category = channel.get('category', 'General')
|
||||
|
||||
print(f" 📡 Procesare: {channel_name}...", end='')
|
||||
|
||||
# Obține URL-ul de stream
|
||||
stream_url = self.get_stream_url(channel_id)
|
||||
|
||||
if stream_url:
|
||||
# Scrie în playlist
|
||||
f.write(f'#EXTINF:-1 tvg-id="{channel_id}" tvg-name="{channel_name}" ')
|
||||
f.write(f'tvg-logo="{channel_logo}" group-title="{category}",{channel_name}\n')
|
||||
f.write(f'{stream_url}\n\n')
|
||||
|
||||
processed += 1
|
||||
print(" ✅")
|
||||
else:
|
||||
print(" ❌ (URL indisponibil)")
|
||||
|
||||
# Footer
|
||||
f.write("# ========================================\n")
|
||||
f.write(f"# Total canale procesate: {processed}/{len(self.channels)}\n")
|
||||
f.write("# ========================================\n")
|
||||
|
||||
print(f"\n✅ Playlist generat cu succes!")
|
||||
print(f"📁 Fișier: {output_file}")
|
||||
print(f"📺 Canale funcționale: {processed}/{len(self.channels)}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Eroare la generare playlist: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Funcția principală"""
|
||||
print("=" * 60)
|
||||
print("🎬 DIGI ONLINE - EXTRACTOR AUTENTIFICAT")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Citește credențialele
|
||||
print("📋 Introdu credențialele DIGI Online:")
|
||||
print("⚠️ NOTĂ: Credențialele sunt folosite DOAR pentru autentificare")
|
||||
print("⚠️ pe digionline.ro oficial. Nu sunt trimise nicăieri altundeva!\n")
|
||||
|
||||
email = input("📧 Email: ").strip()
|
||||
|
||||
if not email:
|
||||
print("❌ Email-ul este obligatoriu!")
|
||||
sys.exit(1)
|
||||
|
||||
# Importă getpass pentru parolă ascunsă
|
||||
try:
|
||||
from getpass import getpass
|
||||
password = getpass("🔒 Parolă: ")
|
||||
except:
|
||||
password = input("🔒 Parolă: ").strip()
|
||||
|
||||
if not password:
|
||||
print("❌ Parola este obligatorie!")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
|
||||
# Creează instanța și autentifică
|
||||
digi = DigiOnlineAuth()
|
||||
|
||||
if not digi.login(email, password):
|
||||
print("\n❌ Autentificare eșuată! Verifică credențialele.")
|
||||
sys.exit(1)
|
||||
|
||||
# Obține lista de canale
|
||||
if not digi.get_channels():
|
||||
print("\n❌ Nu s-au putut obține canalele!")
|
||||
sys.exit(1)
|
||||
|
||||
# Generează playlist-ul
|
||||
output_file = "digi_authenticated.m3u"
|
||||
if digi.generate_playlist(output_file):
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 SUCCES!")
|
||||
print("=" * 60)
|
||||
print(f"\n📁 Playlist generat: {output_file}")
|
||||
print(f"📺 Folosește acest fișier în Dispatcharr/Jellyfin/VLC")
|
||||
print(f"\n💡 TIP: Token-ul expiră după câteva ore.")
|
||||
print(f" Rulează din nou scriptul pentru a reînnoi token-ul.\n")
|
||||
else:
|
||||
print("\n❌ Eroare la generare playlist!")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ Întrerupt de utilizator!")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Eroare neașteptată: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user