From 7fc4df7d0b4231d1d4ec673c52079b532168c5ae Mon Sep 17 00:00:00 2001 From: VineFeeder Date: Sat, 6 Jun 2026 15:01:52 +0100 Subject: [PATCH] shim for compiled code --- .../envied/src/envied/services/DSNP/README.md | 76 +++++++++++++++++ .../src/envied/services/DSNP/__init__.py | 13 +++ .../src/envied/services/DSNP/config.yaml | 84 +++++++++++++++++++ .../src/envied/services/DSNP/queries.py | 14 ++++ packages/envied/src/unshackle/__init__.py | 5 ++ .../envied/src/unshackle/core/__init__.py | 1 + packages/envied/src/unshackle/core/cacher.py | 1 + .../envied/src/unshackle/core/cdm/__init__.py | 0 .../core/cdm/decrypt_labs_remote_cdm.py | 1 + .../envied/src/unshackle/core/cdm/detect.py | 1 + .../envied/src/unshackle/core/constants.py | 1 + .../envied/src/unshackle/core/credential.py | 1 + .../src/unshackle/core/manifests/__init__.py | 1 + .../src/unshackle/core/search_result.py | 1 + packages/envied/src/unshackle/core/service.py | 1 + .../src/unshackle/core/titles/__init__.py | 1 + .../src/unshackle/core/tracks/__init__.py | 1 + .../src/unshackle/core/utils/__init__.py | 1 + .../src/unshackle/core/utils/collections.py | 1 + .../src/unshackle/core/utils/ip_info.py | 1 + packages/vinefeeder/pyproject.toml | 2 +- .../src/vinefeeder/services/PLEX/__init__.py | 2 +- 22 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 packages/envied/src/envied/services/DSNP/README.md create mode 100644 packages/envied/src/envied/services/DSNP/__init__.py create mode 100644 packages/envied/src/envied/services/DSNP/config.yaml create mode 100644 packages/envied/src/envied/services/DSNP/queries.py create mode 100644 packages/envied/src/unshackle/__init__.py create mode 100644 packages/envied/src/unshackle/core/__init__.py create mode 100644 packages/envied/src/unshackle/core/cacher.py create mode 100644 packages/envied/src/unshackle/core/cdm/__init__.py create mode 100644 packages/envied/src/unshackle/core/cdm/decrypt_labs_remote_cdm.py create mode 100644 packages/envied/src/unshackle/core/cdm/detect.py create mode 100644 packages/envied/src/unshackle/core/constants.py create mode 100644 packages/envied/src/unshackle/core/credential.py create mode 100644 packages/envied/src/unshackle/core/manifests/__init__.py create mode 100644 packages/envied/src/unshackle/core/search_result.py create mode 100644 packages/envied/src/unshackle/core/service.py create mode 100644 packages/envied/src/unshackle/core/titles/__init__.py create mode 100644 packages/envied/src/unshackle/core/tracks/__init__.py create mode 100644 packages/envied/src/unshackle/core/utils/__init__.py create mode 100644 packages/envied/src/unshackle/core/utils/collections.py create mode 100644 packages/envied/src/unshackle/core/utils/ip_info.py diff --git a/packages/envied/src/envied/services/DSNP/README.md b/packages/envied/src/envied/services/DSNP/README.md new file mode 100644 index 0000000..78006af --- /dev/null +++ b/packages/envied/src/envied/services/DSNP/README.md @@ -0,0 +1,76 @@ +# DisneyPlus(디즈니플러스) + +``` +uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r SDR,HDR10,DV DSNP entity-4d12671a-f0ad-4c3f-8526-09ae6772390b +``` + +## Information(정보) + +- Authorization: Credentials, Web Token +- Security: UHD@L1/SL3000, FHD@L1/SL3000, HD@L3/SL2000 +- Working Client Agent: AndroidTV +- Support Codec + - Video: H264, H265 + - Audio: AAC, AC3, ATMOS, DTS:X(P2:IMAX) + - Range: SDR, HDR10, HDR10, DV + +## Support Args(지원하는 명령어 인자) + +- `-i`, `--imax`: Prefer IMAX Enhanced version if available. +- `-r`, `--remastered-ar`: Prefer Remastered Aspect Ratio if available. +- `-e`, `--extras`: Select a extras video if available. +- `-tu`, `--tier-unlimits`: Remove stream quality restrictions for a specific account. + +## Tips + +- To enable the web refresh token-based login method, please comment out or delete the DSNP section under credentials in `unshackle.yaml`. + 웹 리프레시 토큰 기반 로그인 방식을 활성화하려면 `unshackle.yaml`의 credentials에서 DSNP부분을 주석 처리하거나 삭제하세요. + + ``` + credentials: + ... + # DSNP: example@example.com:example + ``` + +- Configure user settings within the `unshackle.yaml` file. + 사용자 설정은 `unshackle.yaml`에서 다음과 같이 사용하세요. + + ``` + services: + DSNP: + ## 사용자 환경설정 + ## User configuration + # 해당 설정값이 주석처리 되어 있는 경우에는 설정값들이 자동으로 선택됩니다. + # If these settings are commented out, values will be selected automatically. + preferences: + # 사용할 프로필의 인덱스 번호를 지정합니다. (0 = 첫 번째 프로필, 1 = 두 번째 프로필 등) + # Specifies the index of the profile to use. (0 = first profile, 1 = second profile, etc.) + # 값이 설정되지 않은 경우에는 자동으로 PIN이 안 걸려 있고 키즈 모드가 아닌 프로필로 자동 선택됩니다. + # If no value is set, a profile without a PIN and not in Kids Mode will be automatically selected. + profile: 0 + + # 서비스 내에서 표시되는 메타데이터 언어를 선택합니다. + # Selects the metadata language displayed within the service. + # 언어 설정은 Disney+에서 지원하는 언어 코드(예: "ko", "en")만 사용 가능합니다. + # Language settings are only available for language codes supported by Disney+ (e.g., "ko", "en"). + # 값이 설정되지 않은 경우에는 현재 프로필에 설정된 언어 설정을 사용합니다. + # If no value is set, the language settings of the current profile will be used. + # language: "ko" + + # 매니페스트 로그 출력 레벨을 설정합니다. + # Sets the manifest log output level. + # 로그를 항상 표시해야 하는 경우 "info"를 사용하고, 그 외의 모든 경우에는 가급적 "debug"를 사용하십시오. + # Use "info" if the log must always be displayed; otherwise, use "debug" whenever possible. + # 값이 설정되지 않은 경우 기본값은 "debug"로 적용됩니다. + # If no value is set, the default level is "debug". + # manifest_log: "info" + ``` + +- To enable the tier_unlimits command by default, add the following to `unshackle.yaml`. + tier_unlimits 명령을 기본값으로 활성화하려면 `unshackle.yaml`에 다음을 추가하세요. + ``` + dl: + ... + DSNP: + tier_unlimits: True + ``` diff --git a/packages/envied/src/envied/services/DSNP/__init__.py b/packages/envied/src/envied/services/DSNP/__init__.py new file mode 100644 index 0000000..037d34c --- /dev/null +++ b/packages/envied/src/envied/services/DSNP/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .dsnp import DSNP + +""" +Service code for Disney+ Streaming Service (https://disneyplus.com).\n +Version: 26.05.06 + +Author: Made by CodeName393 and Improvement by Hugov, sp4rk.y with Special Thanks to narakama, Sam\n +Authorization: Credentials, Web Token\n +Security: UHD@L1/SL3000 FHD@L1/SL3000 HD@L3/SL2000 +""" +__all__ = ("DSNP",) diff --git a/packages/envied/src/envied/services/DSNP/config.yaml b/packages/envied/src/envied/services/DSNP/config.yaml new file mode 100644 index 0000000..2d1caa3 --- /dev/null +++ b/packages/envied/src/envied/services/DSNP/config.yaml @@ -0,0 +1,84 @@ +## DO NOT EDIT THIS FILE +# 해당 config 파일은 개발자 외에는 수정하지 마세요. +# This configuration file should not be modified by anyone other than developers. +# 사용자 환경설정은 반드시 "unshackle.yaml"에서만 사용하세요. +# User configuration must be performed only in "unshackle.yaml". + +certificate: | + CAUSugUKtAIIAxIQbj3s4jO5oUyWjDWqjfr9WRjA2afZBSKOAjCCAQoCggEBALhKWfnyA+FGn5P3tl6ffDjoGq2Oq86hKGl6aZIaGaF7XHPO5mIk7Q35ml + ZIgg1A458Udb4eXRws1n+kJFqtZXCY5S1yElLP0Om1WQsoEY2stpl+PZTGnVv/CsOJGKQ8K4KMr7rKjZem9lA9BrBoxgfXY3tbwlnSf3wTEohyANb5Qfpa + xsU4v8tQDA8PcjzzV9ICodl6crcFZhAy4QMNXfbWOv/ZrGFx5blSXrzP1sMQ64IY8bjUYw4coZM34NDhu8aCA692g8k2mTz2494x7u3Is8v7RKC9ZNiETE + K5/4oeVclXPpelNQokR4uvggnCD1L2EULG/pp6wnk1yWNNLxcCAwEAAToHYmFtdGVjaBKAA2FqHlqkE7EUmdOLiCi0hy5jRgBDJrU1CWNHfH6r2i6s5T5k + 6LK7ZfD65Tv6uyqq1k82PsDz4++kxbpfJDZaypFbae4XPc6lZxRCc5X0toX/x9TftOQQ4N82l5Hxoha569EPRkrnNy7rO7xrRILa3ZVj1alttEnEEjxEuw + SV8usdlUg8/LvLA2C59T/HA2I77k7yVbTrVdy0f81r2l+E2SslivCy1JD3xKlgoaKl4xBnRxItWt8+DCw1Xm2lemYl2LGoh1Wk9gvlXQvr2Jv2+dFX3RNs + i5sd00KS9sePszfjoTkQ6fmpRd7ZgFCGFWYB9JZ92aGUFQRE14OTST2uwSf32YCfsoATDNs4V6dB8YDoTGKFGrcoc4gtHPKySGNt7z/fOW4/01ZGzKqoVY + Fp3jPq7R0qyt5P6fU5NshbLh5VKcnQvwg62BuKsdwV9u4NV36b2a546hGRl/GBneQ+QDA7NRrgITR33Sz02Oq8yJr3sy24GfZRTbtLJ4qiWkjtw== + +## config ( {configVersion}/{clientId}/{deviceFamily}/{sdkVersion}/{applicationRuntime}/{deviceProfile}/{environment} ) ## +# Browser (windows, chrome) : /browser/v34.4/windows/chrome/prod.json +# Android Phone : /android/v18.0.0/google/handset/prod.json +# Android TV : /android/v18.0.0/google/tv/prod.json +# Amazon Fire TV : /android/v18.0.0/amazon/tv/prod.json +# Apple Iphone(old) : https://bam-sdk-configs.bamgrid.com/bam-sdk/v2.0/disney-svod-3d9324fc/apple/v9.10.0/ios/iphone/prod.json +# Apple Ipad(old) : https://bam-sdk-configs.bamgrid.com/bam-sdk/v2.0/disney-svod-3d9324fc/apple/v9.10.0/ios/ipad/prod.json + +endpoints: + config: "https://client-sdk-configs.bamgrid.com/bam-sdk/v7.0/disney-svod-3d9324fc/android/v18.0.0/google/tv/prod.json" + +## user_agent (okhttp/5.0.0-alpha.14) ## +# android-phone : BAMSDK/v18.0.0 (disney-svod-3d9324fc 26.0.2+rc1-2026.01.29.0; v7.0/v18.0.0; android; phone) +# android-tv : BAMSDK/v18.0.0 (disney-svod-3d9324fc 26.0.2+rc1-2026.01.29.0; v7.0/v18.0.0; android; tv) + +## api_key ## +# browser : ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84 +# android : ZGlzbmV5JmFuZHJvaWQmMS4wLjA.bkeb0m230uUhv8qrAXuNu39tbE_mD5EEhM_NAcohjyA +# apple : ZGlzbmV5JmFwcGxlJjEuMC4w.H9L7eJvc2oPYwDgmkoar6HzhBJRuUUzt_PcaC3utBI4 + +## yp_service_id ## +# browser : 63626081279ebe65eb50fb54 +# android : 624b805dafc5c73635b1a216 + +bamsdk: + sdk_version: "18.0.1" + application_version: "26.3.0+rc4-2026.03.12.0" + explore_version: "v1.14" + client: "disney-svod-3d9324fc" + user_agent: "BAMSDK/v18.0.1 (disney-svod-3d9324fc 26.3.0+rc4-2026.03.12.0; v7.0/v18.0.0; android; tv)" + api_key: "ZGlzbmV5JmFuZHJvaWQmMS4wLjA.bkeb0m230uUhv8qrAXuNu39tbE_mD5EEhM_NAcohjyA" + yp_service_id: "624b805dafc5c73635b1a216" + +device: + family: "android" + profile: "tv" + platform: "android/google/tv" # {deviceFamily}/{applicationRuntime}/{deviceProfile} + platform_id: "android-tv" + applicationRuntime: "android" + operatingSystem: "Android" + operatingSystemVersion: "16" + +# ## 사용자 환경설정 +# ## User configuration +# # 해당 설정값이 주석처리 되어 있는 경우에는 설정값들이 자동으로 선택됩니다. +# # If these settings are commented out, values will be selected automatically. +# preferences: +# # 사용할 프로필의 인덱스 번호를 지정합니다. (0 = 첫 번째 프로필, 1 = 두 번째 프로필 등) +# # Specifies the index of the profile to use. (0 = first profile, 1 = second profile, etc.) +# # 값이 설정되지 않은 경우에는 자동으로 PIN이 안 걸려 있고 키즈 모드가 아닌 프로필로 자동 선택됩니다. +# # If no value is set, a profile without a PIN and not in Kids Mode will be automatically selected. +# profile: 0 + +# # 서비스 내에서 표시되는 메타데이터 언어를 선택합니다. +# # Selects the metadata language displayed within the service. +# # 언어 설정은 Disney+에서 지원하는 언어 코드(예: "ko", "en")만 사용 가능합니다. +# # Language settings are only available for language codes supported by Disney+ (e.g., "ko", "en"). +# # 값이 설정되지 않은 경우에는 현재 프로필에 설정된 언어 설정을 사용합니다. +# # If no value is set, the language settings of the current profile will be used. +# language: "ko" + +# # 매니페스트 로그 출력 레벨을 설정합니다. +# # Sets the manifest log output level. +# # 로그를 항상 표시해야 하는 경우 "info"를 사용하고, 그 외의 모든 경우에는 가급적 "debug"를 사용하십시오. +# # Use "info" if the log must always be displayed; otherwise, use "debug" whenever possible. +# # 값이 설정되지 않은 경우 기본값은 "debug"로 적용됩니다. +# # If no value is set, the default level is "debug". +# manifest_log: "debug" diff --git a/packages/envied/src/envied/services/DSNP/queries.py b/packages/envied/src/envied/services/DSNP/queries.py new file mode 100644 index 0000000..4ebf957 --- /dev/null +++ b/packages/envied/src/envied/services/DSNP/queries.py @@ -0,0 +1,14 @@ +# REQUEST_DEVICE_CODE = """mutation requestLicensePlate($input: RequestLicensePlateInput!) { requestLicensePlate(requestLicensePlate: $input) { licensePlate expirationTime expiresInSeconds } }""" +CHECK_EMAIL = """query check($email: String!) { check(email: $email) { operations nextOperation } }""" +LOGIN = """mutation login($input: LoginInput!, $includeIdentity: Boolean!, $includeAccountConsentToken: Boolean!) { login(login: $input) { account { __typename ...accountGraphFragment } actionGrant activeSession { __typename ...sessionGraphFragment } identity @include(if: $includeIdentity) { __typename ...identityGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } } fragment accountGraphFragment on Account { id umpMessages { data { messages { messageId messageSource displayLocations content } } } accountConsentToken @include(if: $includeAccountConsentToken) activeProfile { id umpMessages { data { messages { messageId content } } } } profiles { __typename ...profileGraphFragment } profileRequirements { primaryProfiles { personalInfo { requiresCollection } } secondaryProfiles { personalInfo { requiresCollection } personalInfoJrMode { requiresCollection } } } parentalControls { isProfileCreationProtected } flows { star { isOnboarded } } attributes { email emailVerified userVerified maxNumberOfProfilesAllowed locations { manual { country } purchase { country } registration { geoIp { country } } } } } fragment sessionGraphFragment on Session { sessionId device { id } entitlements experiments { featureId variantId version } features { coPlay download noAds } homeLocation { countryCode adsSupported } inSupportedLocation isSubscriber location { countryCode adsSupported } portabilityLocation { countryCode } preferredMaturityRating { impliedMaturityRating ratingSystem } } fragment identityGraphFragment on Identity { id email repromptSubscriberAgreement attributes { passwordResetRequired } commerce { notifications { subscriptionId type showNotification offerData { productType expectedTransition { date price { amount currency } } cypherKeys { key value type } } currentOffer { offerId price { amount currency frequency } } } } flows { marketingPreferences { isOnboarded eligibleForOnboarding } personalInfo { eligibleForCollection requiresCollection } } personalInfo { dateOfBirth gender } locations { purchase { country } } subscriber { subscriberStatus subscriptionAtRisk overlappingSubscription doubleBilled doubleBilledProviders subscriptions { id groupId state partner isEntitled source { sourceProvider sourceType subType sourceRef } product { id sku name entitlements { id name partner } bundle subscriptionPeriod earlyAccess trial { duration } categoryCodes } stacking { status overlappingSubscriptionProviders previouslyStacked previouslyStackedByProvider } term { purchaseDate startDate expiryDate nextRenewalDate pausedDate churnedDate isFreeTrial } } } consent { id idType token } }""" +LOGIN_ACTION_GRANT = """mutation loginWithActionGrant($input: LoginWithActionGrantInput!, $includeAccountConsentToken: Boolean!) { loginWithActionGrant(login: $input) { account { __typename ...accountGraphFragment } activeSession { __typename ...sessionGraphFragment } identity { __typename ...identityGraphFragment } actionGrant } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } } fragment accountGraphFragment on Account { id umpMessages { data { messages { messageId messageSource displayLocations content } } } accountConsentToken @include(if: $includeAccountConsentToken) activeProfile { id umpMessages { data { messages { messageId content } } } } profiles { __typename ...profileGraphFragment } profileRequirements { primaryProfiles { personalInfo { requiresCollection } } secondaryProfiles { personalInfo { requiresCollection } personalInfoJrMode { requiresCollection } } } parentalControls { isProfileCreationProtected } flows { star { isOnboarded } } attributes { email emailVerified userVerified maxNumberOfProfilesAllowed locations { manual { country } purchase { country } registration { geoIp { country } } } } } fragment sessionGraphFragment on Session { sessionId device { id } entitlements experiments { featureId variantId version } features { coPlay download noAds } homeLocation { countryCode adsSupported } inSupportedLocation isSubscriber location { countryCode adsSupported } portabilityLocation { countryCode } preferredMaturityRating { impliedMaturityRating ratingSystem } } fragment identityGraphFragment on Identity { id email repromptSubscriberAgreement attributes { passwordResetRequired } commerce { notifications { subscriptionId type showNotification offerData { productType expectedTransition { date price { amount currency } } cypherKeys { key value type } } currentOffer { offerId price { amount currency frequency } } } } flows { marketingPreferences { isOnboarded eligibleForOnboarding } personalInfo { eligibleForCollection requiresCollection } } personalInfo { dateOfBirth gender } locations { purchase { country } } subscriber { subscriberStatus subscriptionAtRisk overlappingSubscription doubleBilled doubleBilledProviders subscriptions { id groupId state partner isEntitled source { sourceProvider sourceType subType sourceRef } product { id sku name entitlements { id name partner } bundle subscriptionPeriod earlyAccess trial { duration } categoryCodes } stacking { status overlappingSubscriptionProviders previouslyStacked previouslyStackedByProvider } term { purchaseDate startDate expiryDate nextRenewalDate pausedDate churnedDate isFreeTrial } } } consent { id idType token } }""" +LOGIN_OTP = """mutation authenticateWithOtp($input: AuthenticateWithOtpInput!) { authenticateWithOtp(authenticateWithOtp: $input) { actionGrant securityAction passwordRules { __typename ...passwordRulesFragment } } } fragment passwordRulesFragment on PasswordRules { minLength charTypes }""" +ME = """query me($includeAccountConsentToken: Boolean!) { me { account { __typename ...accountGraphFragment } activeSession { __typename ...sessionGraphFragment } identity { __typename ...identityGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } } fragment accountGraphFragment on Account { id umpMessages { data { messages { messageId messageSource displayLocations content } } } accountConsentToken @include(if: $includeAccountConsentToken) activeProfile { id umpMessages { data { messages { messageId content } } } } profiles { __typename ...profileGraphFragment } profileRequirements { primaryProfiles { personalInfo { requiresCollection } } secondaryProfiles { personalInfo { requiresCollection } personalInfoJrMode { requiresCollection } } } parentalControls { isProfileCreationProtected } flows { star { isOnboarded } } attributes { email emailVerified userVerified maxNumberOfProfilesAllowed locations { manual { country } purchase { country } registration { geoIp { country } } } } } fragment sessionGraphFragment on Session { sessionId device { id } entitlements experiments { featureId variantId version } features { coPlay download noAds } homeLocation { countryCode adsSupported } inSupportedLocation isSubscriber location { countryCode adsSupported } portabilityLocation { countryCode } preferredMaturityRating { impliedMaturityRating ratingSystem } } fragment identityGraphFragment on Identity { id email repromptSubscriberAgreement attributes { passwordResetRequired } commerce { notifications { subscriptionId type showNotification offerData { productType expectedTransition { date price { amount currency } } cypherKeys { key value type } } currentOffer { offerId price { amount currency frequency } } } } flows { marketingPreferences { isOnboarded eligibleForOnboarding } personalInfo { eligibleForCollection requiresCollection } } personalInfo { dateOfBirth gender } locations { purchase { country } } subscriber { subscriberStatus subscriptionAtRisk overlappingSubscription doubleBilled doubleBilledProviders subscriptions { id groupId state partner isEntitled source { sourceProvider sourceType subType sourceRef } product { id sku name entitlements { id name partner } bundle subscriptionPeriod earlyAccess trial { duration } categoryCodes } stacking { status overlappingSubscriptionProviders previouslyStacked previouslyStackedByProvider } term { purchaseDate startDate expiryDate nextRenewalDate pausedDate churnedDate isFreeTrial } } } consent { id idType token } }""" +REFRESH_TOKEN = """mutation refreshToken($refreshToken: RefreshTokenInput!) { refreshToken(refreshToken: $refreshToken) { activeSession { sessionId } } }""" +REGISTER_DEVICE = """mutation ($registerDevice: RegisterDeviceInput!) { registerDevice(registerDevice: $registerDevice) { __typename } }""" +REQUESET_OTP = """mutation requestOtp($input: RequestOtpInput!) { requestOtp(requestOtp: $input) { accepted } }""" +SET_IMAX = """mutation updateProfileImaxEnhancedVersion($input: UpdateProfileImaxEnhancedVersionInput!, $includeProfile: Boolean!) { updateProfileImaxEnhancedVersion(updateProfileImaxEnhancedVersion: $input) { accepted profile @include(if: $includeProfile) { __typename ...profileGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } }""" +SET_REMASTERED_AR = """mutation updateProfileRemasteredAspectRatio($input: UpdateProfileRemasteredAspectRatioInput!, $includeProfile: Boolean!) { updateProfileRemasteredAspectRatio(updateProfileRemasteredAspectRatio: $input) { accepted profile @include(if: $includeProfile) { __typename ...profileGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } }""" +SET_APP_LANGUAGE = """mutation updateProfileAppLanguage($input: UpdateProfileAppLanguageInput!, $includeProfile: Boolean!) { updateProfileAppLanguage(updateProfileAppLanguage: $input) { accepted profile @include(if: $includeProfile) { __typename ...profileGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled isGeminiOnboarded profileLinked languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } linkedProfile { pinProtected } } }""" +SWITCH_PROFILE = """mutation switchProfile($input: SwitchProfileInput!, $includeIdentity: Boolean!, $includeAccountConsentToken: Boolean!) { switchProfile(switchProfile: $input) { account { __typename ...accountGraphFragment } activeSession { __typename ...sessionGraphFragment } identity @include(if: $includeIdentity) { __typename ...identityGraphFragment } } } fragment profileGraphFragment on Profile { id name personalInfo { dateOfBirth gender } maturityRating { ratingSystem ratingSystemValues contentMaturityRating maxRatingSystemValue isMaxContentMaturityRating suggestedMaturityRatings { minimumAge maximumAge ratingSystemValue } } isAge21Verified flows { star { eligibleForOnboarding isOnboarded } personalInfo { eligibleForCollection requiresCollection } } attributes { isDefault kidsModeEnabled languagePreferences { appLanguage playbackLanguage preferAudioDescription preferSDH subtitleLanguage subtitlesEnabled } parentalControls { isPinProtected kidProofExitEnabled liveAndUnratedContent { enabled available } } playbackSettings { autoplay backgroundVideo backgroundAudio prefer133 preferImaxEnhancedVersion } avatar { id userSelected } privacySettings { consents { consentType value } } } } fragment accountGraphFragment on Account { id umpMessages { data { messages { messageId messageSource displayLocations content } } } accountConsentToken @include(if: $includeAccountConsentToken) activeProfile { id umpMessages { data { messages { messageId content } } } } profiles { __typename ...profileGraphFragment } profileRequirements { primaryProfiles { personalInfo { requiresCollection } } secondaryProfiles { personalInfo { requiresCollection } personalInfoJrMode { requiresCollection } } } parentalControls { isProfileCreationProtected } flows { star { isOnboarded } } attributes { email emailVerified userVerified maxNumberOfProfilesAllowed locations { manual { country } purchase { country } registration { geoIp { country } } } } } fragment sessionGraphFragment on Session { sessionId device { id } entitlements experiments { featureId variantId version } features { coPlay download noAds } homeLocation { countryCode adsSupported } inSupportedLocation isSubscriber location { countryCode adsSupported } portabilityLocation { countryCode } preferredMaturityRating { impliedMaturityRating ratingSystem } } fragment identityGraphFragment on Identity { id email repromptSubscriberAgreement attributes { passwordResetRequired } commerce { notifications { subscriptionId type showNotification offerData { productType expectedTransition { date price { amount currency } } cypherKeys { key value type } } currentOffer { offerId price { amount currency frequency } } } } flows { marketingPreferences { isOnboarded eligibleForOnboarding } personalInfo { eligibleForCollection requiresCollection } } personalInfo { dateOfBirth gender } locations { purchase { country } } subscriber { subscriberStatus subscriptionAtRisk overlappingSubscription doubleBilled doubleBilledProviders subscriptions { id groupId state partner isEntitled source { sourceProvider sourceType subType sourceRef } product { id sku name entitlements { id name partner } bundle subscriptionPeriod earlyAccess trial { duration } categoryCodes } stacking { status overlappingSubscriptionProviders previouslyStacked previouslyStackedByProvider } term { purchaseDate startDate expiryDate nextRenewalDate pausedDate churnedDate isFreeTrial } } } consent { id idType token } }""" +UPDATE_DEVICE = """mutation updateDeviceOperatingSystem($updateDeviceOperatingSystem: UpdateDeviceOperatingSystemInput!) {updateDeviceOperatingSystem(updateDeviceOperatingSystem: $updateDeviceOperatingSystem) {accepted}}""" diff --git a/packages/envied/src/unshackle/__init__.py b/packages/envied/src/unshackle/__init__.py new file mode 100644 index 0000000..d99c9de --- /dev/null +++ b/packages/envied/src/unshackle/__init__.py @@ -0,0 +1,5 @@ +""" +Compatibility shim for precompiled services expecting `unshackle`. +""" + +from envied import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/__init__.py b/packages/envied/src/unshackle/core/__init__.py new file mode 100644 index 0000000..52d2cbd --- /dev/null +++ b/packages/envied/src/unshackle/core/__init__.py @@ -0,0 +1 @@ +from envied.core import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/cacher.py b/packages/envied/src/unshackle/core/cacher.py new file mode 100644 index 0000000..f8df988 --- /dev/null +++ b/packages/envied/src/unshackle/core/cacher.py @@ -0,0 +1 @@ +from envied.core.cacher import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/cdm/__init__.py b/packages/envied/src/unshackle/core/cdm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/envied/src/unshackle/core/cdm/decrypt_labs_remote_cdm.py b/packages/envied/src/unshackle/core/cdm/decrypt_labs_remote_cdm.py new file mode 100644 index 0000000..eb3330e --- /dev/null +++ b/packages/envied/src/unshackle/core/cdm/decrypt_labs_remote_cdm.py @@ -0,0 +1 @@ +from envied.core.cdm.custom_remote_cdm import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/cdm/detect.py b/packages/envied/src/unshackle/core/cdm/detect.py new file mode 100644 index 0000000..38cd678 --- /dev/null +++ b/packages/envied/src/unshackle/core/cdm/detect.py @@ -0,0 +1 @@ +from envied.core.cdm.detect import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/constants.py b/packages/envied/src/unshackle/core/constants.py new file mode 100644 index 0000000..11c90bb --- /dev/null +++ b/packages/envied/src/unshackle/core/constants.py @@ -0,0 +1 @@ +from envied.core.constants import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/credential.py b/packages/envied/src/unshackle/core/credential.py new file mode 100644 index 0000000..654c9dd --- /dev/null +++ b/packages/envied/src/unshackle/core/credential.py @@ -0,0 +1 @@ +from envied.core.credential import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/manifests/__init__.py b/packages/envied/src/unshackle/core/manifests/__init__.py new file mode 100644 index 0000000..f5f9d63 --- /dev/null +++ b/packages/envied/src/unshackle/core/manifests/__init__.py @@ -0,0 +1 @@ +from envied.core.manifests import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/search_result.py b/packages/envied/src/unshackle/core/search_result.py new file mode 100644 index 0000000..ee824c7 --- /dev/null +++ b/packages/envied/src/unshackle/core/search_result.py @@ -0,0 +1 @@ +from envied.core.search_result import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/service.py b/packages/envied/src/unshackle/core/service.py new file mode 100644 index 0000000..b4d95c8 --- /dev/null +++ b/packages/envied/src/unshackle/core/service.py @@ -0,0 +1 @@ +from envied.core.service import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/titles/__init__.py b/packages/envied/src/unshackle/core/titles/__init__.py new file mode 100644 index 0000000..34c9157 --- /dev/null +++ b/packages/envied/src/unshackle/core/titles/__init__.py @@ -0,0 +1 @@ +from envied.core.titles import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/tracks/__init__.py b/packages/envied/src/unshackle/core/tracks/__init__.py new file mode 100644 index 0000000..5b0f083 --- /dev/null +++ b/packages/envied/src/unshackle/core/tracks/__init__.py @@ -0,0 +1 @@ +from envied.core.tracks import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/utils/__init__.py b/packages/envied/src/unshackle/core/utils/__init__.py new file mode 100644 index 0000000..f470ae1 --- /dev/null +++ b/packages/envied/src/unshackle/core/utils/__init__.py @@ -0,0 +1 @@ +from envied.core.utils import * # noqa: F401,F403 diff --git a/packages/envied/src/unshackle/core/utils/collections.py b/packages/envied/src/unshackle/core/utils/collections.py new file mode 100644 index 0000000..b70001a --- /dev/null +++ b/packages/envied/src/unshackle/core/utils/collections.py @@ -0,0 +1 @@ +from envied.core.utils.collections import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/envied/src/unshackle/core/utils/ip_info.py b/packages/envied/src/unshackle/core/utils/ip_info.py new file mode 100644 index 0000000..8247c48 --- /dev/null +++ b/packages/envied/src/unshackle/core/utils/ip_info.py @@ -0,0 +1 @@ +from envied.core.utils.ip_info import * # noqa: F401,F403 \ No newline at end of file diff --git a/packages/vinefeeder/pyproject.toml b/packages/vinefeeder/pyproject.toml index e32eb51..a929a25 100644 --- a/packages/vinefeeder/pyproject.toml +++ b/packages/vinefeeder/pyproject.toml @@ -3,7 +3,7 @@ name = "vinefeeder" version = "0.2.0" description = "GUI front-end for envied" readme = "README.md" -requires-python = ">=3.10,<=3.13" +requires-python = ">=3.10,<=3.14" dependencies = [ "envied", # resolved via [tool.uv.sources] at the workspace root diff --git a/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py b/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py index f496adc..c9685ac 100644 --- a/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py @@ -182,7 +182,7 @@ class PlexLoader(BaseLoader): ) return None - else: + else: # series - fetch episodes try: headers = { "referer": "https://www.plex.tv",