mirror of
https://github.com/barkeser2002/CoomerDL.git
synced 2026-09-25 02:30:11 +03:00
265 lines
9.3 KiB
Python
265 lines
9.3 KiB
Python
import hashlib
|
|
import re
|
|
from urllib.parse import urljoin, urlparse
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
|
|
class BunkrAdapter:
|
|
site_name = "bunkr"
|
|
|
|
def __init__(self, session, headers=None, log_callback=None, tr=None):
|
|
self.session = session
|
|
self.headers = headers or {
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
|
|
"Referer": "https://bunkr.site/",
|
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
}
|
|
self.log_callback = log_callback
|
|
self.tr = tr
|
|
|
|
def translate(self, key, **kwargs):
|
|
if callable(self.tr):
|
|
try:
|
|
return self.tr(key, **kwargs)
|
|
except TypeError:
|
|
text = self.tr(key)
|
|
if kwargs:
|
|
try:
|
|
return text.format(**kwargs)
|
|
except Exception:
|
|
return text
|
|
return text
|
|
|
|
if kwargs:
|
|
try:
|
|
return key.format(**kwargs)
|
|
except Exception:
|
|
return key
|
|
return key
|
|
|
|
def log(self, message):
|
|
if self.log_callback:
|
|
self.log_callback(self.site_name, message)
|
|
|
|
def clean_filename(self, filename):
|
|
return re.sub(r'[<>:"/\\|?*\u200b]', "_", str(filename or "")).strip()
|
|
|
|
def get_consistent_folder_name(self, url, default_name):
|
|
url_hash = hashlib.md5(url.encode("utf-8")).hexdigest()[:8]
|
|
folder_name = f"{default_name}_{url_hash}"
|
|
return self.clean_filename(folder_name)
|
|
|
|
def can_handle(self, url: str) -> bool:
|
|
host = urlparse(url).netloc.lower()
|
|
return "bunkr" in host
|
|
|
|
def resolve_url(self, url: str):
|
|
if "/f/" in url:
|
|
return self._resolve_f_url(url)
|
|
|
|
return self._resolve_post_or_profile(url)
|
|
|
|
def _request_soup(self, url):
|
|
response = self.session.get(url, headers=self.headers)
|
|
response.raise_for_status()
|
|
return BeautifulSoup(response.text, "html.parser")
|
|
|
|
def _resolve_f_url(self, url):
|
|
self.log(self.translate("BUNKR_RESOLVING_F_URL", url=url))
|
|
soup = self._request_soup(url)
|
|
|
|
first_anchor = soup.find(
|
|
"a",
|
|
{
|
|
"class": "btn btn-main btn-lg rounded-full px-6 font-semibold flex-1 ic-download-01 ic-before before:text-lg"
|
|
},
|
|
)
|
|
if not first_anchor or "href" not in first_anchor.attrs:
|
|
self.log(self.translate("BUNKR_INTERMEDIATE_LINK_NOT_FOUND"))
|
|
return {
|
|
"folder_name": self.get_consistent_folder_name(url, "bunkr_post"),
|
|
"media": [],
|
|
}
|
|
|
|
intermediate_url = first_anchor["href"]
|
|
soup2 = self._request_soup(intermediate_url)
|
|
|
|
p_tag = soup2.find("p", class_="mt-3 text-center")
|
|
if not p_tag:
|
|
self.log(self.translate("BUNKR_FINAL_CONTAINER_NOT_FOUND"))
|
|
return {
|
|
"folder_name": self.get_consistent_folder_name(url, "bunkr_post"),
|
|
"media": [],
|
|
}
|
|
|
|
download_anchor = p_tag.find(
|
|
"a",
|
|
{
|
|
"class": "btn btn-main btn-lg rounded-full px-6 font-semibold ic-download-01 ic-before before:text-lg"
|
|
},
|
|
)
|
|
if not download_anchor or "href" not in download_anchor.attrs:
|
|
self.log(self.translate("BUNKR_FINAL_DOWNLOAD_LINK_NOT_FOUND"))
|
|
return {
|
|
"folder_name": self.get_consistent_folder_name(url, "bunkr_post"),
|
|
"media": [],
|
|
}
|
|
|
|
final_download_url = download_anchor["href"]
|
|
folder_name = self.get_consistent_folder_name(url, "bunkr_post")
|
|
|
|
return {
|
|
"folder_name": folder_name,
|
|
"media": [
|
|
{
|
|
"media_url": final_download_url,
|
|
"title": "bunkr_post",
|
|
"post_id": None,
|
|
"published": "",
|
|
}
|
|
],
|
|
}
|
|
|
|
def _resolve_post_or_profile(self, url):
|
|
soup = self._request_soup(url)
|
|
|
|
title_tag = soup.find("h1", {"class": "truncate"})
|
|
if title_tag:
|
|
base_folder_name = self.clean_filename(title_tag.text.strip())[:50]
|
|
else:
|
|
base_folder_name = "bunkr_profile"
|
|
|
|
folder_name = self.get_consistent_folder_name(url, base_folder_name)
|
|
|
|
media = []
|
|
|
|
grid_div = soup.find(
|
|
"div",
|
|
{"class": "grid gap-4 grid-cols-repeat [--size:11rem] lg:[--size:14rem] grid-images"},
|
|
)
|
|
if grid_div:
|
|
media.extend(self._resolve_profile_media(url, grid_div))
|
|
else:
|
|
media.extend(self._resolve_post_media(url, soup))
|
|
|
|
return {
|
|
"folder_name": folder_name,
|
|
"media": media,
|
|
}
|
|
|
|
def _resolve_profile_media(self, profile_url, grid_div):
|
|
media = []
|
|
links = grid_div.find_all("a", {"class": "after:absolute after:z-10 after:inset-0"})
|
|
|
|
for link in links:
|
|
href = link.get("href")
|
|
if not href:
|
|
continue
|
|
|
|
image_page_url = urljoin(profile_url, href)
|
|
|
|
try:
|
|
image_soup = self._request_soup(image_page_url)
|
|
|
|
media_tag = image_soup.select_one(
|
|
"figure.relative img[class='w-full h-full absolute opacity-20 object-cover blur-sm z-10']"
|
|
)
|
|
if media_tag and media_tag.get("src"):
|
|
media_url = urljoin(image_page_url, media_tag["src"])
|
|
media.append({
|
|
"media_url": media_url,
|
|
"title": "bunkr_profile_item",
|
|
"post_id": None,
|
|
"published": "",
|
|
})
|
|
|
|
video_tag = image_soup.select_one("video#player")
|
|
if video_tag:
|
|
if video_tag.get("src"):
|
|
media_url = urljoin(image_page_url, video_tag["src"])
|
|
media.append({
|
|
"media_url": media_url,
|
|
"title": "bunkr_profile_item",
|
|
"post_id": None,
|
|
"published": "",
|
|
})
|
|
else:
|
|
source_tag = video_tag.find("source")
|
|
if source_tag and source_tag.get("src"):
|
|
media_url = urljoin(image_page_url, source_tag["src"])
|
|
media.append({
|
|
"media_url": media_url,
|
|
"title": "bunkr_profile_item",
|
|
"post_id": None,
|
|
"published": "",
|
|
})
|
|
|
|
except Exception as e:
|
|
self.log(
|
|
self.translate(
|
|
"BUNKR_FAILED_RESOLVING_PROFILE_MEDIA_PAGE",
|
|
url=image_page_url,
|
|
error=e,
|
|
)
|
|
)
|
|
|
|
return media
|
|
|
|
def _resolve_post_media(self, post_url, soup):
|
|
media = []
|
|
|
|
media_divs = soup.find_all(
|
|
"figure",
|
|
{"class": "relative rounded-lg overflow-hidden flex justify-center items-center aspect-video bg-soft"},
|
|
)
|
|
for div in media_divs:
|
|
for img_tag in div.find_all("img"):
|
|
src = img_tag.get("src")
|
|
if src:
|
|
media.append({
|
|
"media_url": src,
|
|
"title": "bunkr_post",
|
|
"post_id": None,
|
|
"published": "",
|
|
})
|
|
|
|
video_divs = soup.find_all("div", {"class": "flex w-full md:w-auto gap-4"})
|
|
for video_div in video_divs:
|
|
download_page_link = video_div.find(
|
|
"a",
|
|
{
|
|
"class": "btn btn-main btn-lg rounded-full px-6 font-semibold flex-1 ic-download-01 ic-before before:text-lg"
|
|
},
|
|
)
|
|
if not download_page_link or "href" not in download_page_link.attrs:
|
|
continue
|
|
|
|
video_page_url = download_page_link["href"]
|
|
|
|
try:
|
|
video_page_soup = self._request_soup(video_page_url)
|
|
download_link = video_page_soup.find(
|
|
"a",
|
|
{
|
|
"class": "btn btn-main btn-lg rounded-full px-6 font-semibold ic-download-01 ic-before before:text-lg"
|
|
},
|
|
)
|
|
if download_link and download_link.get("href"):
|
|
media.append({
|
|
"media_url": download_link["href"],
|
|
"title": "bunkr_post",
|
|
"post_id": None,
|
|
"published": "",
|
|
})
|
|
except Exception as e:
|
|
self.log(
|
|
self.translate(
|
|
"BUNKR_FAILED_RESOLVING_VIDEO_PAGE",
|
|
url=video_page_url,
|
|
error=e,
|
|
)
|
|
)
|
|
|
|
return media |