refactor(MY5): Change endpoints to android

- The old method of fetching hmac keys from javascript has been removed, along with the github that hosted them.
- The required cert is included in the code itself and not needed separately
This commit is contained in:
stabbedbybrick 2024-09-19 14:11:58 +02:00
parent fcc87ac317
commit 62d226671d
2 changed files with 61 additions and 38 deletions

View File

@ -1,24 +1,22 @@
from __future__ import annotations
import base64
import hashlib
import hmac
import json
import re
import tempfile
import os
from collections.abc import Generator
from datetime import datetime
from typing import Any, Union
from urllib.parse import urlparse, urlunparse
import click
import requests
from click import Context
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from devine.core.manifests.dash import DASH
from devine.core.search_result import SearchResult
from devine.core.service import Service
from devine.core.titles import Episode, Movie, Movies, Series
from devine.core.tracks import Chapter, Tracks
from devine.core.utils.sslciphers import SSLCiphers
from pywidevine.cdm import Cdm as WidevineCdm
@ -26,7 +24,6 @@ class MY5(Service):
"""
\b
Service code for Channel 5's My5 streaming service (https://channel5.com).
Credit to @Diazole(https://github.com/Diazole/my5-dl) for solving the hmac.
\b
Author: stabbedbybrick
@ -61,9 +58,7 @@ class MY5(Service):
self.title = title
super().__init__(ctx)
self.gist = self.session.get(
self.config["endpoints"]["gist"].format(timestamp=datetime.now().timestamp())
).json()
self.session.headers.update({"user-agent": self.config["user_agent"]})
def search(self) -> Generator[SearchResult, None, None]:
params = {
@ -173,32 +168,30 @@ class MY5(Service):
# Service specific functions
def decrypt_data(self, media: str) -> dict:
key = base64.b64decode(self.gist["key"])
r = self.session.get(media)
if not r.ok:
raise ConnectionError(r.json().get("message"))
content = r.json()
iv = base64.urlsafe_b64decode(content["iv"])
data = base64.urlsafe_b64decode(content["data"])
cipher = AES.new(key=key, iv=iv, mode=AES.MODE_CBC)
decrypted_data = unpad(cipher.decrypt(data), AES.block_size)
return json.loads(decrypted_data)
def get_playlist(self, asset_id: str) -> tuple:
secret = self.gist["hmac"]
session = self.session
for prefix in ("https://", "http://"):
session.mount(prefix, SSLCiphers())
timestamp = datetime.now().timestamp()
vod = self.config["endpoints"]["vod"].format(id=asset_id, timestamp=f"{timestamp}")
sig = hmac.new(base64.b64decode(secret), vod.encode(), hashlib.sha256)
auth = base64.urlsafe_b64encode(sig.digest()).decode()
vod += f"&auth={auth}"
cert_binary = base64.b64decode(self.config["certificate"])
with tempfile.NamedTemporaryFile(delete=False, suffix=".pem") as cert_file:
cert_file.write(cert_binary)
cert_path = cert_file.name
try:
r = session.get(url=self.config["endpoints"]["auth"].format(title_id=asset_id), cert=cert_path)
except requests.RequestException as e:
if "Max retries exceeded" in str(e):
raise ConnectionError(
"Permission denied. If you're behind a VPN/proxy, you might be blocked"
)
else:
raise ConnectionError(f"Failed to request assets: {str(e)}")
finally:
os.remove(cert_path)
data = self.decrypt_data(vod)
data = r.json()
if not data.get("assets"):
raise ValueError(f"Could not find asset: {data}")
asset = [x for x in data["assets"] if x["drm"] == "widevine"][0]
rendition = asset["renditions"][0]

View File

@ -1,8 +1,38 @@
user_agent: Dalvik/2.1.0 (Linux; U; Android 14; SM-S901B Build/UP1A.231005.007)
endpoints:
base: https://corona.channel5.com
gist: https://gist.githubusercontent.com/stabbedbybrick/8726c719721eac50a28f7bc3c94f18e9/raw/s.txt?={timestamp}
content: https://corona.channel5.com/shows/{show}.json?platform=my5desktop
episodes: https://corona.channel5.com/shows/{show}/episodes.json?platform=my5desktop
single: https://corona.channel5.com/shows/{show}/seasons/{season}/episodes/{episode}.json?platform=my5desktop
vod: https://cassie.channel5.com/api/v2/media/my5desktopng/{id}.json?timestamp={timestamp}
content: https://corona.channel5.com/shows/{show}.json?platform=my5android
episodes: https://corona.channel5.com/shows/{show}/episodes.json?platform=my5android
single: https://corona.channel5.com/shows/{show}/seasons/{season}/episodes/{episode}.json?platform=my5android
auth: https://cassie-auth.channel5.com/api/v2/media/my5androidhydradash/{title_id}.json
search: https://corona.channel5.com/shows/search.json
certificate: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDdXpDQ0FpU2dBd0lCQWdJRVhMU1BGVEFOQmdrcWhraUc5dzBCQVFVRkFE
QmxNUXN3Q1FZRFZRUUdFd0pIDQpRakVWTUJNR0ExVUVCd3dNUkdWbVlYVnNkQ0JEYVhSNU1SSXdFQVlEVlFRS0RBbERhR0Z1Ym1W
c0lEVXhEekFODQpCZ05WQkFzTUJrTmhjM05wWlRFYU1CZ0dBMVVFQXd3UlEyRnpjMmxsSUUxbFpHbGhJRUYxZEdnd0hoY05NVGt3
DQpOREUxTVRRd016QXhXaGNOTWprd05ERTFNVFF3TXpBeFdqQ0JqakVMTUFrR0ExVUVCaE1DUjBJeEVqQVFCZ05WDQpCQW9NQ1VO
b1lXNXVaV3dnTlRFWE1CVUdBMVVFQ3d3T1EyRnpjMmxsSUdOc2FXVnVkSE14VWpCUUJnTlZCQU1NDQpTVU5oYzNOcFpTQlRaV3ht
TFhOcFoyNWxaQ0JEWlhKMGFXWnBZMkYwWlNCbWIzSWdUWGsxSUVGdVpISnZhV1FnDQpUbVY0ZENCSFpXNGdZMnhwWlc1MElERTFO
VFV6TXpZNU9ERXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBDQpNSUdKQW9HQkFNbVVTSHFCZ3pwbThXelVHZ2VDSWZvSTI3
QlovQmNmWktpbnl5dXFNVlpDNXRLaUtaRWpydFV4DQpoMXFVcDJSSkN3Ui9RcENPQ2RQdFhzMENzekZvd1ByTlY4RHFtUXZqbzY5
dlhvTEM3c2RLUjQ1cEFUQU8vY3JLDQorTUFPUXo1VWEyQ1ZrYnY1SCtaMVhWWndqbm1qNGJHZEJHM005b0NzQlVqTEh0bm1nQSty
QWdNQkFBR2pUakJNDQpNQjBHQTFVZERnUVdCQlNVVUhrY3JKNUVkVTVWM2ZJbXQra1ljdkdnZFRBTEJnTlZIUThFQkFNQ0E3Z3dD
UVlEDQpWUjBUQkFJd0FEQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFOQmdrcWhraUc5dzBCQVFVRkFBT0JnUUFpDQpHNi84
OUFEaDhEOUs0OXZjeklMQ2pqbGh6bG5US09GM2l1Um0vSjZYaWtxY3RxSDF0a01na0FXcHAwQldBRm9IDQpJbU5WSEtKdTRnZXgy
cEtLejNqOVlRNG5EWENQVTdVb0N2aDl5TTNYT0RITWZRT01sZkRtMU9GZkh2QkJvSHNVDQpHSE9EQTkwQi8xcU0xSlFaZzBOVjZi
UllrUytCOWdtSFI4dXhtZktrL0E9PQ0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ0KLS0tLS1CRUdJTiBQUklWQVRFIEtFWS0t
LS0tDQpNSUlDZHdJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0FtRXdnZ0pkQWdFQUFvR0JBTW1VU0hxQmd6cG04V3pVDQpHZ2VD
SWZvSTI3QlovQmNmWktpbnl5dXFNVlpDNXRLaUtaRWpydFV4aDFxVXAyUkpDd1IvUXBDT0NkUHRYczBDDQpzekZvd1ByTlY4RHFt
UXZqbzY5dlhvTEM3c2RLUjQ1cEFUQU8vY3JLK01BT1F6NVVhMkNWa2J2NUgrWjFYVlp3DQpqbm1qNGJHZEJHM005b0NzQlVqTEh0
bm1nQStyQWdNQkFBRUNnWUFjTVY4SnN6OTFWWnlDaWcreDZTTnpZdlhHDQo3bTd4bFBSeEdqYXlQclZ6eVJ1YmJnNitPKzFoNS9G
MFc4SWxwb21oOFdLUDhTMnl0RXBFQmhLbDRHN001WXdqDQp0SCtCVXFNMTNjbFdiQkxuQTZMT2RVeEVDTVhIUktjdHk5UE52UlJQ
cU9aV0YycDc5U1BFdFY5Q2o1SXNaVUdNDQpRcHYybk5oN1M2MUZGRVRuSVFKQkFPTXJNd2tnOGQzbksyS0lnVUNrcEtCRHlGTUJj
UXN0NG82VkxvVjNjenBwDQpxMW5FWGx4WnduMFh6Ni9GVjRWdTZYTjJLLzQxL2pCeWdTUlFXa05YVThNQ1FRRGpLYXVpdE1UajBM
ajU3QkJ3DQppNkNON0VFeUJSSkZaVGRSMDM4ZzkxSEFoUkVXVWpuQ0Vrc1UwcTl4TUNOdnM3OFN4RmQ1ODg5RUJQTnd1RDdvDQor
NTM1QWtFQTNwVTNYbHh2WUhQZktKNkR0cWtidlFSdFJoZUZnZVNsdGZzcUtCQVFVVTIwWFRKeEdwL0FWdjE3DQp1OGZxcDQwekpM
VEhDa0F4SFpzME9qYVpHcDU0TFFKQWJtM01iUjA1ZFpINnlpdlMxaE5hYW9QR01iMjdZeGJRDQpMS3dHNmd5d3BrbEp4RE1XdHR4
VHVYeXVJdlVHMVA5cFRJTThEeUhSeVR3cTU4bjVjeU1XYVFKQkFMVFRwZkVtDQoxdWhCeUd0NEtab3dYM2dhREpVZGU0ZjBwN3Ry
RFZGcExDNVJYcVVBQXNBQ2pzTHNYaEFadlovUEEwUDBiU2hmDQp4cUFRa2lnYmNKRXdxdjQ9DQotLS0tLUVORCBQUklWQVRFIEtF
WS0tLS0t