#!/usr/bin/env bash set -Eeuo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" # shellcheck source=lib.sh . "${SCRIPT_DIR}/lib.sh" LOCK_FILE="${RKMEDIA_ROOT}/upstream-deb.lock.json" WORK_DIR="${RKMEDIA_ROOT}/out/repack-work" OUTPUT_DIR="${RKMEDIA_ROOT}/out" CACHE_DIR="${RKMEDIA_ROOT}/out/downloads" INPUT_DEB="" PACKAGE_VERSION="20260721.1741+rk1.1" usage() { cat <<'EOF' Usage: repack-upstream-deb.sh [options] --deb FILE Use an existing upstream .deb instead of downloading --work-dir DIR Temporary extraction directory --output-dir DIR Repacked package output directory --cache-dir DIR Download cache directory --version VERSION Output Debian package version EOF } while (($#)); do case "$1" in --deb) [[ $# -ge 2 ]] || die "--deb requires a file"; INPUT_DEB="$2"; shift 2 ;; --work-dir) [[ $# -ge 2 ]] || die "--work-dir requires a directory"; WORK_DIR="$2"; shift 2 ;; --output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;; --cache-dir) [[ $# -ge 2 ]] || die "--cache-dir requires a directory"; CACHE_DIR="$2"; shift 2 ;; --version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "unknown argument: $1" ;; esac done for command_name in curl dpkg-deb file jq readelf sha256sum stat; do need_command "${command_name}" done [[ -f "${LOCK_FILE}" ]] || die "upstream asset lock is missing: ${LOCK_FILE}" jq -e '.schema_version == 1 and (.asset_sha256 | test("^[0-9a-f]{64}$"))' "${LOCK_FILE}" >/dev/null \ || die "invalid upstream asset lock" WORK_DIR="$(absolute_path "${WORK_DIR}")" OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")" CACHE_DIR="$(absolute_path "${CACHE_DIR}")" mkdir -p -- "${WORK_DIR}" "${OUTPUT_DIR}" "${CACHE_DIR}" asset_name="$(jq -er '.asset_name' "${LOCK_FILE}")" asset_url="$(jq -er '.asset_url' "${LOCK_FILE}")" expected_size="$(jq -er '.asset_size' "${LOCK_FILE}")" expected_sha256="$(jq -er '.asset_sha256' "${LOCK_FILE}")" if [[ -z "${INPUT_DEB}" ]]; then INPUT_DEB="${CACHE_DIR}/${asset_name}" if [[ ! -f "${INPUT_DEB}" ]]; then partial="${INPUT_DEB}.partial" rm -f -- "${partial}" log "downloading pinned upstream Kodi package" curl -L --fail --show-error --output "${partial}" "${asset_url}" mv -- "${partial}" "${INPUT_DEB}" fi fi INPUT_DEB="$(absolute_path "${INPUT_DEB}")" [[ -f "${INPUT_DEB}" ]] || die "upstream package not found: ${INPUT_DEB}" actual_size="$(stat -c '%s' "${INPUT_DEB}")" actual_sha256="$(sha256sum "${INPUT_DEB}" | awk '{print $1}')" [[ "${actual_size}" == "${expected_size}" ]] || die "upstream package size mismatch" [[ "${actual_sha256}" == "${expected_sha256}" ]] || die "upstream package SHA-256 mismatch" [[ "$(dpkg-deb --field "${INPUT_DEB}" Package)" == "$(jq -er '.package' "${LOCK_FILE}")" ]] \ || die "upstream package name mismatch" [[ "$(dpkg-deb --field "${INPUT_DEB}" Version)" == "$(jq -er '.package_version' "${LOCK_FILE}")" ]] \ || die "upstream package version mismatch" [[ "$(dpkg-deb --field "${INPUT_DEB}" Architecture)" == "arm64" ]] \ || die "upstream package is not arm64" STAGE_DIR="${WORK_DIR}/stage" reset_child_dir "${WORK_DIR}" "${STAGE_DIR}" dpkg-deb --extract "${INPUT_DEB}" "${STAGE_DIR}" local_bin="${STAGE_DIR}/usr/local/bin" local_lib="${STAGE_DIR}/usr/local/lib" runtime_bin="${STAGE_DIR}/opt/rkmedia/bin" runtime_lib="${STAGE_DIR}/opt/rkmedia/lib" mkdir -p -- "${runtime_bin}" "${runtime_lib}" for program in ffmpeg ffprobe; do [[ -x "${local_bin}/${program}" ]] || die "upstream package is missing ${program}" mv -- "${local_bin}/${program}" "${runtime_bin}/${program}" done if [[ -x "${local_bin}/di-edid-decode" ]]; then mv -- "${local_bin}/di-edid-decode" "${runtime_bin}/di-edid-decode" fi shopt -s nullglob runtime_libraries=( "${local_lib}"/libdav1d.so* "${local_lib}"/librga.so* "${local_lib}"/librockchip_mpp.so* "${local_lib}"/librockchip_vpu.so* "${local_lib}"/aarch64-linux-gnu/libdisplay-info.so* ) ((${#runtime_libraries[@]} > 0)) || die "no Rockchip runtime libraries found in upstream package" mv -- "${runtime_libraries[@]}" "${runtime_lib}/" shopt -u nullglob find "${local_lib}" -type f \( -name '*.a' -o -name '*.la' \) -delete rm -rf -- "${local_lib}/pkgconfig" "${local_lib}/aarch64-linux-gnu/pkgconfig" rm -f -- \ "${STAGE_DIR}/usr/lib/systemd/system/kodi.service" \ "${STAGE_DIR}/usr/lib/systemd/system/kodi-pulse.service" \ "${STAGE_DIR}/usr/lib/systemd/system/pulseaudio.service" cp -a -- "${RKMEDIA_ROOT}/packaging/rootfs/." "${STAGE_DIR}/" mkdir -p -- "${STAGE_DIR}/usr/bin" "${STAGE_DIR}/usr/share/doc/rk1-media-stack" ln -sfn /opt/rkmedia/bin/ffmpeg-rk "${STAGE_DIR}/usr/bin/ffmpeg-rk" ln -sfn /opt/rkmedia/bin/ffprobe-rk "${STAGE_DIR}/usr/bin/ffprobe-rk" ln -sfn /opt/rkmedia/bin/kodi-rk "${STAGE_DIR}/usr/bin/kodi-rk" install -m 0644 "${LOCK_FILE}" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json" [[ -x "${STAGE_DIR}/usr/local/bin/kodi" ]] || die "Kodi launcher was not retained at its compiled prefix" [[ -x "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" ]] || die "Kodi GBM binary is missing" for soname in librga.so.2 libdav1d.so.7 librockchip_mpp.so.1 libdisplay-info.so.4; do [[ -e "${runtime_lib}/${soname}" ]] || die "relocated runtime is missing ${soname}" readelf -d "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" | grep -Fq "Shared library: [${soname}]" \ || die "Kodi binary does not declare expected dependency ${soname}" done if find "${STAGE_DIR}" -type f -name '*.a' -print -quit | grep -q .; then die "static library remained after repack" fi if find "${STAGE_DIR}/usr/local/lib" -type d -name pkgconfig -print -quit | grep -q .; then die "pkg-config development directory remained after repack" fi "${SCRIPT_DIR}/package-media.sh" \ --stage "${STAGE_DIR}" \ --output-dir "${OUTPUT_DIR}" \ --version "${PACKAGE_VERSION}" \ --depends-from-deb "${INPUT_DEB}" log "repack complete; authoritative input SHA-256: ${actual_sha256}"