Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crunpyroll/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion crunpyroll/methods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -21,6 +22,7 @@ class Methods(
GetManifest,
GetLicense,
GetObjects,
DeleteActiveStream
DeleteActiveStream,
GetHistory
):
pass
44 changes: 44 additions & 0 deletions crunpyroll/methods/get_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from crunpyroll import types

import crunpyroll

class GetHistory:
async def get_history(
self: "crunpyroll.Client",
*,
locale: str = None,
) -> "types.HistoryQuery":
"""
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.HistoryQuery`:
On success, query of watch history.
"""
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
6 changes: 4 additions & 2 deletions crunpyroll/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -42,7 +43,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,
Expand All @@ -59,14 +60,15 @@ 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]:
response = await self._client.api_request(
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,
Expand Down
3 changes: 2 additions & 1 deletion crunpyroll/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
from .objects import ObjectsQuery
from .index import SessionIndex
from .manifest import Manifest, ManifestVideoStream, ManifestAudioStream
from .drm import DRM, ContentProtection
from .drm import DRM, ContentProtection
from .history import HistoryQuery, History
74 changes: 74 additions & 0 deletions crunpyroll/types/history.py
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 4 additions & 0 deletions crunpyroll/types/seasons.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion crunpyroll/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from datetime import datetime
from uuid import uuid4

PUBLIC_TOKEN = "d2piMV90YThta3Y3X2t4aHF6djc6MnlSWlg0Y0psX28yMzRqa2FNaXRTbXNLUVlGaUpQXzU="
PUBLIC_TOKEN = "dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4="

APP_VERSION = "3.59.0"

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
httpx
httpx<0.28
xmltodict
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="crunpyroll",
version="2.4.5",
version="2.5",
author="stefanodvx",
author_email="[email protected]",
description="Async API wrapper for Crunchyroll",
Expand All @@ -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",
)