I was downloading matches from the FIFA+ Archive website using (N_m3u8DL-RE), and I was helped with that by (2nHxWW6GkN1l916N3ayz8HQoi).
During this process, he discovered that the FIFA website has two versions of each match,
a normal version with the FIFA logo,
and another hidden version without the logo
[Attachment 90880 - Click to enlarge]
He created a script file for me to run using Python that allowed me to download the hidden files without the logo
However, after changing the file source from the FIFA website to the FIFA+ Archive website, the script for the hidden version no longer works as it used to.Code:import base64 import json import os import re from os.path import join from urllib.parse import quote import isodate import requests import xmltodict from pssh_box import _parse_boxes, widevine_pssh_data_pb2, _generate_widevine_data from pywidevine import PSSH, Device, Cdm from slugify import slugify USE_MPD = True INPUT_FILE = "input.txt" WVD_FILE = "cdm.wvd" INTRO_DURATION = 5 TEMP_MPD_FOLDER = "temp_mpd" DEFAULT_LICENSE_URL = 'https://content-aeui1.uplynk.com/wv' VIDEO_PLAYER_URL = 'https://cxm-api.fifa.com/fifaplusweb/api/videoPlayerData/{video_id}' PREPLAY_URL = 'https://content.uplynk.com/preplay/{content_id}/multiple.json' def get_keys(license_url, pssh): if license_url is None: license_url = DEFAULT_LICENSE_URL pssh = PSSH(pssh) device = Device.load(WVD_FILE) cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) response = requests.post(license_url, data=challenge) response.raise_for_status() cdm.parse_license(session_id, response.content) keys = [] for key in cdm.get_keys(session_id): if "CONTENT" not in key.type: continue keys.append(f'{key.kid.hex}:{key.key.hex()}') cdm.close(session_id) return keys def get_mpd_command(manifest, video_title): manifest = manifest.replace(".m3u8?", ".mpd?") response = requests.get(manifest).content.decode() response = xmltodict.parse(response) kids_psshs = [] periods = response["MPD"].get("Period", []) if type(periods) is not list: periods = [periods] filtered_periods = [] for period in periods: if period.get("@duration", None) is None: continue duration = isodate.parse_duration(period["@duration"]) duration = duration.total_seconds() if duration < INTRO_DURATION: continue filtered_periods.append(period) adaptations = period.get("AdaptationSet", []) if type(adaptations) is not list: adaptations = [adaptations] for adaptation in adaptations: protections = adaptation.get("ContentProtection", []) if type(protections) is not list: protections = [protections] current_kid, current_pssh, license_url = None, None, None for protection in protections: if protection.get("@cenc:default_KID", None) is not None: current_kid = protection["@cenc:default_KID"] if PSSH.SystemId.Widevine.__str__() in protection.get("@schemeIdUri", ""): current_pssh = protection["cenc:pssh"]["#text"] license_url = protection.get("ms:laurl", {}).get("@licenseUrl", None) if current_kid is not None and current_pssh is not None: break kids_psshs.append((current_kid.replace("-", ""), current_pssh, license_url)) content_id_dict = {} pssh_data_dict = {} for k, p, l in kids_psshs: pssh_box = _parse_boxes(base64.b64decode(p))[0] pssh_data = widevine_pssh_data_pb2.WidevinePsshData() pssh_data.ParseFromString(pssh_box.pssh_data) if not pssh_data.HasField('content_id'): continue content_id = str(base64.b16encode(pssh_data.content_id).decode()) provider = None if pssh_data.HasField('provider'): provider = pssh_data.provider protection_scheme = None if pssh_data.HasField('protection_scheme'): protection_scheme = pssh_data.protection_scheme content_id_dict[content_id] = content_id_dict.get(content_id, []) + [(k, l)] if pssh_data_dict.get(content_id, None) is None: pssh_data_dict[content_id] = (provider, protection_scheme, pssh_data.content_id) merged_psshs = [] for cid, kid_license in content_id_dict.items(): kids = [kid.encode() for kid, _ in kid_license] provider, protection_scheme, raw_cid = pssh_data_dict[cid] pssh_data = _generate_widevine_data( kids, content_id=raw_cid, provider=provider, protection_scheme=protection_scheme ) merged_pssh = PSSH.new(init_data=pssh_data, system_id=PSSH.SystemId.Widevine) merged_psshs.append((merged_pssh.__str__(), kid_license[0][1])) keys = [] for pssh, license_url in merged_psshs: keys += get_keys(license_url, pssh) assert len(keys) > 0 keys = ' '.join([f'--key {k}' for k in keys]) response["MPD"]["Period"] = filtered_periods response = xmltodict.unparse(response, pretty=True) output_path = join(TEMP_MPD_FOLDER, f"{video_title}.mpd") with open(output_path, "w") as f: f.write(response) return ( f'N_m3u8DL-RE "{output_path}" {keys} --save-dir "fifa_com" --save-name "{video_title}" -M format=mkv' f' & ' f'del "{output_path}"' ) def get_command(url): video_id = re.search(r"/watch/([^/?]*)", url).group(1) response = requests.get( VIDEO_PLAYER_URL.format(video_id=video_id), params={'locale': 'en'} ) status_code = response.status_code if status_code == 404: raise Exception("Content not available") response = json.loads(response.content.decode()) video_title = slugify(response.get('title', video_id)) response = response["preplayParameters"] preplay_url = PREPLAY_URL.format(content_id=response["contentId"]) if response.get("queryStr", None) is not None: preplay_url += "?" + response["queryStr"] if response.get("signature", None) is not None: if "?" not in preplay_url: preplay_url += "?" else: preplay_url += "&" preplay_url += "sig=" + quote(response["signature"]) response = json.loads(requests.get(preplay_url).content.decode())["playURL"] if USE_MPD: return get_mpd_command(response, video_title) return f'N_m3u8DL-RE "{response}" --save-dir "fifa_com" --save-name "{video_title}"' if __name__ == '__main__': if not os.path.exists(TEMP_MPD_FOLDER): os.makedirs(TEMP_MPD_FOLDER) with open(INPUT_FILE, 'r') as file: urls = file.readlines() urls = [u.strip() for u in urls] urls = list(dict.fromkeys([u for u in urls if len(u) > 0 and "plus.fifa.com" not in u])) index = 0 nr = len(urls) for u in urls: index += 1 print(f'[{index}/{nr}]') try: print(get_command(u)) except Exception as e: print(f"Failed to get: {u}. Reason: {str(e)}. Try again later...")
This is the link to the match whose images are attached.
and there are more details in these two topicsCode:https://www.plus.fifa.com/en/content/spain-v-nigeria-group-d-1998-fifa-world-cup-france-full-match-replay/277d9e39-29c4-47ad-9511-4d9ceffa699e?gl=sa
Code:https://forum.videohelp.com/threads/415550-Any-Help-with-error-Download-first-segment-failed%21I hope someone can help me write another script that can download the hidden version of the files.Code:https://forum.videohelp.com/threads/414899-Regarding-downloading-using-%28N_m3u8DL-RE%29
+ Reply to Thread
Results 1 to 1 of 1
Similar Threads
-
Video downloading from FIFA+
By VersaceLaurent in forum Video Streaming DownloadingReplies: 96Last Post: 2nd May 2026, 03:12 -
Please help with downloading matches from FIFA+
By Dernhelm in forum Newbie / General discussionsReplies: 0Last Post: 21st Jul 2025, 21:59 -
Help downloading from Fifa Plus please
By Luv4oneanother in forum Newbie / General discussionsReplies: 3Last Post: 26th Jun 2025, 05:56 -
Problem downloading from FIFA+
By khaledgamil in forum Video Streaming DownloadingReplies: 9Last Post: 9th Jun 2024, 08:22 -
Downloading a match from FIFA+
By alextt in forum Video Streaming DownloadingReplies: 8Last Post: 6th Jun 2024, 03:38


Quote