Files
gallery-dl/gallery_dl/extractor/tapas.py
2025-12-31 08:47:37 +01:00

167 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2021-2025 Mike Fährmann
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
"""Extractors for https://tapas.io/"""
from .common import Extractor, Message
from .. import text, exception
from ..cache import cache
BASE_PATTERN = r"(?:https?://)?tapas\.io"
class TapasExtractor(Extractor):
"""Base class for tapas.io extractors"""
category = "tapas"
root = "https://tapas.io"
directory_fmt = ("{category}", "{series[title]}", "{id} {title}")
filename_fmt = "{num:>02}.{extension}"
archive_fmt = "{id}_{num}"
cookies_domain = ".tapas.io"
cookies_names = ("_cpc_",)
_cache = None
def _init(self):
if self._cache is None:
TapasExtractor._cache = {}
def login(self):
if self.cookies_check(self.cookies_names):
return
username, password = self._get_auth_info()
if username:
return self.cookies_update(self._login_impl(username, password))
self.cookies.set(
"birthDate" , "1981-02-03", domain=self.cookies_domain)
self.cookies.set(
"adjustedBirthDate", "1981-02-03", domain=self.cookies_domain)
@cache(maxage=14*86400, keyarg=1)
def _login_impl(self, username, password):
self.log.info("Logging in as %s", username)
url = self.root + "/account/authenticate"
headers = {
"Referer" : url,
}
data = {
"from" : "https://tapas.io/",
"email" : username,
"password": password,
}
response = self.request(
url, method="POST", headers=headers, data=data)
if not response.history or \
"/account/signin_fail" in response.history[-1].url:
raise exception.AuthenticationError()
return {"_cpc_": response.history[0].cookies.get("_cpc_")}
def request_api(self, url, params=None):
headers = {"Accept": "application/json, text/javascript, */*;"}
return self.request_json(url, params=params, headers=headers)["data"]
class TapasEpisodeExtractor(TapasExtractor):
subcategory = "episode"
pattern = BASE_PATTERN + r"/episode/(\d+)"
example = "https://tapas.io/episode/12345"
def items(self):
self.login()
episode_id = self.groups[0]
url = f"{self.root}/episode/{episode_id}"
data = self.request_api(url)
episode = data["episode"]
if not episode.get("free") and not episode.get("unlocked"):
raise exception.AuthorizationError(
f"{episode_id}: Episode '{episode['title']}' not unlocked")
html = data["html"]
episode["series"] = self._extract_series(html)
episode["date"] = self.parse_datetime_iso(episode["publish_date"])
yield Message.Directory, "", episode
if episode["book"]:
content = text.extr(
html, '<div class="viewer">', '<div class="viewer-bottom')
episode["num"] = 1
episode["extension"] = "html"
yield Message.Url, "text:" + content, episode
else: # comic
for episode["num"], url in enumerate(text.extract_iter(
html, 'data-src="', '"'), 1):
url = text.unescape(url)
yield Message.Url, url, text.nameext_from_url(url, episode)
def _extract_series(self, html):
series_id = text.rextr(html, 'data-series-id="', '"')
try:
return self._cache[series_id]
except KeyError:
url = f"{self.root}/series/{series_id}"
series = self._cache[series_id] = self.request_api(url)
return series
class TapasSeriesExtractor(TapasExtractor):
subcategory = "series"
pattern = BASE_PATTERN + r"/series/([^/?#]+)"
example = "https://tapas.io/series/TITLE"
def items(self):
self.login()
url = f"{self.root}/series/{self.groups[0]}"
series_id, _, episode_id = text.extr(
self.request(url).text, 'content="tapastic://series/', '"',
).partition("/episodes/")
url = f"{self.root}/series/{series_id}/episodes"
params = {
"eid" : episode_id,
"page" : 1,
"sort" : "OLDEST",
"last_access": "0",
"max_limit" : "20",
}
base = self.root + "/episode/"
while True:
data = self.request_api(url, params)
for episode in data["episodes"]:
episode["_extractor"] = TapasEpisodeExtractor
yield Message.Queue, base + str(episode["id"]), episode
if not data["pagination"]["has_next"]:
return
params["page"] += 1
class TapasCreatorExtractor(TapasExtractor):
subcategory = "creator"
pattern = BASE_PATTERN + r"/(?!series|episode)([^/?#]+)"
example = "https://tapas.io/CREATOR"
def items(self):
self.login()
url = f"{self.root}/{self.groups[0]}/series"
page = self.request(url).text
page = text.extr(page, '<ul class="content-list-wrap', "</ul>")
data = {"_extractor": TapasSeriesExtractor}
for path in text.extract_iter(page, ' href="', '"'):
yield Message.Queue, self.root + path, data