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:
		
							parent
							
								
									fcc87ac317
								
							
						
					
					
						commit
						62d226671d
					
				@ -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]
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user