globocom / m3u8

Python m3u8 Parser for HTTP Live Streaming (HLS) Transmissions
Other
2.03k stars 471 forks source link

How to deal with m3u8 without links in uri #311

Closed RevealedSoulEven closed 1 year ago

RevealedSoulEven commented 1 year ago
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=187909,RESOLUTION=640x360
hls/360/main.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=158770,RESOLUTION=426x240
hls/240/main.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=310500,RESOLUTION=1280x720
hls/720/main.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=201372,RESOLUTION=854x480
hls/480/main.m3u8

as seen in this m3u8 file the uri can't be specified as a link. How to download this type of file? Even it's possible to download using 1dm app in android

RevealedSoulEven commented 1 year ago

{'media_sequence': None, 'is_variant': True, 'is_endlist': False, 'is_i_frames_only': False, 'is_independent_segments': False, 'playlist_type': None, 'playlists': [{'uri': 'hls/360/main.m3u8', 'stream_info': {'bandwidth': 187909, 'resolution': '640x360'}}, {'uri': 'hls/240/main.m3u8', 'stream_info': {'bandwidth': 158770, 'resolution': '426x240'}}, {'uri': 'hls/720/main.m3u8', 'stream_info': {'bandwidth': 310500, 'resolution': '1280x720'}}, {'uri': 'hls/480/main.m3u8', 'stream_info': {'bandwidth': 201372, 'resolution': '854x480'}}], 'segments': [], 'iframe_playlists': [], 'media': [], 'keys': [], 'rendition_reports': [], 'skip': {}, 'part_inf': {}, 'session_data': [], 'session_keys': [], 'segment_map': [], 'version': 3}

This is the data I get

bbayles commented 1 year ago

Use the absolute_uri attribute.

>>> import m3u8
>>> playlist = m3u8.load('http://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8')
>>> playlist.playlists[0].absolute_uri
http://cph-p2p-msl.akamaized.net/hls/live/2000341/test/level_0.m3u8

If you loaded the multivariant playlist from text rather than a URL, use urljoin.

>>> from urllib.parse import urljoin
>>> urljoin('https://example.org', 'hls/360/main.m3u8')
https://example.org/hls/360/main.m3u8
RevealedSoulEven commented 1 year ago

Alright, the absolute_uri doesn't worked for me

But getting to the second point I added /hls/720/main.m3u8 at last of the url and I got this

[link removed]

But I have no idea how to deal with it

bbayles commented 1 year ago

Use the same trick with urljoin to turn those .ts items into URLs.

RevealedSoulEven commented 1 year ago

Yes but I think that this .ts file is encrypted

bbayles commented 1 year ago

You'll have to download the key and decrypt the .ts files.

Something like this:

import m3u8
import requests
from urllib.parse import urljoin
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

base_url = '...'  # Your URL here
playlist_text = '...'  # Your text here

parsed_playlist = m3u8.loads(playlist_text)
seq_no = parsed_playlist.media_sequence or 0

key_data = {}

for n, segment in enumerate(parsed_playlist.segments, seq_no):
    # Save the key for this segment if we don't have it already
    key_url = segment.key.uri
    if key_url not in key_data:
        key_data[key_url] = requests.get(key_url).content

    # Download the encrypted segment
    segment_url = urljoin(base_url, segment.uri)
    segment_data = requests.get(segment.key.uri).content

    # The initialization vector is either given in the playlist or is the sequence number
    iv = segment.key.iv or n.to_bytes(16, 'big')

    # Decrypt with the key and IV
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
    decryptor = cipher.decryptor()
    decrypted_data = decryptor.update(segment_data) + decryptor.finalize()

    # Do something with the decrypted data

That's probably not going to work verbatim, but those are the right steps.

RevealedSoulEven commented 1 year ago

Then why isn't it all required in the 1dm app? I'm confused whether I'm going right

On Wed, 25 Jan, 2023, 9:28 pm Bo Bayles, @.***> wrote:

You'll have to download the key and decrypt the .ts files.

Something like this:

import m3u8import requestsfrom urllib.parse import urljoinfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes base_url = '...' # Your URL hereplaylist_text = '...' # Your text here parsed_playlist = m3u8.loads(playlist_text)seq_no = parsed_playlist.media_sequence or 0 key_data = {} for n, segment in enumerate(parsed_playlist.segments, seq_no):

Save the key for this segment if we don't have it already

key_url = segment.key.uri
if key_url not in key_data:
    key_data[key_url] = requests.get(key_url).content

# Download the encrypted segment
segment_url = urljoin(base_url, segment.uri)
segment_data = requests.get(segment.key.uri).content

# The initialization vector is either give in the playlist or is the sequence number
iv = segment.key.iv or f'0x{n:032x}'

# Decrypt with the key and IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(ts) + decryptor.finalize()

# Do something with the decrypted data

That's probably not going to work verbatim, but those are the right steps.

— Reply to this email directly, view it on GitHub https://github.com/globocom/m3u8/issues/311#issuecomment-1403841553, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALYHMXXQSB2XG5O3VGJZUMDWUFETDANCNFSM6AAAAAAUGANNQE . You are receiving this because you authored the thread.Message ID: @.***>

RevealedSoulEven commented 1 year ago

Thanks for your support, I've managed to key the key and will try decrypting it soon.

thanks a lot

RevealedSoulEven commented 1 year ago

but the code that you sent is using key as string and the cipher module is giving error that it needs bytes

bbayles commented 1 year ago

Try: iv = segment.key.iv or n.to_bytes(16, 'big') instead of what was there before.

RevealedSoulEven commented 1 year ago
NameError                                 Traceback (most recent call last)
Input In [5], in <cell line: 30>()
     45 cipher = Cipher(algorithms.AES(key_data[key_url]), modes.CBC(iv))
     46 decryptor = cipher.decryptor()
---> 47 decrypted_data = decryptor.update(ts) + decryptor.finalize()
     49 # Do something with the decrypted data
     50 print(decrypted_data)

NameError: name 'ts' is not defined
RevealedSoulEven commented 1 year ago

@bbayles

RevealedSoulEven commented 1 year ago

II managed to do this and got the decrypted .ts file and it's playable. Thanks for your support sir