Setup the execution arch

This commit is contained in:
Barbagus 2022-12-09 00:34:15 +01:00
parent f22fe297c5
commit 9593619c68
4 changed files with 169 additions and 67 deletions

View File

@ -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 ?

View File

@ -25,4 +25,4 @@ dev = [
]
[project.scripts]
delarte = "delarte:main"
delarte = "delarte.__main__:main"

View File

@ -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

View File

@ -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())