From d093275ad0a4a13abe3b782597185c720fe11643 Mon Sep 17 00:00:00 2001 From: Pixel-LH <2569646547@qq.com> Date: Thu, 30 Jan 2025 00:21:35 +0800 Subject: [PATCH 1/7] Update:PUBLIC_TOKEN --- crunpyroll/client.py | 4 +++- crunpyroll/session.py | 4 ++-- crunpyroll/utils.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crunpyroll/client.py b/crunpyroll/client.py index b2a2595..c1961c4 100644 --- a/crunpyroll/client.py +++ b/crunpyroll/client.py @@ -54,7 +54,8 @@ def __init__( device_id: str = DEVICE_ID, device_name: str = DEVICE_NAME, device_type: str = DEVICE_TYPE, - proxies: Union[Dict, str] = None + proxies: Union[Dict, str] = None, + public_token: str = None ) -> None: self.email: str = email self.password: str = password @@ -63,6 +64,7 @@ def __init__( self.device_id: str = device_id self.device_name: str = device_name self.device_type: str = device_type + self.public_token = public_token self.http = httpx.AsyncClient(proxies=proxies, timeout=15) self.session = Session(self) diff --git a/crunpyroll/session.py b/crunpyroll/session.py index fd45251..4f8787a 100644 --- a/crunpyroll/session.py +++ b/crunpyroll/session.py @@ -42,7 +42,7 @@ async def authorize(self) -> Optional[bool]: method="POST", endpoint="auth/v1/token", headers={ - "Authorization": f"Basic {PUBLIC_TOKEN}" + "Authorization": f"Basic {self._client.public_token or PUBLIC_TOKEN}" }, payload={ "username": self._client.email, @@ -66,7 +66,7 @@ async def refresh(self) -> Optional[bool]: method="POST", endpoint="auth/v1/token", headers={ - "Authorization": f"Basic {PUBLIC_TOKEN}" + "Authorization": f"Basic {self._client.public_token or PUBLIC_TOKEN}" }, payload={ "refresh_token": self.refresh_token, diff --git a/crunpyroll/utils.py b/crunpyroll/utils.py index dbf2c51..693d36e 100644 --- a/crunpyroll/utils.py +++ b/crunpyroll/utils.py @@ -2,7 +2,7 @@ from datetime import datetime from uuid import uuid4 -PUBLIC_TOKEN = "d2piMV90YThta3Y3X2t4aHF6djc6MnlSWlg0Y0psX28yMzRqa2FNaXRTbXNLUVlGaUpQXzU=" +PUBLIC_TOKEN = "dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4=" APP_VERSION = "3.59.0" From 5618ca5c170c88ccb86da04aab18863ce4d9df69 Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 17 Nov 2024 02:45:09 +0000 Subject: [PATCH 2/7] Ignore devcontainer config --- crunpyroll/methods/__init__.py | 4 +- crunpyroll/methods/get_history.py | 44 ++++++++++++++++++ crunpyroll/session.py | 2 + crunpyroll/types/__init__.py | 3 +- crunpyroll/types/history.py | 74 +++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 crunpyroll/methods/get_history.py create mode 100644 crunpyroll/types/history.py diff --git a/crunpyroll/methods/__init__.py b/crunpyroll/methods/__init__.py index ef0c581..a2c00cd 100644 --- a/crunpyroll/methods/__init__.py +++ b/crunpyroll/methods/__init__.py @@ -9,6 +9,7 @@ from .get_manifest import GetManifest from .get_license import GetLicense from .delete_active_stream import DeleteActiveStream +from .get_history import GetHistory class Methods( Search, @@ -21,6 +22,7 @@ class Methods( GetManifest, GetLicense, GetObjects, - DeleteActiveStream + DeleteActiveStream, + GetHistory ): pass diff --git a/crunpyroll/methods/get_history.py b/crunpyroll/methods/get_history.py new file mode 100644 index 0000000..6fcfb3a --- /dev/null +++ b/crunpyroll/methods/get_history.py @@ -0,0 +1,44 @@ +from crunpyroll import types + +import crunpyroll + +class GetHistory: + async def get_history( + self: "crunpyroll.Client", + *, + locale: str = None, + ) -> "types.EpisodesQuery": + """ + Get list of seasons from a series. + + Parameters: + locale (``str``, *optional*): + Localize request for different results. + Default to the one used in Client. + + Returns: + :obj:`~crunpyroll.types.EpisodesQuery`: + On success, query of episodes is returned. + """ + await self.session.retrieve() + + response_agg = [] + + next_page: str = None + + while True: + response = types.HistoryQuery.parse(await self.api_request( + method = "GET", + endpoint = next_page or "content/v2/" + self.session.account_id + "/watch-history", + params={ + "locale": locale or self.locale + } + )) + + response_agg += response.items + + next_page = response.next_page.lstrip('/') + if not next_page: + break + + return response_agg \ No newline at end of file diff --git a/crunpyroll/session.py b/crunpyroll/session.py index 4f8787a..55ccfba 100644 --- a/crunpyroll/session.py +++ b/crunpyroll/session.py @@ -21,6 +21,7 @@ def __init__( self.access_token: str = None self.refresh_token: str = None self.expiration: datetime = None + self.account_id: str = None @property def is_authorized(self): @@ -59,6 +60,7 @@ async def authorize(self) -> Optional[bool]: self.expiration = get_date() + timedelta( seconds=response.get("expires_in") ) + self.account_id = response.get("account_id") return True async def refresh(self) -> Optional[bool]: diff --git a/crunpyroll/types/__init__.py b/crunpyroll/types/__init__.py index 15750fc..7409693 100644 --- a/crunpyroll/types/__init__.py +++ b/crunpyroll/types/__init__.py @@ -14,4 +14,5 @@ from .objects import ObjectsQuery from .index import SessionIndex from .manifest import Manifest, ManifestVideoStream, ManifestAudioStream -from .drm import DRM, ContentProtection \ No newline at end of file +from .drm import DRM, ContentProtection +from .history import HistoryQuery, History \ No newline at end of file diff --git a/crunpyroll/types/history.py b/crunpyroll/types/history.py new file mode 100644 index 0000000..d32c385 --- /dev/null +++ b/crunpyroll/types/history.py @@ -0,0 +1,74 @@ +from .obj import Object +from .content import Content +from .episodes import Episode + +from ..utils import str_to_date + +from datetime import datetime +from typing import List, Dict + +class HistoryQuery(Object): + """ + Query containing watch history. + + Parameters: + total (``int``): + Total episodes returned. + + items (``list`` of :obj:`~crunpyroll.types.History`): + List containing each episode. + + next_page (``str```): + URL for next page of results + """ + def __init__(self, data: Dict): + self.total: int = data.get("total") + self.items: List["History"] = data.get("items") + self.next_page: str = data.get("next_page") + + @classmethod + def parse(cls, obj: Dict): + data = {} + data["total"] = obj["total"] + data["next_page"] = obj["meta"]["next_page"] + data["items"] = [ + History.parse(item) + for item in obj["data"] + ] + return cls(data) + +class History(Content): + """ + Info about watch episode. + + Parameters: + id (``str``): + Unique identifier of the episode. + + date_played :py:obj:`~datetime.datetime`): + Date the episode was watched. + + fully_watched (``bool``): + True, if this episode was fully watched. + + episode (:obj:`~crunpyroll.types.Episode`): + Episode metadata. + + """ + def __init__(self, data: Dict): + self.id: str = data.get("id") + self.date_played: datetime = str_to_date(data.get("date_played")) + self.fully_watched: bool = data.get("fully_watched") + self.episode: "Episode" = Episode(data.get("episode")) + + @classmethod + def parse(cls, obj: Dict): + data = {} + data["id"] = obj["id"] + data["date_played"] = obj["date_played"] + data["fully_watched"] = obj["fully_watched"] + data["episode"] = obj.get("panel",{}) + if "episode_metadata" in data["episode"]: + data["episode"].update(data["episode"]["episode_metadata"]) + data["episode"].pop("episode_metadata", None) + return cls(data) \ No newline at end of file From c32f1c073665bac2404cec3257cc7799aca278fd Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 17 Nov 2024 01:25:55 -0600 Subject: [PATCH 3/7] Fix return types in documentation --- crunpyroll/methods/get_history.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crunpyroll/methods/get_history.py b/crunpyroll/methods/get_history.py index 6fcfb3a..f6f84f8 100644 --- a/crunpyroll/methods/get_history.py +++ b/crunpyroll/methods/get_history.py @@ -7,7 +7,7 @@ async def get_history( self: "crunpyroll.Client", *, locale: str = None, - ) -> "types.EpisodesQuery": + ) -> "types.HistoryQuery": """ Get list of seasons from a series. @@ -17,8 +17,8 @@ async def get_history( Default to the one used in Client. Returns: - :obj:`~crunpyroll.types.EpisodesQuery`: - On success, query of episodes is returned. + :obj:`~crunpyroll.types.HistoryQuery`: + On success, query of watch history. """ await self.session.retrieve() From 4d03094cd7ecb70a49365e4a1b4257f47073d60c Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 17 Nov 2024 01:26:14 -0600 Subject: [PATCH 4/7] Expose season sequence number --- crunpyroll/types/seasons.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crunpyroll/types/seasons.py b/crunpyroll/types/seasons.py index c9c2fd4..c808f6c 100644 --- a/crunpyroll/types/seasons.py +++ b/crunpyroll/types/seasons.py @@ -45,6 +45,9 @@ class Season(Content): season_number (``int``): Number of the season. + season_sequence_number (``int``): + Sequential number of the season. + episode_count (``int``): Episode count of the season. @@ -83,6 +86,7 @@ def __init__(self, data: Dict): self.slug: str = data.get("slug_title") self.description: str = data.get("description") self.season_number: int = data.get("season_number") + self.season_sequence_number: int = data.get("season_sequence_number") self.episode_count: int = data.get("number_of_episodes") self.series_id: str = data.get("series_id") self.series_slug: str = data.get("series_slug_title") From 5e30181b841fd5ac26a3c9d8a691468b45809721 Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 17 Nov 2024 14:35:02 +0000 Subject: [PATCH 5/7] Bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index da05566..c8bda39 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="crunpyroll", - version="2.4.5", + version="2.5", author="stefanodvx", author_email="pp.stefanodvx@gmail.com", description="Async API wrapper for Crunchyroll", From 297ef52c82674da8671fdd5b398930109d95ab52 Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 8 Dec 2024 20:20:11 +0000 Subject: [PATCH 6/7] httpx .28 deprecates `proxies` argument --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f13b69..3f7b8b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -httpx +httpx<0.28 xmltodict \ No newline at end of file From 1be17f7673a6eb63d3f0c90f2f9066436b227e3f Mon Sep 17 00:00:00 2001 From: ethanbergstrom Date: Sun, 8 Dec 2024 20:40:02 +0000 Subject: [PATCH 7/7] Align setup.py with requirements --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c8bda39..3c9c71e 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ project_urls={ "Tracker": "https://github.com/stefanodvx/crunpyroll/issues", }, - install_requires=["httpx", "xmltodict"], + install_requires=["httpx<0.28", "xmltodict"], packages=setuptools.find_packages(), python_requires=">=3.7", )