forked from fcode/delarte
Setup the execution arch
This commit is contained in:
parent
f22fe297c5
commit
9593619c68
38
README.md
38
README.md
|
@ -14,8 +14,42 @@ ArteTV is a is a European public service channel dedicated to culture. Available
|
|||
🚀 Quick start
|
||||
---------------
|
||||
|
||||
_to be determined_
|
||||
Install [FFMPEG](https://ffmpeg.org/download.html) binaries and ensure it is in your `PATH`
|
||||
```
|
||||
$ ffmpeg -version
|
||||
ffmpeg version N-109344-g1bebcd43e1-20221202 Copyright (c) 2000-2022 the FFmpeg developers
|
||||
built with gcc 12.2.0 (crosstool-NG 1.25.0.90_cf9beb1)
|
||||
```
|
||||
|
||||
Clone this repository
|
||||
```
|
||||
$ git clone git@gitlab.com:Barbagus/delarte.git
|
||||
$ cd delarte
|
||||
```
|
||||
|
||||
Optionally create a virtual environement
|
||||
```
|
||||
$ python3 -m venv .venv
|
||||
$ source .venv/Scripts/activate
|
||||
```
|
||||
|
||||
Install in edit mode
|
||||
```
|
||||
$ pip install -e .[dev]
|
||||
```
|
||||
|
||||
Now you can run the script
|
||||
```
|
||||
$ python3 -m delarte --help
|
||||
or
|
||||
$ delarte --help
|
||||
ArteTV dowloader.
|
||||
|
||||
usage: delarte [-h|--help] - print this message
|
||||
or: delarte program_page_url - show available versions
|
||||
or: delarte program_page_url version - show available resolutions
|
||||
or: delarte program_page_url version resolution - download the given video
|
||||
```
|
||||
|
||||
🔧 How it works
|
||||
----------------
|
||||
|
@ -258,7 +292,7 @@ The actual build of the video file is handled by [ffmpeg](https://ffmpeg.org/).
|
|||
|
||||
##### Why not use FFMPEG direcly with the _version index_ URL ?
|
||||
|
||||
So we can select the video resolution _version_ and not rely on stream mapping arguments in `ffmpeg`.
|
||||
So we can select the video resolution and not rely on stream mapping arguments in `ffmpeg`.
|
||||
|
||||
##### Why not use VTT subtitles direcly ?
|
||||
|
||||
|
|
|
@ -25,4 +25,4 @@ dev = [
|
|||
]
|
||||
|
||||
[project.scripts]
|
||||
delarte = "delarte:main"
|
||||
delarte = "delarte.__main__:main"
|
|
@ -10,21 +10,15 @@ __version__ = "0.1"
|
|||
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from http import HTTPStatus
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import urlopen
|
||||
|
||||
import m3u8
|
||||
import webvtt
|
||||
|
||||
FFMPEG = os.environ.get("PATH_FFMPEG", "ffmpeg path not found")
|
||||
|
||||
|
||||
def load_api_data(url):
|
||||
"""Retrieve the root node (infamous "data") of an API call response."""
|
||||
|
@ -182,71 +176,29 @@ def find_resolution(version_index, resolution_code):
|
|||
return None
|
||||
|
||||
|
||||
def build_args(video_index_url, audio_track, subtitles_track, file_base_name):
|
||||
def build_ffmpeg_cmd(video_index_url, audio_track, subtitles_track, file_base_name):
|
||||
"""Build FFMPEG args."""
|
||||
audio_lang, audio_index_url = audio_track
|
||||
if subtitles_track:
|
||||
subtitles_lang, subtitles_file = subtitles_track
|
||||
|
||||
args = ["ffmpeg"]
|
||||
args.extend(["-i", video_index_url])
|
||||
args.extend(["-i", audio_index_url])
|
||||
cmd = ["ffmpeg"]
|
||||
cmd.extend(["-i", video_index_url])
|
||||
cmd.extend(["-i", audio_index_url])
|
||||
if subtitles_track:
|
||||
args.extend(["-i", subtitles_file])
|
||||
cmd.extend(["-i", subtitles_file])
|
||||
|
||||
args.extend(["-c:v", "copy"])
|
||||
args.extend(["-c:a", "copy"])
|
||||
cmd.extend(["-c:v", "copy"])
|
||||
cmd.extend(["-c:a", "copy"])
|
||||
if subtitles_track:
|
||||
args.extend(["-c:s", "copy"])
|
||||
cmd.extend(["-c:s", "copy"])
|
||||
|
||||
args.extend(["-bsf:a", "aac_adtstoasc"])
|
||||
args.extend(["-metadata:s:a:0", f"language={audio_lang}"])
|
||||
cmd.extend(["-bsf:a", "aac_adtstoasc"])
|
||||
cmd.extend(["-metadata:s:a:0", f"language={audio_lang}"])
|
||||
|
||||
if subtitles_track:
|
||||
args.extend(["-metadata:s:s:0", f"language={subtitles_lang}"])
|
||||
args.extend(["-disposition:s:0", "default"])
|
||||
cmd.extend(["-metadata:s:s:0", f"language={subtitles_lang}"])
|
||||
cmd.extend(["-disposition:s:0", "default"])
|
||||
|
||||
args.append(f"{file_base_name}.mkv")
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI function, options passed as arguments."""
|
||||
(ui_lang, _, stream_id, _slug) = urlparse(sys.argv[1]).path[1:-1].split("/")
|
||||
version_code = sys.argv[2] if len(sys.argv) > 2 else ""
|
||||
resolution_code = sys.argv[3] if len(sys.argv) > 3 else ""
|
||||
|
||||
if ui_lang not in ("fr", "de", "en", "es", "pl", "it") or _ != "videos":
|
||||
raise ValueError("Invalid URL")
|
||||
|
||||
config = load_config_api(ui_lang, stream_id)
|
||||
|
||||
version_index_url = find_version(config, version_code)
|
||||
if version_index_url is None:
|
||||
print(f"Available versions:", file=sys.stderr)
|
||||
for (code, label, _) in iter_versions(config):
|
||||
print(f"\t{code} - {label}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
version_index = load_version_index(version_index_url)
|
||||
|
||||
stream_info = find_resolution(version_index, resolution_code)
|
||||
if stream_info is None:
|
||||
print(f"Available resolutions:", file=sys.stderr)
|
||||
for code, label in iter_resolutions(version_index):
|
||||
print(f"\t{code} - {label}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
video_index_url, audio_track, subtitles_track = stream_info
|
||||
if subtitles_track:
|
||||
subtitles_lang, subtitles_index_url = subtitles_track
|
||||
subtitle_file = make_srt_tempfile(subtitles_index_url)
|
||||
subtitles_track = (subtitles_lang, subtitle_file)
|
||||
|
||||
file_base_name = build_file_base_name(config)
|
||||
|
||||
args = build_args(video_index_url, audio_track, subtitles_track, file_base_name)
|
||||
|
||||
subprocess.run(args)
|
||||
if subtitle_file:
|
||||
os.unlink(subtitle_file)
|
||||
cmd.append(f"{file_base_name}.mkv")
|
||||
return cmd
|
||||
|
|
|
@ -1,3 +1,119 @@
|
|||
from . import main
|
||||
"""ArteTV dowloader.
|
||||
|
||||
main()
|
||||
usage: delarte [-h|--help] - print this message
|
||||
or: delarte program_page_url - show available versions
|
||||
or: delarte program_page_url version - show available resolutions
|
||||
or: delarte program_page_url version resolution - download the given video
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from . import (
|
||||
build_ffmpeg_cmd,
|
||||
build_file_base_name,
|
||||
find_resolution,
|
||||
find_version,
|
||||
iter_resolutions,
|
||||
iter_versions,
|
||||
load_config_api,
|
||||
load_version_index,
|
||||
make_srt_tempfile,
|
||||
)
|
||||
|
||||
|
||||
def fail(message, code=1):
|
||||
"""Print a message to STDERR and return a given exit code."""
|
||||
print(message, file=sys.stderr)
|
||||
return code
|
||||
|
||||
|
||||
def print_available_versions(config, f):
|
||||
"""Print available program versions."""
|
||||
print(f"Available versions:", file=f)
|
||||
for (code, label, _) in iter_versions(config):
|
||||
print(f"\t{code} - {label}", file=f)
|
||||
|
||||
|
||||
def print_available_resolutions(version_index, f):
|
||||
"""Print available version resolutions."""
|
||||
print(f"Available resolutions:", file=f)
|
||||
for code, label in iter_resolutions(version_index):
|
||||
print(f"\t{code} - {label}", file=f)
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI command."""
|
||||
args = sys.argv[1:]
|
||||
if not args or args[0] == "-h" or args[0] == "--help":
|
||||
print(__doc__)
|
||||
return 0
|
||||
|
||||
try:
|
||||
program_page_url = urlparse(args.pop(0))
|
||||
if program_page_url.hostname != "www.arte.tv":
|
||||
return fail("Not an ArteTV url")
|
||||
|
||||
program_page_path = program_page_url.path.split("/")[1:]
|
||||
|
||||
ui_language = program_page_path.pop(0)
|
||||
|
||||
if ui_language not in ("fr", "de", "en", "es", "pl", "it"):
|
||||
return fail(f"Invalid url language code: {ui_language}")
|
||||
|
||||
if program_page_path.pop(0) != "videos":
|
||||
return fail("Invalid ArteTV url")
|
||||
|
||||
program_id = program_page_path.pop(0)
|
||||
|
||||
except ValueError:
|
||||
return fail("Invalid url")
|
||||
|
||||
try:
|
||||
config = load_config_api(ui_language, program_id)
|
||||
except ValueError:
|
||||
return fail("Invalid program")
|
||||
|
||||
if not args:
|
||||
print_available_versions(config, sys.stdout)
|
||||
return 0
|
||||
|
||||
version_index_url = find_version(config, args.pop(0))
|
||||
if version_index_url is None:
|
||||
fail("Invalid version")
|
||||
print_available_versions(config, sys.stderr)
|
||||
return 1
|
||||
|
||||
version_index = load_version_index(version_index_url)
|
||||
|
||||
if not args:
|
||||
print_available_resolutions(version_index, sys.stdout)
|
||||
return 0
|
||||
|
||||
stream_info = find_resolution(version_index, args.pop(0))
|
||||
if stream_info is None:
|
||||
fail("Invalid resolution")
|
||||
print_available_resolutions(version_index, sys.stderr)
|
||||
return 0
|
||||
|
||||
video_index_url, audio_track, subtitles_track = stream_info
|
||||
if subtitles_track:
|
||||
subtitles_lang, subtitles_index_url = subtitles_track
|
||||
subtitle_file = make_srt_tempfile(subtitles_index_url)
|
||||
subtitles_track = (subtitles_lang, subtitle_file)
|
||||
|
||||
file_base_name = build_file_base_name(config)
|
||||
|
||||
args = build_ffmpeg_cmd(
|
||||
video_index_url, audio_track, subtitles_track, file_base_name
|
||||
)
|
||||
|
||||
subprocess.run(args)
|
||||
if subtitle_file:
|
||||
os.unlink(subtitle_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
Loading…
Reference in New Issue