Version 1.0.2
- Update version - Add initial login variable in class to track initial login - added logic to only `super().authenticate` on first authentication - Store credentials in class for refresh tokens - Store cookies in class for later possible use - Only raise cookie error on first login - Dropped token expiry from 5 minutes to 4 - Update season number to reflect first season if it is the only season for the series - added authenticate method on each license request
This commit is contained in:
parent
192a915834
commit
9349d46546
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
/EXAMPLE
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class CR(Service):
|
|||||||
"""
|
"""
|
||||||
Service code for Crunchyroll
|
Service code for Crunchyroll
|
||||||
Author: TPD94
|
Author: TPD94
|
||||||
Version: 1.0.1
|
Version: 1.0.2
|
||||||
Authorization: Cookies for web endpoints, Credentials for TV endpoints, Cookies/Credentials for both. Cookies required.
|
Authorization: Cookies for web endpoints, Credentials for TV endpoints, Cookies/Credentials for both. Cookies required.
|
||||||
Security: FHD@L3
|
Security: FHD@L3
|
||||||
Use Series ID/URL (for example - https://www.crunchyroll.com/series/GG5H5XQ7D/kaiju-no-8) or Series ID (for example - GG5H5XQ7D).
|
Use Series ID/URL (for example - https://www.crunchyroll.com/series/GG5H5XQ7D/kaiju-no-8) or Series ID (for example - GG5H5XQ7D).
|
||||||
@ -36,7 +36,7 @@ class CR(Service):
|
|||||||
help="""
|
help="""
|
||||||
Service code for Crunchyroll\n
|
Service code for Crunchyroll\n
|
||||||
Author: TPD94\n
|
Author: TPD94\n
|
||||||
Version: 1.0.1\n
|
Version: 1.0.2\n
|
||||||
Authorization: Cookies for web endpoints, Credentials for TV endpoints, Cookies/Credentials for both. Cookies required.\n
|
Authorization: Cookies for web endpoints, Credentials for TV endpoints, Cookies/Credentials for both. Cookies required.\n
|
||||||
Security: FHD@L3\n
|
Security: FHD@L3\n
|
||||||
Use Series ID/URL (for example - https://www.crunchyroll.com/series/GG5H5XQ7D/kaiju-no-8) or Series ID (for example - GG5H5XQ7D).
|
Use Series ID/URL (for example - https://www.crunchyroll.com/series/GG5H5XQ7D/kaiju-no-8) or Series ID (for example - GG5H5XQ7D).
|
||||||
@ -73,12 +73,17 @@ class CR(Service):
|
|||||||
|
|
||||||
self.credential = None
|
self.credential = None
|
||||||
|
|
||||||
|
self.initial_login = False
|
||||||
|
|
||||||
def get_session(self):
|
def get_session(self):
|
||||||
|
|
||||||
# Create a session using curl_cffi as it can impersonate browsers and avoid bot detection by Crunchyroll
|
# Create a session using curl_cffi as it can impersonate browsers and avoid bot detection by Crunchyroll
|
||||||
return session("chrome124")
|
return session("chrome124")
|
||||||
|
|
||||||
def authenticate(self, cookies: Optional[CookieJar] = None, credential: Optional[Credential] = None) -> None:
|
def authenticate(self, cookies: Optional[CookieJar] = None, credential: Optional[Credential] = None) -> None:
|
||||||
|
# Run the super method to load the cookies without writing redundant code
|
||||||
|
if not self.initial_login:
|
||||||
|
super().authenticate(cookies, credential)
|
||||||
if cookies:
|
if cookies:
|
||||||
self.cookies = cookies
|
self.cookies = cookies
|
||||||
elif hasattr(self, 'cookies'):
|
elif hasattr(self, 'cookies'):
|
||||||
@ -89,11 +94,10 @@ class CR(Service):
|
|||||||
elif hasattr(self, 'credential'):
|
elif hasattr(self, 'credential'):
|
||||||
credential = self.credential
|
credential = self.credential
|
||||||
|
|
||||||
# Run the super method to load the cookies without writing redundant code
|
self.initial_login = True
|
||||||
super().authenticate(cookies, credential)
|
|
||||||
|
|
||||||
# Raise error if no cookies, Crunchyroll has implemented recaptcha, so authorization via credentials is not implemented
|
# Raise error if no cookies, Crunchyroll has implemented recaptcha, so authorization via credentials is not implemented
|
||||||
if not cookies:
|
if not cookies and not self.initial_login:
|
||||||
raise EnvironmentError("Service requires cookies for authentication.")
|
raise EnvironmentError("Service requires cookies for authentication.")
|
||||||
|
|
||||||
# If authenticate is being called for the first time and cookies are present, retrieve an authorization token
|
# If authenticate is being called for the first time and cookies are present, retrieve an authorization token
|
||||||
@ -115,10 +119,10 @@ class CR(Service):
|
|||||||
}
|
}
|
||||||
).json()['access_token']
|
).json()['access_token']
|
||||||
|
|
||||||
# Update the token expiry, Crunchyroll offers 15 minutes between expiry.
|
# Update the token expiry, Crunchyroll offers 5 minutes between expiry.
|
||||||
self.auth_token_expiry_web = datetime.now() + timedelta(minutes=5)
|
self.auth_token_expiry_web = datetime.now() + timedelta(minutes=4)
|
||||||
|
|
||||||
if not credential:
|
if not credential and not self.initial_login:
|
||||||
console.log("Only cookies detected, can only fetch web manifests")
|
console.log("Only cookies detected, can only fetch web manifests")
|
||||||
|
|
||||||
if credential and self.auth_token_tv is None:
|
if credential and self.auth_token_tv is None:
|
||||||
@ -141,8 +145,8 @@ class CR(Service):
|
|||||||
}
|
}
|
||||||
).json()['access_token']
|
).json()['access_token']
|
||||||
|
|
||||||
# Update the token expiry, Crunchyroll offers 15 minutes between expiry.
|
# Update the token expiry, Crunchyroll offers 5 minutes between expiry.
|
||||||
self.auth_token_expiry_tv = datetime.now() + timedelta(minutes=5)
|
self.auth_token_expiry_tv = datetime.now() + timedelta(minutes=4)
|
||||||
|
|
||||||
|
|
||||||
# If there is already an authorization token for web, and it is expired, get a new one
|
# If there is already an authorization token for web, and it is expired, get a new one
|
||||||
@ -165,7 +169,7 @@ class CR(Service):
|
|||||||
self.auth_token_web = refresh_response.json()['access_token']
|
self.auth_token_web = refresh_response.json()['access_token']
|
||||||
|
|
||||||
# Update the token expiry time
|
# Update the token expiry time
|
||||||
self.auth_token_expiry_web = datetime.now() + timedelta(minutes=5)
|
self.auth_token_expiry_web = datetime.now() + timedelta(minutes=4)
|
||||||
|
|
||||||
# If there is already an authorization token for TV, and it is expired, get a new one
|
# If there is already an authorization token for TV, and it is expired, get a new one
|
||||||
if self.auth_token_tv is not None and datetime.now() > self.auth_token_expiry_tv:
|
if self.auth_token_tv is not None and datetime.now() > self.auth_token_expiry_tv:
|
||||||
@ -178,8 +182,8 @@ class CR(Service):
|
|||||||
},
|
},
|
||||||
data={
|
data={
|
||||||
'grant_type': 'password',
|
'grant_type': 'password',
|
||||||
'username': credential.username,
|
'username': self.credential.username,
|
||||||
'password': credential.password,
|
'password': self.credential.password,
|
||||||
'scope': 'offline_access',
|
'scope': 'offline_access',
|
||||||
'client_id': 'anydazwaxclrocanwho3',
|
'client_id': 'anydazwaxclrocanwho3',
|
||||||
'client_secret': '88gnIsucV-Q7sYrY29uOW_JGlMqx1mBN',
|
'client_secret': '88gnIsucV-Q7sYrY29uOW_JGlMqx1mBN',
|
||||||
@ -193,7 +197,7 @@ class CR(Service):
|
|||||||
self.auth_token_tv = refresh_response.json()['access_token']
|
self.auth_token_tv = refresh_response.json()['access_token']
|
||||||
|
|
||||||
# Update the token expiry time
|
# Update the token expiry time
|
||||||
self.auth_token_expiry_tv = datetime.now() + timedelta(minutes=5)
|
self.auth_token_expiry_tv = datetime.now() + timedelta(minutes=4)
|
||||||
|
|
||||||
def get_titles(self) -> Titles_T:
|
def get_titles(self) -> Titles_T:
|
||||||
|
|
||||||
@ -241,7 +245,7 @@ class CR(Service):
|
|||||||
id_=episode['id'],
|
id_=episode['id'],
|
||||||
service=self.__class__,
|
service=self.__class__,
|
||||||
title=episode['series_title'],
|
title=episode['series_title'],
|
||||||
season=int(episode['season_display_number']) if episode['season_display_number'] != '' else episode['season_sequence_number'] if episode['season_display_number'] == '' and episode['season_sequence_number'] == 1 else 0,
|
season=int(episode['season_display_number']) if episode['season_display_number'] != '' else episode['season_sequence_number'] if episode['season_display_number'] == '' and episode['season_sequence_number'] == 1 else 1 if episode['season_sequence_number'] == 0 else 0,
|
||||||
number = episode['episode_number'] if isinstance(episode['episode_number'], int) else special_counter,
|
number = episode['episode_number'] if isinstance(episode['episode_number'], int) else special_counter,
|
||||||
name=episode['title'] if episode['title'] else episode['season_title'],
|
name=episode['title'] if episode['title'] else episode['season_title'],
|
||||||
year=episode['episode_air_date'][:4],
|
year=episode['episode_air_date'][:4],
|
||||||
@ -592,6 +596,7 @@ class CR(Service):
|
|||||||
return chapters
|
return chapters
|
||||||
|
|
||||||
def get_widevine_license(self, *, challenge: bytes, title: Title_T, track: AnyTrack) -> Optional[Union[bytes, str]]:
|
def get_widevine_license(self, *, challenge: bytes, title: Title_T, track: AnyTrack) -> Optional[Union[bytes, str]]:
|
||||||
|
self.authenticate()
|
||||||
if track.data['endpoint_type'] == 'tv':
|
if track.data['endpoint_type'] == 'tv':
|
||||||
# Get the episode response
|
# Get the episode response
|
||||||
episode_response = self.session.get(
|
episode_response = self.session.get(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user