#!/usr/bin/env bash # Resolve pipeline input from CLI arg, sources.list index, or env SOURCE_URL. set -euo pipefail # shellcheck disable=SC1091 source "$(dirname "${BASH_SOURCE[0]}")/common.sh" SOURCE_URL="" SOURCE_FMT="" SOURCE_VIDEO_STREAM="v:0" SOURCE_AUDIO_STREAM="a:0" AUDIO_INPUT_ARGS=() USE_YTDLP=0 is_page_url() { local url="$1" [[ "$url" =~ ^https?:// ]] || return 1 [[ "$url" =~ \.(m3u8|mpd|mp4|mkv|mov|ts|flv)(\?|$) ]] && return 1 [[ -f "$url" ]] && return 1 return 0 } resolve_source() { local arg="${1:-}" SOURCE_URL="${SOURCE_URL:-}" SOURCE_FMT="" if [[ -n "${SOURCE_URL}" ]]; then : elif [[ -n "${arg}" && -f "${arg}" && "${arg}" != *".list" ]]; then SOURCE_URL="${arg}" elif [[ -n "${arg}" && "${arg}" =~ ^https?:// ]]; then SOURCE_URL="${arg}" elif [[ -n "${arg}" && "${arg}" =~ ^[0-9]+$ ]]; then if [[ ! -f "${SOURCES_LIST}" ]]; then die "sources list missing: ${SOURCES_LIST} (copy sources.example.list)" fi local line line="$(sed -n "${arg}p" "${SOURCES_LIST}" | sed 's/#.*//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" [[ -n "${line}" ]] || die "no source on line ${arg} of ${SOURCES_LIST}" SOURCE_URL="$(awk '{print $1}' <<<"${line}")" SOURCE_FMT="$(awk '{print $2}' <<<"${line}")" elif [[ -f "${SOURCES_LIST}" ]]; then local line line="$(grep -v '^[[:space:]]*#' "${SOURCES_LIST}" | grep -v '^[[:space:]]*$' | head -1)" SOURCE_URL="$(awk '{print $1}' <<<"${line}")" SOURCE_FMT="$(awk '{print $2}' <<<"${line}")" else die "usage: pass URL, file path, or 1-based index into ${SOURCES_LIST}" fi [[ -n "${SOURCE_URL}" ]] || die "empty source URL" if [[ -n "${AUDIO_SOURCE:-}" ]]; then read_rate_flag AUDIO_INPUT_ARGS=("${RE_FLAG[@]}" -thread_queue_size "${THREAD_QUEUE_SIZE}" -i "${AUDIO_SOURCE}") SOURCE_AUDIO_STREAM="a:0" fi if [[ "${YTDLP_ENABLED}" == "1" ]] && is_page_url "${SOURCE_URL}"; then USE_YTDLP=1 fi echo "${SOURCE_URL}" > "${ROOT}/.current_source" log "source: ${SOURCE_URL} (ytdlp=${USE_YTDLP})" } ytdlp_auth_args() { local auth="${ROOT}/config/yt-dlp.auth.env" if [[ -f "${auth}" ]]; then # shellcheck disable=SC1090 source "${auth}" fi local args=() [[ -n "${YTDLP_USERNAME:-}" ]] && args+=(--username "${YTDLP_USERNAME}") [[ -n "${YTDLP_PASSWORD:-}" ]] && args+=(--password "${YTDLP_PASSWORD}") [[ -n "${YTDLP_NETRC:-}" ]] && args+=(--netrc) [[ -n "${YTDLP_EXTRA_ARGS:-}" ]] && args+=(${YTDLP_EXTRA_ARGS}) printf '%s\n' "${args[@]}" } build_ytdlp_pipeline() { local fmt_args=() [[ -n "${SOURCE_FMT}" ]] && fmt_args=(-f "${SOURCE_FMT}") mapfile -t auth < <(ytdlp_auth_args) printf '%s\0' "${YTDLP_BIN}" \ -o - --no-part --no-live-from-start --no-write-subs --no-embed-subs \ --remux-video mkv --merge-output-format mkv --no-audio-multistreams \ --geo-bypass --ignore-errors --hls-use-mpegts \ "${auth[@]}" \ "${fmt_args[@]}" \ "${SOURCE_URL}" } # Returns ffmpeg input argv: either direct -i URL or pipe from yt-dlp. build_primary_input() { read_rate_flag if [[ "${USE_YTDLP}" == "1" ]]; then require_cmd "${YTDLP_BIN}" printf '%s\0' pipe printf '%s\0' -thread_queue_size "${THREAD_QUEUE_SIZE}" -i pipe:0 return fi local url="${SOURCE_URL}" if [[ "${url}" == *.jpg || "${url}" == *.jpeg || "${url}" == *.png ]]; then printf '%s\0' direct printf '%s\0' "${RE_FLAG[@]}" -thread_queue_size "${THREAD_QUEUE_SIZE}" \ -loop 1 -i "${url}" -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 return fi printf '%s\0' direct printf '%s\0' "${RE_FLAG[@]}" -thread_queue_size "${THREAD_QUEUE_SIZE}" \ -reconnect_on_network_error 1 -reconnect_streamed 1 -reconnect_delay_max 5 \ -i "${url}" }