Linux support WIP

This commit is contained in:
Aswin 2025-03-21 00:39:48 +05:30
parent f8c4accd54
commit 13a9a72b80
11 changed files with 58 additions and 18 deletions

@ -6,15 +6,45 @@ Modified to remove Playready DRM instead of Widevine.
- Progress Bars for decryption ([mp4decrypt](https://github.com/chu23465/bentoOldFork), Shaka) - Progress Bars for decryption ([mp4decrypt](https://github.com/chu23465/bentoOldFork), Shaka)
- Refresh Token fixed for Amazon service - Refresh Token fixed for Amazon service
- Reprovision .prd after a week - Reprovision .prd after a week
- ISM manifest support (Microsoft Smooth Streaming) (Few features to be added) - ISM manifest support (Microsoft Smooth Streaming) (WIP/Experimental)
- N_m3u8DL-RE downloader support - N_m3u8DL-RE downloader support
## Broken
- `--bitrate CVBR+CBR` is currently broken
- Atmos audio with ISM manifest (Amazon) is currently broken (Needs a working init.mp4). If the title has the atmos audio in MPD then it should work.
- ATVP service is currently broken in dev branch
- Netflix service is currently broken (will probably be fixed Soon™)
If anyone has any idea how to fix above issues, feel free to open a pull request.
## Usage ## Usage
1. Run `install.bat` 1. Make sure git is installed in your system by running `git --version`. If not refer to [link](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
2. Choose a branch, either `dev` or `main`. Use below command to download. (Recommended instead of downloading zip)
```bash
git clone -b <branch-name> --single-branch https://github.com/chu23465/VT-PR
```
2. Activate venv using `venv.cmd`. 3. Navigate and find `install.bat`
4. Run `install.bat`
5. Activate venv using `venv.cmd`.
6. Run desired command using poetry.
## Updating
1. Backup your `vinetrimmer/Cookies/`, `vinetrimmer/Cache/`, `/Downloads` directories just in case.
2. Open a command prompt and navigate your `VT-PR` directory.
3. Recall the branch you downloaded and modify below command accordingly:
```bash
git pull origin <branch-name>
```
### Config ### Config
@ -124,7 +154,7 @@ Below flags to be passed after the `AMZN` or `Amazon` keyword in command.
| -ism, --ism | Set manifest override to SmoothStreaming. Defaults to DASH w/o this flag. | | -ism, --ism | Set manifest override to SmoothStreaming. Defaults to DASH w/o this flag. |
| -?, -h, --help | Show this message and exit. | | -?, -h, --help | Show this message and exit. |
To get UHD/4k with Amazon, navigate to - To get Atmos/UHD/4k with Amazon, navigate to -
``` ```
https://www.primevideo.com/region/eu/ontv/code?ref_=atv_auth_red_aft https://www.primevideo.com/region/eu/ontv/code?ref_=atv_auth_red_aft

BIN
linux_binaries/N_m3u8DL-RE Normal file

Binary file not shown.

Binary file not shown.

BIN
linux_binaries/mp4decrypt Normal file

Binary file not shown.

BIN
linux_binaries/mp4dump Normal file

Binary file not shown.

BIN
linux_binaries/packager Normal file

Binary file not shown.

@ -98,7 +98,7 @@ def get_cdm(log, service, profile=None, cdm_name=None):
device = Device.load(os.path.join(directories.devices, f"{cdm_name}.wvd")) device = Device.load(os.path.join(directories.devices, f"{cdm_name}.wvd"))
except: except:
device_path = os.path.abspath(os.path.join(directories.devices, f"{cdm_name}.prd")) device_path = os.path.abspath(os.path.join(directories.devices, f"{cdm_name}.prd"))
if ( int( time.time() ) - int( os.path.getmtime( device_path ) ) ) > 600000: #roughly a week if ( int( time.time() ) - int( os.path.getmtime( device_path ) ) ) > 160000: #roughly 2 days
try: try:
reprovision_device(device_path) reprovision_device(device_path)
log.info(f" + Reprovisioned Playready Device (.prd) file, {cdm_name}") log.info(f" + Reprovisioned Playready Device (.prd) file, {cdm_name}")

@ -217,6 +217,7 @@ def parse(*, url=None, data=None, source, session=None, downloader=None):
# extra # extra
extra=(list(quality_level), list(stream_info),) # Either set size as a attribute of VideoTrack or append to extra here. extra=(list(quality_level), list(stream_info),) # Either set size as a attribute of VideoTrack or append to extra here.
)) ))
"""
elif type_info == 'audio': elif type_info == 'audio':
atmos = ( str( quality_level.get('@HasAtmos', 'N/A') ).lower() == "true" ) or ( "ATM" in stream_info.get('@Name', 'N/A') ) atmos = ( str( quality_level.get('@HasAtmos', 'N/A') ).lower() == "true" ) or ( "ATM" in stream_info.get('@Name', 'N/A') )
tracks.append(AudioTrack( tracks.append(AudioTrack(
@ -224,7 +225,7 @@ def parse(*, url=None, data=None, source, session=None, downloader=None):
source=source, source=source,
url=url, url=url,
# metadata # metadata
codec=(codec or "").split(".")[0], codec="E-AC3" if codec == "EC-3" else (codec or "").split(".")[0],
language=lang, language=lang,
bitrate=bitrate, bitrate=bitrate,
channels=quality_level.get('@Channels', 'N/A'), channels=quality_level.get('@Channels', 'N/A'),
@ -232,11 +233,12 @@ def parse(*, url=None, data=None, source, session=None, downloader=None):
# switches/options # switches/options
descriptor=Track.Descriptor.ISM, descriptor=Track.Descriptor.ISM,
# decryption # decryption
needs_repack=False, # Necessary needs_repack=True, # Necessary
encrypted=encrypted, encrypted=encrypted,
pssh=pssh, pssh=pssh,
kid=kid, kid=kid,
# extra # extra
extra=(dict(quality_level), dict(stream_info),) extra=(dict(quality_level), dict(stream_info),)
)) ))
"""
return tracks return tracks

@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
from sys import platform
import base64 import base64
import hashlib import hashlib
import json import json
@ -75,7 +77,7 @@ class Amazon(BaseService):
@click.option("-c", "--cdn", default=None, type=str, @click.option("-c", "--cdn", default=None, type=str,
help="CDN to download from, defaults to the CDN with the highest weight set by Amazon.") help="CDN to download from, defaults to the CDN with the highest weight set by Amazon.")
# UHD, HD, SD. UHD only returns HEVC, ever, even for <=HD only content # UHD, HD, SD. UHD only returns HEVC, ever, even for <=HD only content
@click.option("-vq", "--vquality", default="UHD", @click.option("-vq", "--vquality", default="HD",
type=click.Choice(["SD", "HD", "UHD"], case_sensitive=False), type=click.Choice(["SD", "HD", "UHD"], case_sensitive=False),
help="Manifest quality to request.") help="Manifest quality to request.")
@click.option("-s", "--single", is_flag=True, default=False, @click.option("-s", "--single", is_flag=True, default=False,
@ -83,7 +85,7 @@ class Amazon(BaseService):
@click.option("-am", "--amanifest", default="H265", @click.option("-am", "--amanifest", default="H265",
type=click.Choice(["CVBR", "CBR", "H265"], case_sensitive=False), type=click.Choice(["CVBR", "CBR", "H265"], case_sensitive=False),
help="Manifest to use for audio. Defaults to H265 if the video manifest is missing 640k audio.") help="Manifest to use for audio. Defaults to H265 if the video manifest is missing 640k audio.")
@click.option("-aq", "--aquality", default="SD", @click.option("-aq", "--aquality", default="HD",
type=click.Choice(["SD", "HD", "UHD"], case_sensitive=False), type=click.Choice(["SD", "HD", "UHD"], case_sensitive=False),
help="Manifest quality to request for audio. Defaults to the same as --quality.") help="Manifest quality to request for audio. Defaults to the same as --quality.")
@click.option("-ism", "--ism", is_flag=True, default=False, @click.option("-ism", "--ism", is_flag=True, default=False,
@ -115,7 +117,11 @@ class Amazon(BaseService):
self.cdm = ctx.obj.cdm self.cdm = ctx.obj.cdm
self.profile = ctx.obj.profile self.profile = ctx.obj.profile
if "linux" in platform:
import yaml
#Read YAML file
with open("/content/vinetrimmer/config/Services/amazon.yml", 'r') as stream:
self.config = yaml.safe_load(stream)
self.region: dict[str, str] = {} self.region: dict[str, str] = {}
self.endpoints: dict[str, str] = {} self.endpoints: dict[str, str] = {}
self.device: dict[str, str] = {} self.device: dict[str, str] = {}
@ -411,6 +417,7 @@ class Amazon(BaseService):
video_codec="H265" if manifest_type == "H265" else "H264", video_codec="H265" if manifest_type == "H265" else "H264",
bitrate_mode="CVBR" if manifest_type != "CBR" else "CBR", bitrate_mode="CVBR" if manifest_type != "CBR" else "CBR",
quality=self.aquality or self.vquality, quality=self.aquality or self.vquality,
manifest_type="DASH",
hdr=None, hdr=None,
ignore_errors=True ignore_errors=True
) )
@ -506,7 +513,8 @@ class Amazon(BaseService):
# replace the audio tracks with DV manifest version if atmos is present # replace the audio tracks with DV manifest version if atmos is present
if any(x for x in uhd_audio_mpd.audios if x.atmos): if any(x for x in uhd_audio_mpd.audios if x.atmos):
tracks.audios = uhd_audio_mpd.audios print("Hello")
tracks.audios = uhd_audio_mpd.audios
for video in tracks.videos: for video in tracks.videos:
try: try:

@ -62,6 +62,7 @@ class Max(BaseService):
self.acodec = ctx.parent.params["acodec"] self.acodec = ctx.parent.params["acodec"]
self.range = ctx.parent.params["range_"] self.range = ctx.parent.params["range_"]
self.alang = ctx.parent.params["alang"] self.alang = ctx.parent.params["alang"]
self.quality = ctx.parent.params["quality"] or 1080
# self.api_region = self.config.get(ctx.obj.profile, {}).get('api_region', 'comet-latam') # self.api_region = self.config.get(ctx.obj.profile, {}).get('api_region', 'comet-latam')
# self.license_api = None # self.license_api = None

@ -15,6 +15,7 @@ import yaml
from vinetrimmer import config from vinetrimmer import config
from vinetrimmer.utils.collections import as_list from vinetrimmer.utils.collections import as_list
from sys import platform
def load_yaml(path): def load_yaml(path):
if not os.path.isfile(path): if not os.path.isfile(path):
@ -239,12 +240,11 @@ async def saldl(uri, out, headers=None, proxy=None):
async def m3u8dl(uri: str, out: str, track): async def m3u8dl(uri: str, out: str, track):
executable = shutil.which("N_m3u8DL-RE") or shutil.which("m3u8DL") executable = shutil.which("N_m3u8DL-RE") or shutil.which("m3u8DL") or "/usr/bin/N_m3u8DL-RE"
if not executable: if not executable:
raise EnvironmentError("N_m3u8DL-RE executable not found...") raise EnvironmentError("N_m3u8DL-RE executable not found...")
ffmpeg_binary = shutil.which("ffmpeg") ffmpeg_binary = shutil.which("ffmpeg") or "/usr/bin/ffmpeg"
arguments = [ arguments = [
executable, executable,
uri, uri,
@ -257,11 +257,10 @@ async def m3u8dl(uri: str, out: str, track):
"--download-retry-count", "8", "--download-retry-count", "8",
"--ffmpeg-binary-path", ffmpeg_binary, "--ffmpeg-binary-path", ffmpeg_binary,
"--binary-merge", "--binary-merge",
"--decryption-engine", "SHAKA_PACKAGER",
"--http-request-timeout", "8",
"--live-real-time-merge"
] ]
if not ("linux" in platform):
arguments.append("--http-request-timeout")
arguments.append("8")
if track.__class__.__name__ == "VideoTrack": if track.__class__.__name__ == "VideoTrack":
if track.height: if track.height:
arguments.extend([ arguments.extend([