ShortMax is a premium short drama platform with an extensive catalog of short-form series across multiple genres. This API provides multi-quality video streaming, trending content, search, and complete episode access with full catalog browsing.
Returns proxied HLS m3u8 playlist with absolute CDN segment URLs. No API key required. TS segments are served directly from ShortMax CDN (zero server bandwidth).
ShortMax uses a custom encryption format for TS segments served from the hls-encrypted CDN path. These segments cannot be played directly by standard HLS players — they require client-side decryption first.
⚠
Note: The m3u8 playlist does NOT contain #EXT-X-KEY tags. The AES key is embedded inside each TS segment's 1024-byte header. You must download each segment, extract the key, decrypt, then play/concatenate the raw MPEG-TS data.
How to detect: If the video URL contains /hls-encrypted/ in the path, the segments are encrypted. URLs with /hls/ (without -encrypted) are standard unencrypted HLS.
Segment Binary Format
1024-byte Header
Byte Range
Size
Content
Description
0 – 15
16 bytes
shortmax00000001
Magic header (identifies encrypted segment)
16 – 19
4 bytes
UTF-8 decimal
keyOffset — position within the 1024-byte header where the 16-byte AES key starts
20 – 23
4 bytes
UTF-8 decimal
encryptedLength — number of payload bytes that are encrypted (the rest is plaintext)
24 – 1023
1000 bytes
Filler data
Random-looking padding. Contains the AES key at header[keyOffset : keyOffset+16]
Download the full TS segment. The first 1024 bytes are the header; everything after is the payload.
2Extract keyOffset and encryptedLength
Parse bytes [16:20] as a UTF-8 decimal integer → keyOffset.
Parse bytes [20:24] as a UTF-8 decimal integer → encryptedLength.
Trim any whitespace/padding from these strings before parsing.
3Extract the AES key from the header
Read 16 bytes from the header starting at keyOffset: aesKey = header[keyOffset : keyOffset + 16]
4Decrypt with AES-128-CBC
Use the extracted key and fixed IV: IV = "shortmax00000000" (16 bytes, ASCII)
Split the payload (bytes after 1024):
• Encrypted part = payload[0 : encryptedLength]
• Plaintext tail = payload[encryptedLength : end]
Decrypt the encrypted part with AES-128-CBC, then remove PKCS7 padding.
5Concatenate the result
Final MPEG-TS segment = decrypted data + plaintext tail
The result is a standard MPEG-TS segment (first byte should be 0x47 — the MPEG-TS sync byte).
Code Examples
Copy & Paste
PythonDecrypt a single TS segment
from Crypto.Cipher import AES
import requests
defdecrypt_shortmax_ts(data):
"""Decrypt a ShortMax hls-encrypted TS segment."""if len(data) <= 1024:
return data
header = data[:1024]
# Parse keyOffset and encryptedLength from header
key_offset = int(header[16:20].decode('utf-8').strip())
enc_length = int(header[20:24].decode('utf-8').strip())
# Extract AES key from header at dynamic offset
aes_key = header[key_offset : key_offset + 16]
iv = b"shortmax00000000"
payload = data[1024:]
encrypted = payload[:enc_length]
tail = payload[enc_length:]
# AES-128-CBC decrypt
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(encrypted)
# Remove PKCS7 padding
pad = decrypted[-1]
if0 < pad <= 16and all(b == pad for b in decrypted[-pad:]):
decrypted = decrypted[:-pad]
return decrypted + tail
# Usage:
seg_url = "https://akamai-static.shorttv.live/hls-encrypted/{hash}_720/main/segment-0.ts"
raw = requests.get(seg_url).content
ts_data = decrypt_shortmax_ts(raw)
# ts_data is now a standard MPEG-TS segment (first byte = 0x47)
JavaScriptNode.js / Browser (Web Crypto API)
async functiondecryptShortmaxTS(data) {
const buf = data instanceof ArrayBuffer ? data : data.buffer;
if (buf.byteLength <= 1024) return new Uint8Array(buf);
const header = new TextDecoder().decode(new Uint8Array(buf, 0, 1024));
const keyOffset = parseInt(header.slice(16, 20).trim());
const encLength = parseInt(header.slice(20, 24).trim());
// Extract AES key from headerconst aesKeyBytes = new Uint8Array(buf, keyOffset, 16);
const iv = new TextEncoder().encode("shortmax00000000");
// Import key for AES-CBCconst key = await crypto.subtle.importKey(
"raw", aesKeyBytes, { name: "AES-CBC" }, false, ["decrypt"]
);
// Decrypt encrypted portionconst encrypted = new Uint8Array(buf, 1024, encLength);
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-CBC", iv }, key, encrypted
); // Web Crypto removes PKCS7 padding automatically// Concat decrypted + plaintext tailconst tail = new Uint8Array(buf, 1024 + encLength);
const result = new Uint8Array(decrypted.byteLength + tail.byteLength);
result.set(new Uint8Array(decrypted), 0);
result.set(tail, decrypted.byteLength);
return result; // Standard MPEG-TS data
}
// Usage:const res = await fetch(segmentUrl);
const encrypted = await res.arrayBuffer();
const tsData = await decryptShortmaxTS(encrypted);
GoDecrypt TS segment
funcdecryptShortmaxTS(data []byte) ([]byte, error) {
if len(data) <= 1024 {
return data, nil
}
header := string(data[:1024])
keyOffset, _ := strconv.Atoi(strings.TrimSpace(
string(data[16:20])))
encLength, _ := strconv.Atoi(strings.TrimSpace(
string(data[20:24])))
// AES key is embedded in the header
aesKey := []byte(header[keyOffset : keyOffset+16])
iv := []byte("shortmax00000000")
block, _ := aes.NewCipher(aesKey)
payload := data[1024:]
if encLength > len(payload) {
encLength = len(payload)
}
encrypted := payload[:encLength]
tail := payload[encLength:]
decrypted := make([]byte, len(encrypted))
cipher.NewCBCDecrypter(block, iv).CryptBlocks(
decrypted, encrypted)
// Remove PKCS7 padding
pad := int(decrypted[len(decrypted)-1])
if pad > 0 && pad <= aes.BlockSize {
decrypted = decrypted[:len(decrypted)-pad]
}
result := make([]byte, len(decrypted)+len(tail))
copy(result, decrypted)
copy(result[len(decrypted):], tail)
return result, nil
}
Full Playback Flow
End-to-End
Step 1: Get the HLS playlist GET /api/shortmax/hls?id=3711&ep=1&q=720p
Returns an m3u8 playlist with absolute CDN segment URLs.
Step 2: Parse the m3u8
Extract all .ts segment URLs from the playlist.
Step 3: Check the URL path
If the segment URL contains /hls-encrypted/ → decrypt each segment.
If the URL contains /hls/ (no -encrypted) → play directly, no decryption needed.
Step 4: Download & decrypt each segment
For each .ts URL: download the raw bytes, apply the decrypt function above.
Step 5: Play or save
Concatenate all decrypted segments into a single .ts file, or feed them into a player that accepts raw MPEG-TS.
Full download + decrypt example (bash)
# 1. Get m3u8 playlist
curl -s "{{BASE}}/api/shortmax/hls?id=3711&ep=1&q=720p" -o playlist.m3u8
# 2. Extract segment URLs
grep -v '^#' playlist.m3u8 | grep '.ts' > segments.txt
# 3. Download all segments
mkdir -p segments
i=0; while read url; do
curl -s "$url" -o "segments/seg_$i.ts"
i=$((i+1))
done < segments.txt
# 4. Decrypt with Python (using the function above)
python3 -c "
from Crypto.Cipher import AES
import glob, os
def decrypt(data):
if len(data) <= 1024: return data
ko = int(data[16:20].decode().strip())
el = int(data[20:24].decode().strip())
key = data[ko:ko+16]
iv = b'shortmax00000000'
payload = data[1024:]
enc, tail = payload[:el], payload[el:]
dec = AES.new(key, AES.MODE_CBC, iv).decrypt(enc)
pad = dec[-1]
if 0 < pad <= 16: dec = dec[:-pad]
return dec + tail
files = sorted(glob.glob('segments/seg_*.ts'),
key=lambda f: int(f.split('_')[1].split('.')[0]))
with open('output.ts', 'wb') as out:
for f in files:
out.write(decrypt(open(f, 'rb').read()))
print(f'Decrypted {len(files)} segments -> output.ts')
"
# 5. Play with ffplay or convert to mp4
ffplay output.ts
# or: ffmpeg -i output.ts -c copy output.mp4
Quick Reference
Cheat Sheet
Property
Value
Algorithm
AES-128-CBC
Key Size
16 bytes (128 bits)
Key Source
Embedded in segment header at dynamic offset
IV (fixed)
shortmax00000000 (16 bytes ASCII)
Padding
PKCS7
Header Size
1024 bytes (always)
Magic Bytes
shortmax00000001 (first 16 bytes)
Partial Encryption
Only first encryptedLength bytes of payload are encrypted; the rest is plaintext
ShortMax — Api-Dracin Multi-Source Drama Streaming API