Giter Club home page Giter Club logo

vsdownload's Introduction

This project is deprecated and it will no longer be maintained. This program has been rewritten in Rust located at https://github.com/clitic/vsd.

VSDownload - Video Stream (M3U8) Downloader

command line program to download hls video streams from websites, m3u8 files and urls. What is m3u8 ?

a compact lightweight m3u8 downloader

Installations    |    Usage

Features Implemented

  • auto binary merge for ts segments
  • auto decrypt for aes standard cbc encrypted playlists (beta)
  • auto mux for seperate video, audio and subtitle (webvtt) stream
  • capturing m3u8 links and urls from a website
  • custom headers, proxies, key and iv
  • downloading in multiple threads
  • ffmpeg conversion integration
  • gui support
  • master m3u8 playlist parsing
  • platform independent
  • realtime file size prediction (arithmetic mean) and downloading speed
  • resume and retry support
  • supports live stream download

Important Declaration

If you are distributing downloaded video streams, first ensure that you have rights for those video streams or files.

Installations

Requires*

pip install https://github.com/clitic/vsdownload/archive/main.zip

Or you can also find a windows executable / gui wrapper from releases.

Usage

vsdownload capture <website url> --driver <driver path>
vsdownload save log.json
  • Downloading hls video streams from m3u8 files
vsdownload save <m3u8 url or file> -o video.mp4

In -o/--output flag, any ffmpeg supported extension could be provided
Add --no-cleanup flag to use resume capabilities

GUI Wrapper

To use gui wrapper, first install PyQt6 and then run vsdownload-gui

$ pip install PyQt6
$ vsdownload-gui

Scripting And Automation

You can also integrate vsdownload save and capture command in any python program. This is useful when you have to automate or create sub website m3u8 downloaders. First you can find or parse the m3u8 uri from a website then call vsdownload.save() function in order to download it.

  • save command function
from vsdownload import vsdownload

vsdownload.save("http://videoserver.com/playlist.m3u8", output="merged.mp4")
  • capture and save command functions
from vsdownload import vsdownload

log_file = "stream_log.json"

vsdownload.capture("http://streamingsite.com/stream.html", "chromedriver.exe", output=log_file)
vsdownload.save(log_file, output="merged.mp4")
  • calling vsdownload through subprocess
import subprocess

command_args = [
  "vsdownload", # command
  "save", # sub command
  "http://videoserver.com/playlist.m3u8", # input
  "-o", # extra options
  "a.mp4" # output file
]

try:
  subprocess.run(command_args, check=True)
except subprocess.CalledProcessError as e:
  print(f"error code: {e.returncode}")

License

© 2021-22 clitic

This repository is licensed under the MIT license. See LICENSE for details.

vsdownload's People

Contributors

clitic avatar jacobthebanana avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vsdownload's Issues

Trying the save command, didn't work

Hey, was trying your soft because I'd like to download an m3u8 file (didn't know that existed 2hrs ago) and encountered an issue

INFO: segment 2426 added to retry queue 11
INFO: segment 2427 added to retry queue 11
INFO: segment 2428 added to retry queue 11
INFO: segment 2429 added to retry queue 11
INFO: segment 2430 added to retry queue 11
INFO: segment 2431 added to retry queue 11
INFO: segment 2432 added to retry queue 11
INFO: segment 2433 added to retry queue 11
INFO: segment 2434 added to retry queue 11
INFO: segment 2435 added to retry queue 11
INFO: segment 2436 added to retry queue 11
INFO: segment 2437 added to retry queue 11
INFO: segment 2438 added to retry queue 11
INFO: segment 2439 added to retry queue 11
INFO: segment 2440 added to retry queue 11
INFO: segment 2441 added to retry queue 11
INFO: segment 2442 added to retry queue 11
INFO: segment 2443 added to retry queue 11
INFO: segment 2444 added to retry queue 11

Here are the logs output, it keeps putting every segment into retry for some reason

The command I used which can probably reproduce the error

vsdownload.exe save https://s1.streami.cool/files/11891/output.m3u8 -o test.mp4

Cheers :)

'NoneType' object has no attribute 'replace'

┌─────────────────────────────── Traceback (most recent call last) ────────────────────────────────┐
│ C:\Users\ATES-PC\anaconda3\lib\site-packages\vsdownload\vsdownload.py:51 in call_save │
│ │
│ 48 │ │ timeout: int = typer.Option(5, help="waiting time for post tasks to perform afte │
│ 49 │ │ pre_select: int = typer.Option(None, help="pre select a url from log.json file", │
│ 50 │ ): │
│ > 51 │ command_save(Namespace(**locals())) │
│ 52 │
│ 53 def capture( │
│ 54 │ │ url: str, driver: str, output: str = "log.json", scan_ext: str = "ts", baseurl: │
│ │
│ ┌─────────────────────────────────────────── locals ───────────────────────────────────────────┐ │
│ │ baseurl = None │ │
│ │ chunk_size = 1024 │ │
│ │ cleanup = True │ │
│ │ decrypt = True │ │
│ │ ffmpeg_path = 'ffmpeg' │ │
│ │ headers = None │ │
│ │ input = 'https://btk-cdn01.cinema8.com/hls/content/entry/data/0/38/0_htzs22hi_0_lid… │ │
│ │ key_iv = None │ │
│ │ max_quality = False │ │
│ │ output = 'merged.ts' │ │
│ │ pre_select = None │ │
│ │ proxy_address = None │ │
│ │ retry_count = 10 │ │
│ │ tempdir = 'temptsfiles' │ │
│ │ threads = 5 │ │
│ │ timeout = 5 │ │
│ │ verbose = False │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ C:\Users\ATES-PC\anaconda3\lib\site-packages\vsdownload\commands\save.py:477 in command_save │
│ │
│ 474 │ segments = m3u8_downloader.parse_m3u8(parsed_links) │
│ 475 │ print(f"file will be saved at: {args.output}") │
│ 476 │ print(f"starting download in {args.threads} thread/s\n") │
│ > 477 │ m3u8_downloader.download_in_mutiple_thread(segments, parsed_links) │
│ 478 │ console.print(f"\n[green]file downloaded successfully at: {args.output}[/green]") │
│ 479 │
│ │
│ ┌─────────────────────────────────────────── locals ───────────────────────────────────────────┐ │
│ │ args = Namespace(input='https://btk-cdn01.cinema8.com/hls/content/entry/data/0/3… │ │
│ │ output='merged.ts', cleanup=True, max_quality=False, verbose=False, │ │
│ │ baseurl=None, threads=5, chunk_size=1024, headers=None, decrypt=True, │ │
│ │ key_iv=None, proxy_address=None, ffmpeg_path='ffmpeg', │ │
│ │ tempdir='temptsfiles', retry_count=10, timeout=5, pre_select=None) │ │
│ │ check = False │ │
│ │ m3u8_downloader = <vsdownload.commands.save.ProcessM3U8 object at 0x00000193A9964340> │ │
│ │ parsed_links = ( │ │
│ │ │ │ │
│ │ 'https://btk-cdn01.cinema8.com/hls/content/entry/data/0/38/0_htzs22hi_0_l… │ │
│ │ │ None │ │
│ │ ) │ │
│ │ segments = [ │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964C10>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964820>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964E20>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D60>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D00>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D30>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964EE0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964DC0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964CA0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964E80>, │ │
│ │ │ ... +43 │ │
│ │ ] │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ C:\Users\ATES-PC\anaconda3\lib\site-packages\vsdownload\commands\save.py:232 in │
│ download_in_mutiple_thread │
│ │
│ 229 │ │ │ │ │ │ "uri": utils.find_absolute_uri(parsed_links[1], segment), │
│ 230 │ │ │ │ │ │ "byterange": segment.byterange, │
│ 231 │ │ │ │ │ │ "key": self._find_key(parsed_links[1], segment), │
│ > 232 │ │ │ │ │ │ "iv": self._find_iv(segment), │
│ 233 │ │ │ │ │ │ "index": processed_ts_index, │
│ 234 │ │ │ │ │ │ "retrycount": 0 │
│ 235 │ │ │ │ │ } │
│ │
│ ┌─────────────────────────────────────────── locals ───────────────────────────────────────────┐ │
│ │ executor = <concurrent.futures.thread.ThreadPoolExecutor object at │ │
│ │ 0x00000193A9964C70> │ │
│ │ parsed_links = ( │ │
│ │ │ │ │
│ │ 'https://btk-cdn01.cinema8.com/hls/content/entry/data/0/38/0_htzs22hi_… │ │
│ │ │ None │ │
│ │ ) │ │
│ │ processed_ts_index = 1 │ │
│ │ segment = <m3u8.model.Segment object at 0x00000193A9964C10> │ │
│ │ segments = [ │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964C10>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964820>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964E20>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D60>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D00>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964D30>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964EE0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964DC0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964CA0>, │ │
│ │ │ <m3u8.model.Segment object at 0x00000193A9964E80>, │ │
│ │ │ ... +43 │ │
│ │ ] │ │
│ │ self = <vsdownload.commands.save.ProcessM3U8 object at 0x00000193A9964340> │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ C:\Users\ATES-PC\anaconda3\lib\site-packages\vsdownload\commands\save.py:283 in _find_iv │
│ │
│ 280 │ │ │ return str(iv) or None │
│ 281 │ │ │
│ 282 │ │ if segment.key is not None: │
│ > 283 │ │ │ return segment.key.iv.replace("0x", "") │
│ 284 │ │ else: │
│ 285 │ │ │ return None │
│ 286 │
│ │
│ ┌─────────────────────────────────── locals ────────────────────────────────────┐ │
│ │ segment = <m3u8.model.Segment object at 0x00000193A9964C10> │ │
│ │ self = <vsdownload.commands.save.ProcessM3U8 object at 0x00000193A9964340> │ │
│ └───────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
AttributeError: 'NoneType' object has no attribute 'replace'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.