Here are some fixes for iTunes #2

Closed
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"]
        try:
            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"]
        except Exception as e:
            self.storefront = "143441-19,29"

        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)
                candidates = None
                if isinstance(data, list):
                    candidates = data
                elif isinstance(data, dict):
                    if isinstance(data.get("data"), list):
                        candidates = data["data"]
                    else:
                        candidates = [data]

                if candidates:
                    for item in candidates:
                        if not isinstance(item, dict):
                            continue
                        cfg = (item.get("data") or {}).get("configureParams")
                        if isinstance(cfg, dict) and cfg:
                            return cfg
            except (json.JSONDecodeError, KeyError, IndexError, TypeError, ValueError) as e:
                self.log.debug(f"Failed to parse serialized server data: {e}")

        return None

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"] try: 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"] except Exception as e: self.storefront = "143441-19,29" 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) candidates = None if isinstance(data, list): candidates = data elif isinstance(data, dict): if isinstance(data.get("data"), list): candidates = data["data"] else: candidates = [data] if candidates: for item in candidates: if not isinstance(item, dict): continue cfg = (item.get("data") or {}).get("configureParams") if isinstance(cfg, dict) and cfg: return cfg except (json.JSONDecodeError, KeyError, IndexError, TypeError, ValueError) as e: self.log.debug(f"Failed to parse serialized server data: {e}") return None ``` 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.