Some scripts on this forum are unfortunately very slow, fragile, and unnecessarily complex. This is videohelp.com, not videoconfusion.com. The goal here is to provide the simplest, most efficient, and robust script to download content from itv.com without the need for downloading anything other than the essential components. No browser extensions, no clipboard dependencies, just paste your URLs and go.
To download content, simply copy URLs from the address bar
E.g.
Code:python itv.py https://www.itv.com/watch/love-island/2a3697/2a3697a0460
Setup
Get mp4decryptCode:sudo pacman -S mkvtoolnix-gui yay -S n-m3u8dl-re-bin pip install httpx selectolax
https://github.com/axiomatic-systems/Bento4#on-linux-and-other-platforms-using-cmake
Script
itv.py
Code:import sys import platform import subprocess from base64 import b64encode from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from httpx import URL, Client from selectolax.lexbor import LexborHTMLParser out = Path('out') out.mkdir(exist_ok=True,parents=True) def download(url: str) -> None: client = Client(headers={'authority':'www.itv.com','user-agent':'AppleWebKit/537.36'},timeout=20) title,data = get_data(client,url) video = data['Playlist']['Video'] media = video['MediaFiles'] mpd_url = [f'{video["Base"]}{y}' for x in media if (y:=URL(x['Href'])).path.endswith('.mpd')][0] lic_url = [x['KeyServiceUrl'] for x in media][0] pssh = get_pssh(client,mpd_url) key = get_key(client,pssh,lic_url) m3u8dl = 'N_m3u8DL-RE' if platform.system() in {'Windows','Darwin'} else 'n-m3u8dl-re' subprocess.run([ m3u8dl, mpd_url, '--append-url-params', '--header','cookie: hdntl=~data=hdntl~hmac=' + '.', # append any random byte '--header','host: itvpnpdotcom.blue.content.itv.com', '--header',f'user-agent: {client.headers["user-agent"]}', '--auto-select', '--save-name',title, '--save-dir','./out', '--tmp-dir','./', '-mt', '--key',key, '-M', 'format=mkv:muxer=mkvmerge', '--no-log', ]) def get_data(client: Client, url: str) -> tuple: html = LexborHTMLParser(client.get(url).text) title = html.css_first('title').text() magni_url = html.css_first('[data-video-id]').attributes.get('data-video-id') featureset = {k:('mpeg-dash','widevine','outband-webvtt','hd','single-track') for k in ('min','max')} payload = {'client':{'id':'browser'},'variantAvailability':{'featureset':featureset,'platformTag':'dotcom'}} return title, client.post(magni_url,json=payload).json() def get_key(client:Client, pssh:str, lic_url:str) -> str: payload = {'license':lic_url,'headers':'connection: keep-alive','pssh':pssh,'buildInfo':'','proxy':'','cache':True} return LexborHTMLParser(client.post('https://cdrm-project.com/wv',json=payload).text).css_first('li').text() def get_pssh(client:Client, mpd_url:str) -> str: version = '3870737368' # b'8pssh' system_id = 'EDEF8BA979D64ACEA3C827DCD51D21ED' data = '48E3DC959B06' key_id = (LexborHTMLParser(client.get(mpd_url).text).css_first('ContentProtection').attributes.get('cenc:default_kid').replace('-','')) s = f'000000{version}00000000{system_id}000000181210{key_id}{data}' return b64encode(bytes.fromhex(s)).decode() def main(): urls = sys.argv[1:] with ThreadPoolExecutor() as e: [f.result() for f in as_completed(e.submit(download,u) for u in urls)] if __name__ == "__main__": main()
Support our site by donate $5 directly to us Thanks!!!
Try StreamFab Downloader and download streaming video from Netflix, Amazon!
Try StreamFab Downloader and download streaming video from Netflix, Amazon!
+ Reply to Thread
Results 1 to 13 of 13
-
Last edited by akduyurye; 21st Jun 2023 at 09:20. Reason: Handle OSX
-
Much appreciated, thank you! Thanks also for updating it for Mac.
p.s. if you can post a similarly-easy Channel4 solution that would be brilliant too!Last edited by PhilipG; 20th Jun 2023 at 10:17.
-
You've used this phrase multiple times with multiple accounts by now. You must be real proud of it. It's a nice one.
Great script, though! I always like like seeing high-quality work. It's a nice learning opportunity for someone like myself. And this is a bit nicer way of presenting it than doing it while directly insulting other members at the same time like you did on your last account.
Looking forward to seeing more of your work. -
Other scripts are "unnecessarily complex" because they don't outsource the decryption/CDM part to an external 3rd party server. Appreciate the gesture here and all but it's not like this is some innovative new method, you're just doing the "complicated" part remotely. Sure it's more end-user friendly, but essentially you're putting a load on someone else's server and creating a dependency elsewhere.
This is like saying brewing beer is unnecessarily complex when it can also be found in the store.
Besides, claiming this uses "no CDM" is just false.
It does use a CDM, just not a local one.Last edited by mike94; 21st Jun 2023 at 02:46.
I cannot help via Telegram, MSN, ICQ - no unsolicited DMs please.
All knowledge can be obtained by reading the information on these forums. -
Completely agree dear. Although one could then argue about the terms "external" and "3rd party", as dumping android data with frida scripts and downloading/decrypting content with m3u8 and mp4decrypt is dependent on their current implementations. So there's "no avoiding dependencies" in general, again this can be argued. I understand your point though, and as for the unnecessary complexities, I'm specifically referring to the overuse of browser extensions, clipboard management, regex + magic number indices for data processing, etc.
There is no mention of innovation here, just "Fast". The only potential innovation here is my use of an arbitrary hmac in the cookie. -
This is very useful, I'd been using the 'magni' contents for itv, but getting the 'magni' URL automatically is something I'd not contemplated.
Interesting to know about that HMAC too. -
For you my dear: https://www.youtube.com/watch?v=sQiI5k8FS7A
-
I don't know when, but this stopped working
HTML Code:python3 itv.py https://www.itv.com/watch/love-island/2a3697/2a3697a0460 Traceback (most recent call last): File "/home/user/.local/lib/python3.10/site-packages/httpcore/_exceptions.py", line 10, in map_exceptions yield File "/home/user/.local/lib/python3.10/site-packages/httpcore/backends/sync.py", line 28, in read return self._sock.recv(max_bytes) File "/usr/lib/python3.10/ssl.py", line 1259, in recv return self.read(buflen) File "/usr/lib/python3.10/ssl.py", line 1132, in read return self._sslobj.read(len) TimeoutError: The read operation timed out During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/user/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 60, in map_httpcore_exceptions yield File "/home/user/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 104, in __iter__ for part in self._httpcore_stream: File "/home/user/.local/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 338, in __iter__ for part in self._stream: File "/home/user/.local/lib/python3.10/site-packages/httpcore/_sync/http11.py", line 315, in __iter__ raise exc File "/home/user/.local/lib/python3.10/site-packages/httpcore/_sync/http11.py", line 308, in __iter__ for chunk in self._connection._receive_response_body(**kwargs): File "/home/user/.local/lib/python3.10/site-packages/httpcore/_sync/http11.py", line 177, in _receive_response_body event = self._receive_event(timeout=timeout) File "/home/user/.local/lib/python3.10/site-packages/httpcore/_sync/http11.py", line 191, in _receive_event data = self._network_stream.read( File "/home/user/.local/lib/python3.10/site-packages/httpcore/backends/sync.py", line 26, in read with map_exceptions(exc_map): File "/usr/lib/python3.10/contextlib.py", line 153, in __exit__ self.gen.throw(typ, value, traceback) File "/home/user/.local/lib/python3.10/site-packages/httpcore/_exceptions.py", line 14, in map_exceptions raise to_exc(exc) httpcore.ReadTimeout: The read operation timed out The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/user/Documents/har/itv.py", line 73, in <module> main() File "/home/user/Documents/har/itv.py", line 69, in main with ThreadPoolExecutor() as e: [f.result() for f in as_completed(e.submit(download,u) for u in urls)] File "/home/user/Documents/har/itv.py", line 69, in <listcomp> with ThreadPoolExecutor() as e: [f.result() for f in as_completed(e.submit(download,u) for u in urls)] File "/usr/lib/python3.10/concurrent/futures/_base.py", line 451, in result return self.__get_result() File "/usr/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result raise self._exception File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/home/user/Documents/har/itv.py", line 17, in download title,data = get_data(client,url) File "/home/user/Documents/har/itv.py", line 45, in get_data html = LexborHTMLParser(client.get(url).text) File "/home/user/.local/lib/python3.10/site-packages/httpx/_client.py", line 1045, in get return self.request( File "/home/user/.local/lib/python3.10/site-packages/httpx/_client.py", line 821, in request return self.send(request, auth=auth, follow_redirects=follow_redirects) File "/home/user/.local/lib/python3.10/site-packages/httpx/_client.py", line 922, in send raise exc File "/home/user/.local/lib/python3.10/site-packages/httpx/_client.py", line 916, in send response.read() File "/home/user/.local/lib/python3.10/site-packages/httpx/_models.py", line 805, in read self._content = b"".join(self.iter_bytes()) File "/home/user/.local/lib/python3.10/site-packages/httpx/_models.py", line 823, in iter_bytes for raw_bytes in self.iter_raw(): File "/home/user/.local/lib/python3.10/site-packages/httpx/_models.py", line 881, in iter_raw for raw_stream_bytes in self.stream: File "/home/user/.local/lib/python3.10/site-packages/httpx/_client.py", line 124, in __iter__ for chunk in self._stream: File "/home/user/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 103, in __iter__ with map_httpcore_exceptions(): File "/usr/lib/python3.10/contextlib.py", line 153, in __exit__ self.gen.throw(typ, value, traceback) File "/home/user/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 77, in map_httpcore_exceptions raise mapped_exc(message) from exc httpx.ReadTimeout: The read operation timed out
-
I help all that ask.
-
I help all that ask.
-
I help all that ask.
Similar Threads
-
Chrome CDM
By mostafasmh1996 in forum Video Streaming DownloadingReplies: 20Last Post: 17th Nov 2023, 12:23 -
Chrome CDM
By ronron555 in forum Video Streaming DownloadingReplies: 10Last Post: 3rd Dec 2022, 11:26 -
itv hd
By bigd in forum Video Streaming DownloadingReplies: 22Last Post: 23rd Nov 2022, 06:25 -
problems downloading itv.com/ itv player files with tubedigger software
By elm in forum Video Streaming DownloadingReplies: 1Last Post: 4th May 2021, 04:28 -
new cdm
By ThunderRanger in forum Video Streaming DownloadingReplies: 0Last Post: 6th May 2020, 03:16