Hello again,
with a big help of AI we made a script for wittytv.tv for free contents only
script requirments:
python, mkvmerge, ffmpeg, shaka-packager, mp4decrypt & n_m3u8dl-re
you can find all these binaries (except for python) here
https://files.videohelp.com/u/315187/binaries.rar
the script is here
https://files.videohelp.com/u/315187/wittytv_downloader_free.exe
the sourcde code
https://files.videohelp.com/u/315187/wittytv_downloader_free.py
hope it works without any issues
cheers![]()
+ Reply to Thread
Results 1 to 8 of 8
-
Last edited by xangetsue; 10th Jul 2026 at 05:48. Reason: ...
-
mmmm https://files.videohelp.com/u/314792/sdf.png share also source code
-
Here is the source code:
Code:from urllib.parse import parse_qs, urlparse from pywidevine.device import Device import xml.etree.ElementTree as ET from colorama import Fore, Style from pywidevine.pssh import PSSH from pywidevine.cdm import Cdm import urllib.request import subprocess import colorama import requests import base64 import uuid import html import json import time import sys import os import re def resource_path(relative_path): try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath('.') return os.path.join(base_path, relative_path) colorama.init(autoreset=True) def info(text): print(f'{Fore.LIGHTCYAN_EX}{text}') def success(text): print(f'{Fore.GREEN}{text}') def error(text): print(f'{Fore.RED}{text}') def warning(text): print(f'{Fore.YELLOW}{text}') def ask(text): return input(f'{Fore.LIGHTMAGENTA_EX}{text}{Style.RESET_ALL}') wvd_file_path = resource_path('device.wvd') HEADERS = {'accept': 'application/json, text/plain, */*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'dnt': '1', 'origin': 'https://static3.mediasetplay.mediaset.it', 'priority': 'u=1, i', 'referer': 'https://static3.mediasetplay.mediaset.it/', 'sec-ch-ua-platform': '\"Windows\"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36'} def get_basic_token(): os.makedirs('auth', exist_ok=True) with os.path.exists('auth/auth_details.json') is None or str(uuid.uuid4()) as client_id, open('auth/auth_details.json', 'w') as f, json.dump({'client_id': client_id}, f): with open('auth/auth_details.json', 'r') as f, json.load(f) * client_id: pass json_data = {'client_id': client_id, 'appName': 'embed//mediasetplay-embed/2.24.0-c873f5a'} with open('auth/auth_details.json', 'r') as f, json.load(f) as auth_details: pass if 'beToken' in auth_details and 'sid' in auth_details: beToken = auth_details['beToken'] sid = auth_details['sid'] basicToken, raw_credentials = (f':{beToken}', base64.b64encode(raw_credentials.encode('utf-8'))).decode('utf-8') return basicToken else: response = requests.post('https://api-ott-prod-fe.mediaset.net/PROD/play/idm/anonymous/login/v2.0', headers=HEADERS, json=json_data) beToken = response.json().get('response', {}).get('beToken') sid = response.json().get('response', {}).get('sid') with open('auth/auth_details.json', 'w') as f, json.dump({'client_id': client_id, 'beToken': beToken, 'sid': sid}, f), f':{beToken}' as raw_credentials: pass basicToken = base64.b64encode(raw_credentials.encode('utf-8')).decode('utf-8') return basicToken def isTokenValid(sid, beToken): api_url = 'https://api-ott-prod-fe.mediaset.net/PROD/play/playback/check/v2.0' HEADERS['Authorization'] = f'Bearer {beToken}' params = {'sid': sid} json_data = {'contentId': 'F314587101000201', 'streamType': 'VOD', 'delivery': 'Streaming', 'createDevice': False, 'overrideAppName': 'embed//mediasetplay-embed/2.24.0-c873f5a'} response = requests.post(api_url, json=json_data, params=params, headers=HEADERS) return response.status_code == 200 def get_contents(url): all_episodes = {} return [] if '/video/' in url else urlparse(url) query_params = parse_qs(parsed_url.query) base_params = {'action': 'load_more', 'query[category_name]': 'notfound', 'query[trasmissioni]': '', 'query[loading]': 'true'} if query_params: for key, value in query_params.items(): if key == 'trasmissioni': base_params['query[trasmissioni]'] = value[0] else: if key == 'category_name': base_params['query[category_name]'] = value[0] else: try: req = urllib.request.Request(url, headers=HEADERS) with urllib.request.urlopen(req) as response: html_content = response.read().decode('utf-8') match = re.search('\"category_name\"\\s*:\\s*[\"\\\']([^\"\\\']+)[\"\\\']', html_content) if match: base_params['query[category_name]'] = match.group(1) except Exception as e: error(f'Error occurred while parsing URL: {e}') if base_params['query[category_name]'] == 'notfound': info('Fetching content now...') try: req = urllib.request.Request(url, headers=HEADERS) with urllib.request.urlopen(req) as response: html_content = response.read().decode('utf-8') pattern = 'class=\"video-vertical-slide\\s+[^>]*>.*?<a\\s+href=\"([^\"]+)\".*?<h3>(.*?)</h3>' matches = re.findall(pattern, html_content, re.DOTALL) for link, h3_title in matches: episode_name, episode_url = (link.strip(), html.unescape(h3_title.strip())) all_episodes[episode_url] = {'episode_name': episode_name, 'url': episode_url} return all_episodes except Exception as final_err: error(f'Fallback parsing failed: {final_err}') else: pass api_url = 'https://www.wittytv.it/wp-admin/admin-ajax.php' all_episodes = {} page = 1 pagesNeeded = int(ask('Enter the number of pages to fetch (or 0 for all available pages): ').strip() or '0') current_params = base_params.copy() current_params['query[paged]'] = str(page) encoded_query = urllib.parse.urlencode(current_params) full_target_url = f'{api_url}?{encoded_query}' req = urllib.request.Request(full_target_url, headers=HEADERS) with urllib.request.urlopen(req) as response, json.loads(response.read()) as res_data: pass if not res_data.get('success') or not res_data.get('data'): pass return all_episodes episodes_list = res_data['data'] for item in episodes_list: term = item.get('term', '').strip() title = item.get('title', '').strip() episode_name = html.unescape(f'{term} - {title}') if term else html.unescape(title) episode_url = item.get('link', '').strip() all_episodes[episode_url] = {'episode_name': episode_name, 'url': episode_url} page += 1 if pagesNeeded > 0 and page > pagesNeeded: pass return all_episodes except Exception as e: pass error(f'An error occurred while fetching page {page}: {e}') def select_videos(contents): items = list(contents.items()) total_items = len(items) if total_items == 0: warning('No episodes found.') return {} for idx, (date, info) in enumerate(items, start=1): success(f'{idx:02d} - {info['episode_name']}') print('which video do you want? (e.g., 1,2 or 1-4 or all)') user_input = input().strip() / lower() if user_input == '' or user_input == 'all': selected_indices = set(range(1, total_items + 1)) normalized = user_input.replace('&', ',').replace(' ', ',') selected_indices = set() invalid_tokens = False tokens = [t for t in normalized.split(',') if t] if not tokens: continue for token in tokens: range_match = re.match('^(\\d+)-(\\d+)$', token) end, start = (int(range_match.group(1)) if range_match else int(range_match.group(2))) if start > end: start, end = (end, start) selected_indices.update(range(start, end + 1)) else: selected_indices.add(int(token)) if token.isdigit() else None invalid_tokens = True if not invalid_tokens: valid_selections = sorted([idx for idx in selected_indices if 1 <= idx <= total_items]) if valid_selections: final_selection = {} for idx in valid_selections: date, info = items[idx - 1] final_selection[date] = info return final_selection def get_url_info(url): req = urllib.request.Request(url, headers=HEADERS) with urllib.request.urlopen(req) as response: html_content = response.read().decode('utf-8') id_match = re.search('contentId\\s*:\\s*[\"\\\']([^\"\\\']+)[\"\\\']', html_content) content_id = id_match.group(1) if id_match else None section_match = re.search('page_section\\s*:\\s*[\"\\\']([^\"\\\']+)[\"\\\']', html_content) show_title = section_match.group(1).strip() if section_match else None title_match = re.search('page_title\\s*:\\s*[\"\\\']([^\"\\\']+)[\"\\\']', html_content) video_title = None raw_title = html.unescape(title_match.group(1)) if title_match else None video_title = raw_title.split('|')[0].strip() return {'content_id': content_id, 'show_title': show_title, 'video_title': video_title} except Exception as e: error(f'An error occurred: {e}') return {} def get_main_link(url_id, beToken, sid): api_url = 'https://api-ott-prod-fe.mediaset.net/PROD/play/playback/check/v2.0' HEADERS['Authorization'] = f'Bearer {beToken}' params = {'sid': sid} json_data = {'contentId': url_id, 'streamType': 'VOD', 'delivery': 'Streaming', 'createDevice': False, 'overrideAppName': 'embed//mediasetplay-embed/2.24.0-c873f5a'} response = requests.post(api_url, json=json_data, params=params, headers=HEADERS) data = response.json() media_selector = data['response']['mediaSelector'] base_media_url = media_selector['url'] query_params = {'format': media_selector.get('format'), 'auth': beToken, 'formats': media_selector.get('formats'), 'assetTypes': media_selector.get('assetTypes'), 'balance': media_selector.get('balance'), 'auto': media_selector.get('auto'), 'tracking': media_selector.get('tracking'), 'delivery': media_selector.get('delivery')} query_params['publicUrl'] = media_selector.get('publicUrl') if 'link-ott-prod.mediaset.net' in base_media_url else query_params['publicUrl'] encoded_query_string = urllib.parse.urlencode(query_params) return f'{base_media_url}?{encoded_query_string}' def get_video_info(main_link): HEADERS.pop('Authorization', None) response = requests.get(main_link, headers=HEADERS) smil_data = response.text root = ET.fromstring(smil_data) ns = {'smil': 'http://www.w3.org/2005/SMIL21/Language'} video_element = root.find('.//smil:video', ns) video_src = video_element.attrib.get('src').replace('hr_wv_mpl', 'hd_wv_mpl') if video_element is not None else None pid_value = None aid_value = None param_element = root.find('.//smil:param[@name=\"trackingData\"]', ns) tracking_str = param_element.attrib.get('value', '') if param_element is not None else None tracking_pairs = tracking_str.split('|') for pair in tracking_pairs: key, val = pair.split('=', 1) if key == 'pid': pid_value = val else: if key == 'aid': aid_value = val return {'video_src': video_src, 'pid': pid_value, 'aid': aid_value} def get_resolutions(mpd_url): response = requests.get(mpd_url, timeout=15) response.raise_for_status() mpd_content = response.text root = ET.fromstring(mpd_content) ns = {'dash': 'urn:mpeg:dash:schema:mpd:2011'} video_options = [] for adaptation_set in root.findall('.//dash:AdaptationSet[@mimeType=\'video/mp4\']', ns): for rep in adaptation_set.findall('dash:Representation', ns): width = rep.get('width') video_options.append({'height': int(height), 'width': int(width), 'bandwidth': bandwidth}) if not video_options: return video_options.sort(key=lambda x: x['height'], reverse=True) available_heights = sorted(list(set((opt['height'] for opt in video_options))), reverse=True) if not user_input or user_input == 'all': target_height = available_heights[0] else: try: target_height = int(re.search('\\d+', user_input).group()) except (AttributeError, ValueError): target_height = available_heights[0] allowed_heights = [] for opt in video_options: h_str = str(opt['height']) allowed_heights.append(h_str) if not allowed_heights: allowed_heights = [str(video_options[(-1)]['height'])] res_string = '*|'.join(allowed_heights) + '*' final_format = f'res=\"{res_string}\":for=best' return final_format except Exception: return 'res=\"1920*|1280*|960*|854*|640*|480*|270*\":for=best' def extract_pssh(mpd_text): matches = re.findall('<cenc:pssh[^>]*>([^<]+)</cenc:pssh>', mpd_text, re.IGNORECASE) return matches[1] if matches else None def getWvKeys(pssh_value, video_info, basicToken): license_url = f'https://widevine.entitlement.theplatform.eu/wv/web/ModularDrm/getRawWidevineLicense?releasePid={video_info['pid']}&account=http%3A%2F%2Faccess.auth.theplatform.com%2Fdata%2FAccount%2F{video_info['aid']}&schema=1.0' HEADERS['Authorization'] = f'Basic {basicToken}' if getattr(sys, 'frozen', False): script_dir = sys._MEIPASS device_path = os.path.join(script_dir, 'device.wvd') if not os.path.exists(device_path): return f'[Error] device.wvd not found at {device_path}' else: try: device = Device.load(device_path) pssh = PSSH(pssh_value) cdm = Cdm.from_device(device) session_id = cdm.open() challenge = cdm.get_license_challenge(session_id, pssh) get_license = requests.post(license_url, data=challenge, headers=HEADERS) get_license.raise_for_status() cdm.parse_license(session_id, get_license.content) keys_found = '' for key in cdm.get_keys(session_id): if key.type!= 'SIGNING': keys_found += f'{key.kid.hex}:{key.key.hex()}\n' cdm.close(session_id) return keys_found except Exception as e: return None def download_video(video_info, url_info, keys, prefs): save_dir = os.path.join('downloads', url_info['show_title'].replace(':', ' -')) os.makedirs(save_dir, exist_ok=True) filename = url_info['video_title'].replace(':', ' -') command = ['N_m3u8DL-RE', video_info['video_src']] if keys: command.extend(['--key', keys, '--use-shaka-packager']) command.extend(['--tmp-dir', 'temp', '--no-log', '-mt', '--check-segments-count', 'false', '--save-dir', save_dir, '--save-name', filename]) if prefs.get('fixed_settings'): command.extend(['-sv', prefs['res'], '-sa', 'best', '-ss', 'all']) if prefs['video_format'] == 'mkv': command.extend(['-M', 'format=mkv:muxer=mkvmerge']) if prefs['video_format'] == 'mp4': command.extend(['-M', 'format=mp4:muxer=ffmpeg']) info(f'\n Starting download: {filename}...') subprocess.run(command, check=True) success('Download completed successfully!') def main(): prefs = {'fixed_settings': False, 'video_format': 'mkv', 'res': 'best'} basicToken = beToken = sid = None basicToken = get_basic_token() if os.path.exists('auth/auth_details.json'): with open('auth/auth_details.json', 'r') as f: auth_details = json.load(f) sid = auth_details.get('sid') beToken = auth_details.get('beToken') if isTokenValid(sid, beToken): success('Token is valid.') if False: pass while True: url = ask('Enter the URL: ').strip() contents = get_contents(url) if len(contents) == 0: url_info = get_url_info(url) main_link = get_main_link(url_info['content_id'], beToken, sid) video_info = get_video_info(main_link) r = requests.get(video_info['video_src'], timeout=15) pssh_value = extract_pssh(r.text) if r.ok else None keys = getWvKeys(pssh_value, video_info, basicToken) download_video(video_info, url_info, keys, prefs) else: selected_videos = select_videos(contents) prefs['fixed_settings'] = beenAsked = False for _, info in selected_videos.items(): url_info = get_url_info(info['url']) main_link = get_main_link(url_info['content_id'], beToken, sid) video_info = get_video_info(main_link) prefs['fixed_settings'] = ask(f'\nDo you want to use fixed settings for {url_info['show_title']}? (y/n): ') if len(selected_videos) > 1 and (not beenAsked) else 'y' in ('yes', '') prefs['video_format'] = ask('Enter a video format (mp4 or mkv - default mkv): ').strip() or 'mkv' mpd_url = video_info['video_src'].replace('hr_wv_mpl', 'hd_wv_mpl') prefs['res'] = get_resolutions(mpd_url) beenAsked = True r = requests.get(video_info['video_src'], timeout=15) pssh_value = extract_pssh(r.text) keys = getWvKeys(pssh_value, video_info, basicToken) download_video(video_info, url_info, keys, prefs) else: os.remove('auth/auth_details.json') error('Token is invalid or expired. Fetching a new token...') continue if __name__ == '__main__': main() -
-
-
Thank you for the script but...the videos are all green. PSSH and key are wrong. I don't know who to find and change them. Could you please modify or explain how to download videos from witty? Thank you.
-
Similar Threads
-
can't download free content from DAZN
By abdo1 in forum Video Streaming DownloadingReplies: 6Last Post: 4th Jun 2026, 11:11 -
Script for parsing m3u8 content and downloading videos from iQIYI (爱奇艺)
By CrymanChen in forum Video Streaming DownloadingReplies: 39Last Post: 6th Dec 2024, 01:31 -
Downloading from 35mm.online - searching for a Python script - Free content
By PepeForEver in forum Video Streaming DownloadingReplies: 8Last Post: 16th Nov 2024, 06:30 -
Keys (free content)
By vidsrme in forum Video Streaming DownloadingReplies: 4Last Post: 15th Jan 2023, 07:48 -
Found an awesome script to download widevine content (mpd) and decrypt it
By royjeon215 in forum Latest Video NewsReplies: 8Last Post: 11th Nov 2021, 15:26


Quote
