Here are some fixes for iTunes #2

Open
opened 2026-01-16 22:20:30 +00:00 by alfonso219 · 0 comments

Since you've uploaded VT.DRM.LAB.3.0 as a .ZIP file, I can't really do a proper pull request, so this is the easiest way to do it:

When double-clicking "download.iTunes.bat" you are met with this error:

Failed to get iTunes' WEB TV App Environment Configuration...

In order to fix that, you have to go into "itunes.py" and change these lines:

    def configure(self):
        #if not re.match(r"https?://(?:geo\.)?itunes\.apple\.com/", self.title):
         #   raise ValueError("Url must be an iTunes URL...")
        if not self.storefront:
            cc = self.session.cookies.get_dict()["itua"]
            r = self.session.get("https://gist.githubusercontent.com/BrychanOdlum/2208578ba151d1d7c4edeeda15b4e9b1/raw/8f01e4a4cb02cf97a48aba4665286b0e8de14b8e/storefrontmappings.json").json()
            for g in r:
                if g['code'] == cc:
                    store_front = g['storefrontId']
                    self.storefront = store_front

        environment = self.get_environment_config()
        if not environment:
            raise self.log.exit("Failed to get iTunes' WEB TV App Environment Configuration...")
        try:
            self.session.headers.update({
                "User-Agent": self.config["user_agent"],
                "Authorization": f"Bearer {environment['MEDIA_API']['token']}",
                "media-user-token": self.session.cookies.get_dict()["media-user-token"],
                "x-apple-music-user-token": self.session.cookies.get_dict()["media-user-token"]
            })
        except KeyError:
            raise self.log.exit(" - No media-user-token cookie found, cannot log in.")




    def get_environment_config(self):
        """Loads environment config data from WEB App's <meta> tag."""
        res = self.session.get("https://tv.apple.com").text
        env = re.search(r'web-tv-app/config/environment"[\s\S]*?content="([^"]+)', res)
        if not env:
            return None
        return json.loads(unquote(env[1]))

To this:

    def configure(self):
        cc = self.session.cookies.get_dict()["itua"]
        r = self.session.get(
            "https://gist.githubusercontent.com/BrychanOdlum/2208578ba151d1d7c4edeeda15b4e9b1/raw/8f01e4a4cb02cf97a48aba4665286b0e8de14b8e/storefrontmappings.json"
        ).json()
        for g in r:
            if g["code"] == cc:
                self.storefront = g["storefrontId"]

        environment = self.get_environment_config()
        if not environment:
            raise ValueError(
                "Failed to get AppleTV+ WEB TV App Environment Configuration..."
            )
        self.session.headers.update(
            {
                "User-Agent": self.config["user_agent"],
                "Authorization": f"Bearer {environment['developerToken']}",
                "media-user-token": self.session.cookies.get_dict()["media-user-token"],
                "x-apple-music-user-token": self.session.cookies.get_dict()[
                    "media-user-token"
                ],
            }
        )

    def get_environment_config(self):
        """Loads environment config data from WEB App's serialized server data."""
        res = self.session.get("https://tv.apple.com").text

        script_match = re.search(
            r'<script[^>]*id=["\']serialized-server-data["\'][^>]*>(.*?)</script>',
            res,
            re.DOTALL,
        )
        if script_match:
            try:
                script_content = script_match.group(1).strip()
                data = json.loads(script_content)
                if (
                    data
                    and len(data) > 0
                    and "data" in data[0]
                    and "configureParams" in data[0]["data"]
                ):
                    return data[0]["data"]["configureParams"]
            except (json.JSONDecodeError, KeyError, IndexError) as e:
                print(f"Failed to parse serialized server data: {e}")

        return None

But that only solves one problem, because now you're met with these errors:

ERROR: Failed to get KEY, ignore.: One or more errors occurred. (The 'skd' scheme is not supported.)
ERROR: Failed to get KEY, ignore.: One or more errors occurred. (The 'data' scheme is not supported.)

In order to fix this issue you have to go into "tracks.py" and change this line:

if self.descriptor == self.Descriptor.M3U and not self.source in ["iT", "ATVP"]:

To this:

if self.descriptor == self.Descriptor.M3U and not self.source in ["ATVP"]:

But then you are met with yet another error, which is this:

ValueError: N_m3u8DL code does not yet support multiple uri (e.g. segmented) downloads.

Which can be fixed by going back into "tracks.py" and changing this line:

elif self.source in ["iT", "ATVP"]:

To this:

elif self.source in ["ATVP"]:

Now everything should work as intended and the video will download successfully.
I hope this is helpful for others who might've encountered the same issue as I did.

EDIT: In the "itunes.py" file there is one line that seems to have been missed from an earlier change:

'locale': 'pt-BR'

That line can be changed to:

'locale': 'en-US'

So that it matches with the two other "locale" lines.

Since you've uploaded VT.DRM.LAB.3.0 as a .ZIP file, I can't really do a proper pull request, so this is the easiest way to do it: When double-clicking "download.iTunes.bat" you are met with this error: **Failed to get iTunes' WEB TV App Environment Configuration...** In order to fix that, you have to go into "itunes.py" and change these lines: ``` def configure(self): #if not re.match(r"https?://(?:geo\.)?itunes\.apple\.com/", self.title): # raise ValueError("Url must be an iTunes URL...") if not self.storefront: cc = self.session.cookies.get_dict()["itua"] r = self.session.get("https://gist.githubusercontent.com/BrychanOdlum/2208578ba151d1d7c4edeeda15b4e9b1/raw/8f01e4a4cb02cf97a48aba4665286b0e8de14b8e/storefrontmappings.json").json() for g in r: if g['code'] == cc: store_front = g['storefrontId'] self.storefront = store_front environment = self.get_environment_config() if not environment: raise self.log.exit("Failed to get iTunes' WEB TV App Environment Configuration...") try: self.session.headers.update({ "User-Agent": self.config["user_agent"], "Authorization": f"Bearer {environment['MEDIA_API']['token']}", "media-user-token": self.session.cookies.get_dict()["media-user-token"], "x-apple-music-user-token": self.session.cookies.get_dict()["media-user-token"] }) except KeyError: raise self.log.exit(" - No media-user-token cookie found, cannot log in.") def get_environment_config(self): """Loads environment config data from WEB App's <meta> tag.""" res = self.session.get("https://tv.apple.com").text env = re.search(r'web-tv-app/config/environment"[\s\S]*?content="([^"]+)', res) if not env: return None return json.loads(unquote(env[1])) ``` To this: ``` def configure(self): cc = self.session.cookies.get_dict()["itua"] r = self.session.get( "https://gist.githubusercontent.com/BrychanOdlum/2208578ba151d1d7c4edeeda15b4e9b1/raw/8f01e4a4cb02cf97a48aba4665286b0e8de14b8e/storefrontmappings.json" ).json() for g in r: if g["code"] == cc: self.storefront = g["storefrontId"] environment = self.get_environment_config() if not environment: raise ValueError( "Failed to get AppleTV+ WEB TV App Environment Configuration..." ) self.session.headers.update( { "User-Agent": self.config["user_agent"], "Authorization": f"Bearer {environment['developerToken']}", "media-user-token": self.session.cookies.get_dict()["media-user-token"], "x-apple-music-user-token": self.session.cookies.get_dict()[ "media-user-token" ], } ) def get_environment_config(self): """Loads environment config data from WEB App's serialized server data.""" res = self.session.get("https://tv.apple.com").text script_match = re.search( r'<script[^>]*id=["\']serialized-server-data["\'][^>]*>(.*?)</script>', res, re.DOTALL, ) if script_match: try: script_content = script_match.group(1).strip() data = json.loads(script_content) if ( data and len(data) > 0 and "data" in data[0] and "configureParams" in data[0]["data"] ): return data[0]["data"]["configureParams"] except (json.JSONDecodeError, KeyError, IndexError) as e: print(f"Failed to parse serialized server data: {e}") return None ``` But that only solves _one_ problem, because now you're met with these errors: **ERROR: Failed to get KEY, ignore.: One or more errors occurred. (The 'skd' scheme is not supported.) ERROR: Failed to get KEY, ignore.: One or more errors occurred. (The 'data' scheme is not supported.)** In order to fix this issue you have to go into "tracks.py" and change this line: ` if self.descriptor == self.Descriptor.M3U and not self.source in ["iT", "ATVP"]:` To this: ` if self.descriptor == self.Descriptor.M3U and not self.source in ["ATVP"]:` But then you are met with yet another error, which is this: **ValueError: N_m3u8DL code does not yet support multiple uri (e.g. segmented) downloads.** Which can be fixed by going back into "tracks.py" and changing this line: ` elif self.source in ["iT", "ATVP"]:` To this: ` elif self.source in ["ATVP"]:` Now everything should work as intended and the video will download successfully. I hope this is helpful for others who might've encountered the same issue as I did. **EDIT:** In the "itunes.py" file there is one line that seems to have been missed from an earlier change: ` 'locale': 'pt-BR'` That line can be changed to: ` 'locale': 'en-US'` So that it matches with the two other "locale" lines.
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Mike/Vinetrimmer#2
No description provided.