|
''' |
|
Sumber = https://gist.github.com/Xnuvers007/ac52ba42187d9758ec9a9f5f1174b40c |
|
pembuat : Xnuvers007 |
|
''' |
|
|
|
import sys, subprocess, platform, os, socket, re |
|
|
|
def clear_screen(): |
|
try: |
|
if platform.system() == "Windows": |
|
os.system("cls") |
|
else: |
|
os.system("clear") |
|
except Exception as e: |
|
print(f"Terjadi kesalahan saat membersihkan layar: {e}") |
|
|
|
try: |
|
import requests, random, re, time, urllib3, html, logging |
|
from bs4 import BeautifulSoup |
|
from urllib.parse import urlparse, quote |
|
except ImportError as e: |
|
missing_module = str(e).split()[-1] |
|
print(f"Modul {missing_module} tidak ditemukan. Mencoba menginstal...") |
|
|
|
try: |
|
subprocess.check_call([sys.executable, "-m", "pip", "install", missing_module]) |
|
globals()[missing_module] = __import__(missing_module) |
|
print(f"Berhasil menginstal dan mengimpor {missing_module}.") |
|
except subprocess.CalledProcessError as install_error: |
|
print(f"Terjadi kesalahan saat menginstal {missing_module}: {install_error}") |
|
except socket.gaierror as network_error: |
|
print(f"Kesalahan jaringan: Tidak dapat menginstal {missing_module}. Periksa koneksi internet Anda. {network_error}") |
|
except Exception as e: |
|
print(f"Terjadi kesalahan tak terduga saat menginstal {missing_module}: {e}") |
|
finally: |
|
print("Pemeriksaan instalasi selesai.") |
|
|
|
clear_screen() |
|
|
|
logging.basicConfig(filename="app.log", level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') |
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) |
|
|
|
time.sleep(random.uniform(1, 3)) |
|
|
|
listofuseragents = [ |
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.3", |
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.3", |
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", |
|
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:88.0) Gecko/20100101 Firefox/88.0", |
|
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0", |
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0", |
|
"Mozilla/5.0 (Linux; Android 10; Pixel 4 Build/QD1A.200205.003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.119 Mobile Safari/537.36", |
|
"Mozilla/5.0 (Linux; Android 11; Samsung Galaxy S21 Build/RP1A.200720.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.142 Mobile Safari/537.36", |
|
"Mozilla/5.0 (Linux; Android 9; OnePlus 6T Build/PKQ1.180904.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", |
|
"Mozilla/5.0 (Linux; Android 10; Xiaomi Mi 9 Build/QKQ1.190828.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Mobile Safari/537.36", |
|
"Mozilla/5.0 (Linux; Android 8.1.0; Huawei P30 Pro Build/HUAWEIP30Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Mobile Safari/537.36" |
|
] |
|
|
|
ALLOWED_DOMAINS = [ |
|
"doramaindo.ai", |
|
"midori.doramaindo.ai", |
|
"doramaindo.id", |
|
] |
|
|
|
denganlist = "https://midori.doramaindo.ai/drama-list" |
|
|
|
headers = { |
|
'User-Agent': random.choice(listofuseragents), |
|
'X-Content-Type-Options': 'nosniff', |
|
'X-Frame-Options': 'DENY', |
|
'Referrer-Policy': 'no-referrer', |
|
'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7', |
|
'Connection': 'keep-alive' |
|
} |
|
|
|
panjang_karakter = 100 |
|
|
|
def is_scraping_allowed(url): |
|
""" |
|
Memeriksa apakah scraping diizinkan melalui robots.txt. |
|
Jika tidak dapat mengakses robots.txt, default = diizinkan. |
|
""" |
|
parsed_url = urlparse(url) |
|
robots_url = f"{parsed_url.scheme}://{parsed_url.netloc}/robots.txt" |
|
try: |
|
resp = requests.get(robots_url, headers=headers, timeout=5, verify=True) |
|
resp.raise_for_status() |
|
time.sleep(random.uniform(1, 3)) |
|
|
|
user_agentforrobots = re.compile(r"User-agent:\s+\*") |
|
disallow = re.compile(r"Disallow:\s+") |
|
|
|
for line in resp.text.splitlines(): |
|
if re.match(user_agentforrobots, line): |
|
for line in resp.text.splitlines(): |
|
if re.match(disallow, line): |
|
return False |
|
return True |
|
return True |
|
|
|
# if "Disallow: /" in resp.text: |
|
# return False |
|
# return True |
|
except requests.exceptions.RequestException as e: |
|
logging.info(f"Gagal mengakses robots.txt: {e}") |
|
return True |
|
except Exception as e: |
|
logging.info(f"Gagal mengakses robots.txt: {e}") |
|
return True |
|
|
|
|
|
class Doramaindo: |
|
|
|
@staticmethod |
|
def renameFileFromDoramaIndo(): |
|
directory = input("Masukkan path folder: ").strip() |
|
|
|
directory = os.path.normpath(directory) |
|
|
|
if not os.path.isdir(directory): |
|
print("Directory not found.") |
|
exit() |
|
|
|
try: |
|
files = os.listdir(directory) |
|
except OSError as e: |
|
print(f"Error accessing directory: {e}") |
|
exit() |
|
|
|
files = [file for file in files if re.match(r'\[doramaindo\.(ai|id)\].*\.mp4', file)] |
|
|
|
try: |
|
files = sorted(files, key=lambda x: int(re.search(r'E(\d+)', x).group(1))) |
|
except AttributeError as e: |
|
print(f"Error sorting files: {e}") |
|
exit() |
|
|
|
for file in files: |
|
match = re.match(r'\[doramaindo\.(ai|id)\](.*)\.E(\d+)\.mp4', file) |
|
if match: |
|
series_name = match.group(2).replace('.', ' ').strip() |
|
episode_number = match.group(3) |
|
|
|
new_name = f"{series_name}S1-E{episode_number}-720p.mp4" |
|
|
|
new_name = re.sub(r'[<>:"/\\|?*]', '', new_name) |
|
|
|
old_file = os.path.join(directory, file) |
|
new_file = os.path.join(directory, new_name) |
|
|
|
try: |
|
os.rename(old_file, new_file) |
|
print(f"Renamed: {file} -> {new_name}") |
|
except OSError as e: |
|
print(f"Error renaming file {file}: {e}") |
|
|
|
print("Renaming completed.") |
|
|
|
@staticmethod |
|
def validate_url(url): |
|
regex = re.compile(r'^(https?://)?[a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+') |
|
if not url.startswith("https://"): |
|
return False |
|
|
|
parsed_url = urlparse(url) |
|
if not is_scraping_allowed(url): |
|
return False |
|
# return re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc) is not None |
|
# if not re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc or re.fullmatch(regex, parsed_url.scheme + '://' + parsed_url.netloc)): |
|
if not re.match(regex, parsed_url.scheme + '://' + parsed_url.netloc): |
|
return False |
|
|
|
domain = parsed_url.netloc |
|
if domain in ALLOWED_DOMAINS: |
|
return True |
|
|
|
if re.match(r"^\d{1,3}(\.\d{1,3}){3}$", parsed_url.netloc): |
|
return False |
|
|
|
return False |
|
|
|
@staticmethod |
|
def validate_name(name): |
|
sanitized_name = re.sub(r"[^a-zA-Z0-9\s()]", "", name) |
|
regex = re.compile(r'^[a-zA-Z0-9\s()]+$') |
|
|
|
if len(sanitized_name) > panjang_karakter: |
|
print("Nama terlalu panjang. Max 100 karakter.") |
|
return None |
|
|
|
if re.match(regex, sanitized_name) and re.fullmatch(regex, sanitized_name): |
|
return sanitized_name.strip() |
|
else: |
|
return None |
|
|
|
@staticmethod |
|
def getlinkdownload(): |
|
try: |
|
url = input("Masukkan URL: ").strip() |
|
url = html.escape(url) |
|
url = quote(url, safe=":/?=&") |
|
if not Doramaindo.validate_url(url): |
|
print("URL tidak valid.") |
|
return |
|
if not is_scraping_allowed(url): |
|
print("Scraping tidak diizinkan oleh robots.txt.") |
|
return |
|
except ValueError: |
|
print("URL tidak valid.") |
|
return |
|
except TypeError: |
|
print("Terjadi kesalahan tipe data.") |
|
return |
|
except Exception as e: |
|
logging.error(f"Input URL error: {e}") |
|
print(f"Terjadi kesalahan: {e}") |
|
return |
|
|
|
try: |
|
req = requests.get(url, headers=headers, timeout=10, verify=True) |
|
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False) |
|
req.raise_for_status() |
|
time.sleep(random.uniform(1, 3)) |
|
|
|
soup = BeautifulSoup(req.text, 'html.parser') |
|
|
|
sinopre = re.compile(r"Sinopsis\s+") |
|
statre = re.compile(r"Status\s+") |
|
genrere = re.compile(r"Genre\s+") |
|
seasre = re.compile(r"Seasons\s+") |
|
typere = re.compile(r"Type\s+") |
|
|
|
image_tag = soup.find('img', itemprop='image') |
|
if image_tag: |
|
image_url = image_tag['src'] |
|
print(f"Drama Image: {image_url}\n") |
|
|
|
sinopsis_div = soup.find('p', string=sinopre) |
|
if sinopsis_div: |
|
# sinopsis = sinopsis_div.find_next('p').text.strip() |
|
sinopsis = sinopsis_div.find_next('p').get_text(strip=True) |
|
print(f"Sinopsis: {sinopsis}\n") |
|
|
|
status = soup.find('b', string=statre) |
|
if status: |
|
# status_text = status.find_next('a').text.strip() |
|
status_text = status.find_next('a').get_text(strip=True) |
|
print(f"Status: {status_text}") |
|
|
|
genre = soup.find('b', string=genrere) |
|
if genre: |
|
# genre_text = genre.find_next('a').text.strip() |
|
genre_text = genre.find_next('a').get_text(strip=True) |
|
print(f"Genre: {genre_text}") |
|
|
|
seasons = soup.find('b', string=seasre) |
|
if seasons: |
|
# seasons_text = seasons.find_next('a').text.strip() |
|
seasons_text = seasons.find_next('a').get_text(strip=True) |
|
print(f"Seasons: {seasons_text}") |
|
|
|
type_tag = soup.find('b', string=typere) |
|
if type_tag: |
|
# type_text = type_tag.find_next('a').text.strip() |
|
type_text = type_tag.find_next('a').get_text(strip=True) |
|
print(f"Type: {type_text}") |
|
|
|
episode_list_div = soup.find('div', class_='episodelist') |
|
if episode_list_div: |
|
for li in episode_list_div.find_all('li'): |
|
a_tag = li.find('a', href=True) |
|
if a_tag: |
|
download_link = a_tag['href'] |
|
print(f"Advanced Link: {download_link}\n\n") |
|
else: |
|
print("Tidak ada episode ditemukan.") |
|
return |
|
|
|
try: |
|
# download_req = requests.get(download_link, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False) |
|
download_req = requests.get(download_link, headers=headers, timeout=10, verify=True) |
|
download_req.raise_for_status() |
|
time.sleep(random.uniform(1, 3)) |
|
|
|
download_soup = BeautifulSoup(download_req.text, 'html.parser') |
|
episode_list_div = download_soup.find('div', id='asw') |
|
if episode_list_div: |
|
download_links = set() |
|
for li in episode_list_div.find_all('li'): |
|
a_tag = li.find('a', href=True) |
|
if a_tag: |
|
download_link = a_tag['href'] |
|
download_links.add(download_link) |
|
|
|
for link in sorted(download_links): |
|
print(f"Download Link: {link}") |
|
print("-" * 50) |
|
|
|
except requests.exceptions.RequestException as e: |
|
print(f"Terjadi kesalahan saat mengunduh: {e}") |
|
except requests.exceptions.Timeout: |
|
print("Permintaan melebihi batas waktu.") |
|
except requests.exceptions.SSLError: |
|
print("Verifikasi SSL gagal.") |
|
except requests.exceptions.HTTPError as e: |
|
print(f"Terjadi kesalahan HTTP: {e}") |
|
except requests.exceptions.MissingSchema: |
|
print("Format URL tidak valid.") |
|
except Exception as e: |
|
print(f"Terjadi kesalahan: {e}") |
|
|
|
except requests.exceptions.RequestException as e: |
|
print(f"Terjadi kesalahan: {e}") |
|
except requests.exceptions.Timeout: |
|
print("Permintaan melebihi batas waktu.") |
|
except requests.exceptions.SSLError: |
|
print("Verifikasi SSL gagal.") |
|
except requests.exceptions.HTTPError as e: |
|
print(f"Terjadi kesalahan HTTP: {e}") |
|
except requests.exceptions.MissingSchema: |
|
print("Format URL tidak valid.") |
|
except Exception as e: |
|
print(f"Terjadi kesalahan: {e}") |
|
|
|
@staticmethod |
|
def WithName(): |
|
try: |
|
nama = input("Masukkan nama Drama: ").strip() |
|
sanitized_name = Doramaindo.validate_name(nama) |
|
|
|
if sanitized_name is None: |
|
print("Nama tidak valid.") |
|
return |
|
|
|
except ValueError: |
|
print("Nama tidak valid.") |
|
return |
|
except TypeError: |
|
print("Terjadi kesalahan tipe data.") |
|
return |
|
except Exception as e: |
|
logging.error(f"Input nama error: {e}") |
|
print(f"Terjadi kesalahan: {e}") |
|
return |
|
|
|
url = f"https://midori.doramaindo.ai/?s={quote(sanitized_name)}&post_type=series" |
|
try: |
|
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False) |
|
req = requests.get(url, headers=headers, timeout=10, verify=True) |
|
req.raise_for_status() |
|
time.sleep(random.uniform(1, 3)) |
|
|
|
soup = BeautifulSoup(req.text, 'html.parser') |
|
|
|
results = soup.find_all('div', class_='resultnime') |
|
|
|
if not results: |
|
print("Tidak ada hasil ditemukan.") |
|
return |
|
|
|
for idx, result in enumerate(results, 1): |
|
title_tag = result.find('h2').find('a') |
|
title = title_tag.text.strip() if title_tag else "No title found" |
|
link = title_tag['href'] if title_tag else "#" |
|
|
|
image_tag = result.find('div', class_='nimestrike').find('img') |
|
image_url = image_tag['src'] if image_tag else "No image" |
|
|
|
status_tag = result.find('div', class_='genrestrike') |
|
status = status_tag.find('a').text.strip() if status_tag else "No status" |
|
|
|
genre_tags = result.find_all('a', rel='tag') |
|
genres = [genre.text for genre in genre_tags] |
|
|
|
print(f"Drama {idx}:") |
|
print(f"Title: {title}") |
|
print(f"Link: {link}") |
|
print(f"Image: {image_url}") |
|
print(f"Status: {status}") |
|
print(f"Genres: {', '.join(genres) if genres else 'No genres'}") |
|
print("-" * 50) |
|
|
|
Doramaindo.getlinkdownload() |
|
except requests.exceptions.RequestException as e: |
|
print(f"Terjadi kesalahan saat mengunduh: {e}") |
|
except requests.exceptions.Timeout: |
|
print("Permintaan melebihi batas waktu.") |
|
except requests.exceptions.SSLError: |
|
print("Verifikasi SSL gagal.") |
|
except requests.exceptions.HTTPError as e: |
|
print(f"Terjadi kesalahan HTTP: {e}") |
|
except requests.exceptions.MissingSchema: |
|
print("Format URL tidak valid.") |
|
except Exception as e: |
|
logging.error(f"Error pada WithName: {e}") |
|
print(f"Terjadi kesalahan: {e}") |
|
|
|
@staticmethod |
|
def WithList(): |
|
url = denganlist |
|
try: |
|
# req = requests.get(url, headers={'User-Agent': random.choice(listofuseragents)}, timeout=10, verify=False) |
|
req = requests.get(url, headers=headers, timeout=10, verify=True) |
|
req.raise_for_status() |
|
time.sleep(random.uniform(1, 3)) |
|
|
|
soup = BeautifulSoup(req.text, 'html.parser') |
|
|
|
for group in soup.find_all('div', class_='letter-group'): |
|
letter = group.find('div', class_='letter-cell') |
|
if letter: |
|
print(f"Group: {letter.get_text()}") |
|
for title_row in group.find_all('div', class_='row-cells'): |
|
title_link = title_row.find('a', class_='series') |
|
if title_link: |
|
title = title_link.get_text(strip=True) |
|
link = title_link['href'] |
|
print(f"Title: {title} - Link: {link}") |
|
print("-" * 50) |
|
else: |
|
print("No title found") |
|
except requests.exceptions.RequestException as e: |
|
print(f"Terjadi kesalahan saat mengunduh: {e}") |
|
except requests.exceptions.Timeout: |
|
print("Permintaan melebihi batas waktu.") |
|
except requests.exceptions.SSLError: |
|
print("Verifikasi SSL gagal.") |
|
except requests.exceptions.HTTPError as e: |
|
print(f"Terjadi kesalahan HTTP: {e}") |
|
except requests.exceptions.MissingSchema: |
|
print("Format URL tidak valid.") |
|
except Exception as e: |
|
logging.error(f"Error pada WithList: {e}") |
|
print(f"Terjadi kesalahan: {e}") |
|
|
|
Doramaindo.getlinkdownload() |
|
|
|
dorama = Doramaindo() |
|
print(''' |
|
Created by @Xnuvers007 |
|
Github: Xnuvers007 |
|
Instagram: @Indradwi.25 |
|
''') |
|
try: |
|
tanya = input("Apakah ingin mencari drama berdasarkan nama? (y/n)\nTekan R untuk merename file yang di tonton (dari Dorama): ").strip() |
|
except ValueError: |
|
print("Terjadi kesalahan tipe data.") |
|
except TypeError: |
|
print("Terjadi kesalahan tipe data.") |
|
except Exception as e: |
|
print(f"Terjadi kesalahan: {e}") |
|
exit() |
|
|
|
if ( |
|
tanya.lower() == "y" or |
|
tanya.lower() == "yes" or |
|
re.match(r"y[a]+", tanya.lower()) or |
|
re.match(r"yes[a]+", tanya.lower()) or |
|
"y" in tanya.lower() and len(tanya.split()) == 1 or |
|
any(word.startswith("y") for word in tanya.split()) or |
|
re.search(r"\by+\b", tanya.lower()) or |
|
re.match(r"^y(?:es)?$", tanya.lower()) or |
|
re.search(r"\byes\w*", tanya.lower()) or |
|
re.match(r"y[a-z]*", tanya.lower()) |
|
): |
|
dorama.WithName() |
|
elif ( |
|
tanya.lower() == "n" or |
|
tanya.lower() == "no" or |
|
re.match(r"n[o]+", tanya.lower()) or |
|
re.match(r"no[a]+", tanya.lower()) or |
|
"n" in tanya.lower() and len(tanya.split()) == 1 or |
|
any(word.startswith("n") for word in tanya.split()) or |
|
re.search(r"\bn+\b", tanya.lower()) or |
|
re.match(r"^n(?:o)?$", tanya.lower()) or |
|
re.search(r"\bno\w*", tanya.lower()) or |
|
re.match(r"n[a-z]*", tanya.lower()) |
|
): |
|
dorama.WithList() |
|
elif ( |
|
tanya.lower() == "r" or |
|
tanya.lower() == "rename" or |
|
re.match(r"r[e]+", tanya.lower()) or |
|
re.match(r"rename[a]+", tanya.lower()) or |
|
"r" in tanya.lower() and len(tanya.split()) == 1 or |
|
any(word.startswith("r") for word in tanya.split()) or |
|
re.search(r"\br+\b", tanya.lower()) or |
|
re.match(r"^r(?:ename)?$", tanya.lower()) or |
|
re.search(r"\brename\w*", tanya.lower()) or |
|
re.match(r"r[a-z]*", tanya.lower()) |
|
): |
|
dorama.renameFileFromDoramaIndo() |
|
else: |
|
print("Pilihan tidak valid.") |
|
exit() |