forked from kenzuya/unshackle
Compare commits
9 Commits
0a820e6552
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b61135175d | ||
|
|
528a62c243 | ||
|
|
81661a44b9 | ||
|
|
b22c422408 | ||
|
|
f4152bc777 | ||
|
|
9c7af72cad | ||
|
|
1244141df2 | ||
|
|
5dde031bd8 | ||
|
|
a07302cb88 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,8 +6,6 @@ update_check.json
|
||||
*.exe
|
||||
*.dll
|
||||
*.crt
|
||||
*.wvd
|
||||
*.prd
|
||||
*.der
|
||||
*.pem
|
||||
*.bin
|
||||
@@ -21,12 +19,11 @@ device_vmp_blob
|
||||
unshackle/cache/
|
||||
unshackle/cookies/
|
||||
unshackle/certs/
|
||||
unshackle/WVDs/
|
||||
unshackle/PRDs/
|
||||
temp/
|
||||
logs/
|
||||
Temp/
|
||||
binaries/
|
||||
Logs
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
unshackle/WVDs/hisense_msd6a648_4.10.2891.0_2a621b99_7110_l1.wvd
Normal file
BIN
unshackle/WVDs/hisense_msd6a648_4.10.2891.0_2a621b99_7110_l1.wvd
Normal file
Binary file not shown.
BIN
unshackle/WVDs/ktc_tv358dvb_17.0.0_6b8f3314_12063_l3.wvd
Normal file
BIN
unshackle/WVDs/ktc_tv358dvb_17.0.0_6b8f3314_12063_l3.wvd
Normal file
Binary file not shown.
BIN
unshackle/WVDs/lg_50ut73006la.cekukh_17.0.0_22163355_7110_l1.wvd
Normal file
BIN
unshackle/WVDs/lg_50ut73006la.cekukh_17.0.0_22163355_7110_l1.wvd
Normal file
Binary file not shown.
Binary file not shown.
BIN
unshackle/WVDs/samsung_sm-g975f_16.0.0_1e7c5ba2_22589_l3.wvd
Normal file
BIN
unshackle/WVDs/samsung_sm-g975f_16.0.0_1e7c5ba2_22589_l3.wvd
Normal file
Binary file not shown.
BIN
unshackle/WVDs/xiaomi_m2102j20sg_16.0.0_b007be8e_22590_l3.wvd
Normal file
BIN
unshackle/WVDs/xiaomi_m2102j20sg_16.0.0_b007be8e_22590_l3.wvd
Normal file
Binary file not shown.
@@ -129,20 +129,20 @@ class MSL:
|
||||
raise Exception("- No CDM available")
|
||||
cdm.parse_license(msl_keys.cdm_session, key_data["cdmkeyresponse"])
|
||||
keys = cdm.get_keys(msl_keys.cdm_session)
|
||||
cls.log.info(f"Keys: {keys}")
|
||||
cls.log.debug(f"Keys: {keys}")
|
||||
encryption_key = MSL.get_widevine_key(
|
||||
kid=base64.b64decode(key_data["encryptionkeyid"]),
|
||||
keys=keys,
|
||||
permissions=["allow_encrypt", "allow_decrypt"]
|
||||
)
|
||||
msl_keys.encryption = encryption_key
|
||||
cls.log.info(f"Encryption key: {encryption_key}")
|
||||
cls.log.debug(f"Encryption key: {encryption_key}")
|
||||
sign = MSL.get_widevine_key(
|
||||
kid=base64.b64decode(key_data["hmackeyid"]),
|
||||
keys=keys,
|
||||
permissions=["allow_sign", "allow_signature_verify"]
|
||||
)
|
||||
cls.log.info(f"Sign key: {sign}")
|
||||
cls.log.debug(f"Sign key: {sign}")
|
||||
msl_keys.sign = sign
|
||||
|
||||
elif scheme == KeyExchangeSchemes.AsymmetricWrapped:
|
||||
@@ -244,7 +244,7 @@ class MSL:
|
||||
|
||||
@classmethod
|
||||
def get_widevine_key(cls, kid, keys: list[Key], permissions):
|
||||
cls.log.info(f"KID: {Key.kid_to_uuid(kid)}")
|
||||
cls.log.debug(f"KID: {Key.kid_to_uuid(kid)}")
|
||||
for key in keys:
|
||||
# cls.log.info(f"KEY: {key.kid_to_uuid}")
|
||||
if key.kid != Key.kid_to_uuid(kid):
|
||||
@@ -258,10 +258,10 @@ class MSL:
|
||||
return key.key
|
||||
return None
|
||||
|
||||
def send_message(self, endpoint, params, application_data, userauthdata=None):
|
||||
def send_message(self, endpoint, params, application_data, userauthdata=None, headers=None, unwrap_result=True):
|
||||
message = self.create_message(application_data, userauthdata)
|
||||
res = self.session.post(url=endpoint, data=message, params=params)
|
||||
header, payload_data = self.parse_message(res.text)
|
||||
res = self.session.post(url=endpoint, data=message, params=params, headers=headers)
|
||||
header, payload_data = self.parse_message(res.text, unwrap_result=unwrap_result)
|
||||
if "errordata" in header:
|
||||
raise Exception(
|
||||
"- MSL response message contains an error: {}".format(
|
||||
@@ -302,7 +302,7 @@ class MSL:
|
||||
|
||||
return message
|
||||
|
||||
def decrypt_payload_chunks(self, payload_chunks):
|
||||
def decrypt_payload_chunks(self, payload_chunks, unwrap_result=True):
|
||||
"""
|
||||
Decrypt and extract data from payload chunks
|
||||
|
||||
@@ -310,7 +310,6 @@ class MSL:
|
||||
:return: json object
|
||||
"""
|
||||
raw_data = ""
|
||||
|
||||
for payload_chunk in payload_chunks:
|
||||
# todo ; verify signature of payload_chunk["signature"] against payload_chunk["payload"]
|
||||
# expecting base64-encoded json string
|
||||
@@ -344,10 +343,12 @@ class MSL:
|
||||
self.log.critical(f"- {error}")
|
||||
raise Exception(f"- MSL response message contains an error: {error}")
|
||||
# sys.exit(1)
|
||||
self.log.debug(f"Payload Chunks: {data}")
|
||||
if unwrap_result:
|
||||
return data["result"]
|
||||
return data
|
||||
|
||||
return data["result"]
|
||||
|
||||
def parse_message(self, message):
|
||||
def parse_message(self, message, unwrap_result=True):
|
||||
"""
|
||||
Parse an MSL message into a header and list of payload chunks
|
||||
|
||||
@@ -359,7 +360,7 @@ class MSL:
|
||||
header = parsed_message[0]
|
||||
encrypted_payload_chunks = parsed_message[1:] if len(parsed_message) > 1 else []
|
||||
if encrypted_payload_chunks:
|
||||
payload_chunks = self.decrypt_payload_chunks(encrypted_payload_chunks)
|
||||
payload_chunks = self.decrypt_payload_chunks(encrypted_payload_chunks, unwrap_result=unwrap_result)
|
||||
else:
|
||||
payload_chunks = {}
|
||||
|
||||
|
||||
@@ -31,6 +31,19 @@ class UserAuthentication(MSLObject):
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def UserIDToken(cls, token_data, signature, master_token):
|
||||
return cls(
|
||||
scheme=UserAuthenticationSchemes.UserIDToken,
|
||||
authdata={
|
||||
"useridtoken": {
|
||||
"tokendata": token_data,
|
||||
"signature": signature
|
||||
},
|
||||
"mastertoken": master_token
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def NetflixIDCookies(cls, netflixid, securenetflixid):
|
||||
"""
|
||||
|
||||
@@ -15,6 +15,7 @@ class EntityAuthenticationSchemes(Scheme):
|
||||
class UserAuthenticationSchemes(Scheme):
|
||||
"""https://github.com/Netflix/msl/wiki/User-Authentication-%28Configuration%29"""
|
||||
EmailPassword = "EMAIL_PASSWORD"
|
||||
UserIDToken = "USER_ID_TOKEN"
|
||||
NetflixIDCookies = "NETFLIXID"
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -36,61 +36,32 @@ title_cache_max_retention: 86400 # Maximum cache retention for fallback when API
|
||||
muxing:
|
||||
set_title: true
|
||||
|
||||
# Configuration for serve
|
||||
serve:
|
||||
api_secret: "kenzuya"
|
||||
users:
|
||||
secret_key_for_user:
|
||||
devices:
|
||||
- generic_nexus_4464_l3
|
||||
username: user
|
||||
|
||||
# Login credentials for each Service
|
||||
credentials:
|
||||
# Direct credentials (no profile support)
|
||||
EXAMPLE: email@example.com:password
|
||||
|
||||
# Per-profile credentials with default fallback
|
||||
SERVICE_NAME:
|
||||
default: default@email.com:password # Used when no -p/--profile is specified
|
||||
profile1: user1@email.com:password1
|
||||
profile2: user2@email.com:password2
|
||||
|
||||
# Per-profile credentials without default (requires -p/--profile)
|
||||
SERVICE_NAME2:
|
||||
john: john@example.com:johnspassword
|
||||
jane: jane@example.com:janespassword
|
||||
|
||||
# You can also use list format for passwords with special characters
|
||||
SERVICE_NAME3:
|
||||
default: ["user@email.com", ":PasswordWith:Colons"]
|
||||
|
||||
Netflix:
|
||||
default: ["ariel-prinsess828@ezweb.ne.jp", "AiNe892186"]
|
||||
secondary: ["csyc5478@naver.com", "wl107508!"]
|
||||
third: ["erin.e.pfleger@gmail.com", "Pfleger93"]
|
||||
# default: ["pbgarena0838@gmail.com", "Andhika1978"]
|
||||
# Override default directories used across unshackle
|
||||
directories:
|
||||
cache: Cache
|
||||
# cookies: Cookies
|
||||
dcsl: DCSL # Device Certificate Status List
|
||||
downloads: Downloads
|
||||
downloads: /mnt/ketuakenzuya/Downloads/
|
||||
logs: Logs
|
||||
temp: Temp
|
||||
# wvds: WVDs
|
||||
# Additional directories that can be configured:
|
||||
# commands: Commands
|
||||
# services:
|
||||
# - /path/to/services
|
||||
# - /other/path/to/services
|
||||
# vaults: Vaults
|
||||
# fonts: Fonts
|
||||
|
||||
# Pre-define which Widevine or PlayReady device to use for each Service
|
||||
temp: /tmp/unshackle
|
||||
cdm:
|
||||
# Global default CDM device (fallback for all services/profiles)
|
||||
default: chromecdm
|
||||
default: samsung_sm-g975f_16.0.0_1e7c5ba2_22589_l3
|
||||
|
||||
# Direct service-specific CDM
|
||||
DIFFERENT_EXAMPLE: PRD_1
|
||||
|
||||
# Per-profile CDM configuration
|
||||
EXAMPLE:
|
||||
john_sd: chromecdm_903_l3 # Profile 'john_sd' uses Chrome CDM L3
|
||||
jane_uhd: nexus_5_l1 # Profile 'jane_uhd' uses Nexus 5 L1
|
||||
default: generic_android_l3 # Default CDM for this service
|
||||
|
||||
# Use pywidevine Serve-compliant Remote CDMs
|
||||
remote_cdm:
|
||||
- name: "chromecdm"
|
||||
device_name: widevine
|
||||
@@ -127,22 +98,7 @@ key_vaults:
|
||||
api_mode: "decrypt_labs"
|
||||
host: "https://keyvault.decryptlabs.com"
|
||||
password: "7547150416_41da0a32d6237d83_KeyXtractor_api_ext"
|
||||
# Additional vault types:
|
||||
# - type: API
|
||||
# name: "Remote Vault"
|
||||
# uri: "https://key-vault.example.com"
|
||||
# token: "secret_token"
|
||||
# no_push: true # This vault will only provide keys, not receive them
|
||||
# - type: MySQL
|
||||
# name: "MySQL Vault"
|
||||
# host: "127.0.0.1"
|
||||
# port: 3306
|
||||
# database: vault
|
||||
# username: user
|
||||
# password: pass
|
||||
# no_push: false # Default behavior - vault both provides and receives keys
|
||||
|
||||
# Choose what software to use to download data
|
||||
downloader: aria2c
|
||||
# Options: requests | aria2c | curl_impersonate | n_m3u8dl_re
|
||||
# Can also be a mapping:
|
||||
@@ -199,26 +155,10 @@ filenames:
|
||||
# API key for The Movie Database (TMDB)
|
||||
tmdb_api_key: "8f5c14ef648a0abdd262cf809e11fcd4"
|
||||
|
||||
# conversion_method:
|
||||
# - auto (default): Smart routing - subby for WebVTT/SAMI, standard for others
|
||||
# - subby: Always use subby with advanced processing
|
||||
# - pycaption: Use only pycaption library (no SubtitleEdit, no subby)
|
||||
# - subtitleedit: Prefer SubtitleEdit when available, fall back to pycaption
|
||||
subtitle:
|
||||
conversion_method: auto
|
||||
sdh_method: auto
|
||||
|
||||
# Configuration for pywidevine's serve functionality
|
||||
serve:
|
||||
users:
|
||||
secret_key_for_user:
|
||||
devices:
|
||||
- generic_nexus_4464_l3
|
||||
username: user
|
||||
# devices:
|
||||
# - '/path/to/device.wvd'
|
||||
|
||||
# Configuration data for each Service
|
||||
services:
|
||||
# Service-specific configuration goes here
|
||||
# Profile-specific configurations can be nested under service names
|
||||
@@ -251,6 +191,21 @@ services:
|
||||
|
||||
# External proxy provider services
|
||||
proxy_providers:
|
||||
gluetun:
|
||||
base_port: 8888
|
||||
auto_cleanup: true
|
||||
container_prefix: "unshackle-gluetun"
|
||||
verify_ip: true
|
||||
providers:
|
||||
protonvpn:
|
||||
vpn_type: "openvpn"
|
||||
credentials:
|
||||
username: "L83JaCnXKIviymQm"
|
||||
password: "UewUDYdthTLLhOBJDympFFxJn4uG12BV"
|
||||
server_countries:
|
||||
us: United States
|
||||
id: Indonesia
|
||||
kr: Korea
|
||||
basic:
|
||||
SG:
|
||||
- "http://127.0.0.1:6004"
|
||||
|
||||
Reference in New Issue
Block a user