2022-12-20 08:48:57 +00:00
|
|
|
# License: GNU AGPL v3: http://www.gnu.org/licenses/
|
|
|
|
# This file is part of `delarte` (https://git.afpy.org/fcode/delarte.git)
|
2022-12-06 00:16:16 +00:00
|
|
|
|
2022-12-13 06:29:59 +00:00
|
|
|
"""delarte - ArteTV downloader."""
|
2022-12-06 00:16:16 +00:00
|
|
|
|
2022-12-08 21:39:46 +00:00
|
|
|
__version__ = "0.1"
|
2022-12-27 07:54:14 +00:00
|
|
|
|
2023-01-08 19:04:18 +00:00
|
|
|
from .error import *
|
|
|
|
from .model import *
|
|
|
|
|
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
def fetch_program_sources(url, http):
|
2023-01-24 07:27:37 +00:00
|
|
|
"""Fetch program sources listed on given ArteTV page."""
|
|
|
|
from .www import iter_programs
|
|
|
|
|
|
|
|
return [
|
|
|
|
ProgramSource(
|
|
|
|
program,
|
|
|
|
player_config_url,
|
|
|
|
)
|
2023-02-13 08:35:33 +00:00
|
|
|
for program, player_config_url in iter_programs(url, http)
|
2023-01-24 07:27:37 +00:00
|
|
|
]
|
2023-01-08 19:04:18 +00:00
|
|
|
|
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
def fetch_rendition_sources(program_sources, http):
|
2023-01-24 07:27:37 +00:00
|
|
|
"""Fetch renditions for given programs."""
|
|
|
|
from itertools import groupby
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
from .api import iter_renditions
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
sources = [
|
|
|
|
RenditionSource(
|
|
|
|
program,
|
|
|
|
rendition,
|
|
|
|
protocol,
|
|
|
|
program_index_url,
|
2022-12-27 07:54:14 +00:00
|
|
|
)
|
2023-01-24 07:27:37 +00:00
|
|
|
for program, player_config_url in program_sources
|
|
|
|
for rendition, protocol, program_index_url in iter_renditions(
|
|
|
|
program.id,
|
|
|
|
player_config_url,
|
2023-02-13 08:35:33 +00:00
|
|
|
http,
|
2023-01-24 07:27:37 +00:00
|
|
|
)
|
|
|
|
]
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
descriptors = list({(s.rendition.code, s.rendition.label) for s in sources})
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
descriptors.sort()
|
|
|
|
for code, group in groupby(descriptors, key=lambda t: t[0]):
|
|
|
|
labels_for_code = [t[1] for t in group]
|
|
|
|
if len(labels_for_code) != 1:
|
|
|
|
raise UnexpectedError("MULTIPLE_RENDITION_LABELS", code, labels_for_code)
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
return sources
|
2023-01-09 18:30:46 +00:00
|
|
|
|
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
def fetch_variant_sources(renditions_sources, http):
|
2023-01-24 07:27:37 +00:00
|
|
|
"""Fetch variants for given renditions."""
|
|
|
|
from itertools import groupby
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
from .hls import iter_variants
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
sources = [
|
|
|
|
VariantSource(
|
|
|
|
program,
|
|
|
|
rendition,
|
|
|
|
variant,
|
|
|
|
VariantSource.VideoMedia(*video),
|
|
|
|
VariantSource.AudioMedia(*audio),
|
|
|
|
VariantSource.SubtitlesMedia(*subtitles) if subtitles else None,
|
|
|
|
)
|
|
|
|
for program, rendition, protocol, program_index_url in renditions_sources
|
|
|
|
for variant, video, audio, subtitles in iter_variants(
|
2023-02-13 08:35:33 +00:00
|
|
|
protocol, program_index_url, http
|
2023-01-24 07:27:37 +00:00
|
|
|
)
|
|
|
|
]
|
|
|
|
|
|
|
|
descriptors = list(
|
|
|
|
{(s.variant.code, s.video_media.track.frame_rate) for s in sources}
|
|
|
|
)
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
descriptors.sort()
|
|
|
|
for code, group in groupby(descriptors, key=lambda t: t[0]):
|
|
|
|
frame_rates_for_code = [t[1] for t in group]
|
|
|
|
if len(frame_rates_for_code) != 1:
|
|
|
|
raise UnexpectedError(
|
|
|
|
"MULTIPLE_RENDITION_FRAME_RATES", code, frame_rates_for_code
|
|
|
|
)
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
return sources
|
2023-01-09 18:30:46 +00:00
|
|
|
|
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
def fetch_targets(variant_sources, http, **naming_options):
|
2023-01-24 07:27:37 +00:00
|
|
|
"""Compile download targets for given variants."""
|
|
|
|
from .hls import fetch_mp4_media, fetch_vtt_media
|
|
|
|
from .naming import file_name_builder
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
build_file_name = file_name_builder(**naming_options)
|
|
|
|
|
|
|
|
targets = [
|
|
|
|
Target(
|
|
|
|
Target.VideoInput(
|
|
|
|
video_media.track,
|
2023-02-13 08:35:33 +00:00
|
|
|
fetch_mp4_media(video_media.track_index_url, http),
|
2023-01-24 07:27:37 +00:00
|
|
|
),
|
|
|
|
Target.AudioInput(
|
|
|
|
audio_media.track,
|
2023-02-13 08:35:33 +00:00
|
|
|
fetch_mp4_media(audio_media.track_index_url, http),
|
2023-01-24 07:27:37 +00:00
|
|
|
),
|
|
|
|
(
|
|
|
|
Target.SubtitlesInput(
|
|
|
|
subtitles_media.track,
|
2023-02-13 08:35:33 +00:00
|
|
|
fetch_vtt_media(subtitles_media.track_index_url, http),
|
2023-01-24 07:27:37 +00:00
|
|
|
)
|
|
|
|
if subtitles_media
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
(program.title, program.subtitle) if program.subtitle else program.title,
|
|
|
|
build_file_name(program, rendition, variant),
|
|
|
|
)
|
|
|
|
for program, rendition, variant, video_media, audio_media, subtitles_media in variant_sources
|
|
|
|
]
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
return targets
|
2023-01-09 18:30:46 +00:00
|
|
|
|
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
def download_targets(targets, http, on_progress):
|
2023-01-24 07:27:37 +00:00
|
|
|
"""Download given target."""
|
|
|
|
import os
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
from .download import download_mp4_media, download_vtt_media
|
|
|
|
from .muxing import mux_target
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
for target in targets:
|
2023-01-25 07:53:25 +00:00
|
|
|
output_path = f"{target.output}.mkv"
|
|
|
|
|
|
|
|
if os.path.isfile(output_path):
|
|
|
|
print(f"Skipping {output_path!r}")
|
|
|
|
continue
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
video_path = target.output + ".video.mp4"
|
|
|
|
audio_path = target.output + ".audio.mp4"
|
|
|
|
subtitles_path = target.output + ".srt"
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
download_mp4_media(target.video_input.url, video_path, http, on_progress)
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-02-13 08:35:33 +00:00
|
|
|
download_mp4_media(target.audio_input.url, audio_path, http, on_progress)
|
2023-01-09 18:30:46 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
if target.subtitles_input:
|
|
|
|
download_vtt_media(
|
2023-02-13 08:35:33 +00:00
|
|
|
target.subtitles_input.url, subtitles_path, http, on_progress
|
2023-01-24 07:27:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
mux_target(
|
|
|
|
target._replace(
|
|
|
|
video_input=target.video_input._replace(url=video_path),
|
|
|
|
audio_input=target.audio_input._replace(url=audio_path),
|
|
|
|
subtitles_input=(
|
|
|
|
target.subtitles_input._replace(url=subtitles_path)
|
|
|
|
if target.subtitles_input
|
|
|
|
else None
|
|
|
|
),
|
|
|
|
),
|
|
|
|
on_progress,
|
|
|
|
)
|
2022-12-27 07:54:14 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
if os.path.isfile(subtitles_path):
|
|
|
|
os.unlink(subtitles_path)
|
2022-12-27 07:54:14 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
if os.path.isfile(audio_path):
|
|
|
|
os.unlink(audio_path)
|
2023-01-08 19:04:18 +00:00
|
|
|
|
2023-01-24 07:27:37 +00:00
|
|
|
if os.path.isfile(video_path):
|
|
|
|
os.unlink(video_path)
|