|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +BASE_DIR="/var/www/releases" |
| 4 | +BASE_URL="https://releases.libreelec.tv" |
| 5 | +FORCE="no" |
| 6 | +COMPRESS="yes" |
| 7 | +COPY_TO_MAIN="no" |
| 8 | +DEVICES="RPi2 RPi4 RPi5" |
| 9 | + |
| 10 | +DESCRIPTION="A fast and user-friendly Kodi mediacenter distribution" |
| 11 | + |
| 12 | +usage() |
| 13 | +{ |
| 14 | + cat << EOF |
| 15 | +usage: $0 [options...] VERSION |
| 16 | + -d devices comma separated list of devices (default: RPi2,RPi4,RPi5) |
| 17 | + -f force overwriting existing versions (default: skip extraction) |
| 18 | + -r release this version (create pinn/os_list.json) |
| 19 | + -C disable image compression (default: create .tar.xz images) |
| 20 | + -D directory base output directory (default: /var/www/releases) |
| 21 | + -U url base URL (default: https://releases.libreelec.tv) |
| 22 | +EOF |
| 23 | +} |
| 24 | + |
| 25 | +while getopts d:frCD:U: opt; do |
| 26 | + case "${opt}" in |
| 27 | + d) DEVICES=$(echo "${OPTARG}" | tr ',' ' ');; |
| 28 | + f) FORCE="yes";; |
| 29 | + r) COPY_TO_MAIN="yes";; |
| 30 | + C) COMPRESS="no";; |
| 31 | + D) BASE_DIR="${OPTARG}";; |
| 32 | + U) BASE_URL="${OPTARG}";; |
| 33 | + \?) usage; exit 1;; |
| 34 | + esac |
| 35 | +done |
| 36 | + |
| 37 | +shift $((OPTIND - 1)) |
| 38 | + |
| 39 | +if [ $# -ne 1 ]; then |
| 40 | + usage |
| 41 | + exit 1 |
| 42 | +fi |
| 43 | + |
| 44 | +VERSION="$1" |
| 45 | + |
| 46 | +set -e |
| 47 | + |
| 48 | +PINN_DIR="${BASE_DIR}/pinn" |
| 49 | +PINN_VERSION_DIR="${PINN_DIR}/${VERSION}" |
| 50 | +ICON_FILE="LibreELEC.png" |
| 51 | +MARKETING_FILE="marketing.tar" |
| 52 | + |
| 53 | +PINN_ICON_FILE="${PINN_DIR}/${ICON_FILE}" |
| 54 | +PINN_MARKETING_FILE="${PINN_DIR}/${MARKETING_FILE}" |
| 55 | + |
| 56 | +if [ "${COMPRESS}" = "yes" ]; then |
| 57 | + SYSTEM_TAR="System.tar.xz" |
| 58 | +else |
| 59 | + SYSTEM_TAR="System.tar" |
| 60 | +fi |
| 61 | + |
| 62 | +base_setup() |
| 63 | +{ |
| 64 | + if [ ! -d "${PINN_DIR}" ]; then |
| 65 | + mkdir -p "${PINN_DIR}" |
| 66 | + fi |
| 67 | + |
| 68 | + if [ ! -e "${PINN_ICON_FILE}" ]; then |
| 69 | + # copy icon from old noobs release |
| 70 | + if [ -e "${BASE_DIR}/noobs/LibreELEC_RPi5/LibreELEC_RPi5.png" ]; then |
| 71 | + cp "${BASE_DIR}/noobs/LibreELEC_RPi5/LibreELEC_RPi5.png" "${PINN_ICON_FILE}" |
| 72 | + else |
| 73 | + echo "error: no LibreELEC.png icon file found, copy it to ${PINN_ICON_FILE}" |
| 74 | + exit 1 |
| 75 | + fi |
| 76 | + fi |
| 77 | + |
| 78 | + if [ ! -e "${PINN_MARKETING_FILE}" ]; then |
| 79 | + # copy marketing.tar from old noobs release |
| 80 | + if [ -e "${BASE_DIR}/noobs/LibreELEC_RPi5/marketing.tar" ]; then |
| 81 | + cp "${BASE_DIR}/noobs/LibreELEC_RPi5/marketing.tar" "${PINN_MARKETING_FILE}" |
| 82 | + else |
| 83 | + echo "error: no marketing.tar file found, copy it to ${PINN_MARKETING_FILE}" |
| 84 | + exit 1 |
| 85 | + fi |
| 86 | + fi |
| 87 | +} |
| 88 | + |
| 89 | +prepare_common() |
| 90 | +{ |
| 91 | + PINN_TEMP=$(mktemp -d --tmpdir pinn.XXXXXXXXXX) |
| 92 | + |
| 93 | + trap cleanup 0 |
| 94 | +} |
| 95 | + |
| 96 | +cleanup() |
| 97 | +{ |
| 98 | + if [ -n "${PINN_TEMP}" ]; then |
| 99 | + rm -rf "${PINN_TEMP}" |
| 100 | + fi |
| 101 | +} |
| 102 | + |
| 103 | +prepare_version() |
| 104 | +{ |
| 105 | + if [ ! -d "${PINN_VERSION_DIR}" ]; then |
| 106 | + mkdir -p "${PINN_VERSION_DIR}" |
| 107 | + fi |
| 108 | +} |
| 109 | + |
| 110 | +# args: update-tar-file |
| 111 | +create_system_tar() |
| 112 | +{ |
| 113 | + update_dir="${PINN_TEMP}/update-tar" |
| 114 | + rm -rf "${update_dir}" |
| 115 | + mkdir -p "${update_dir}" |
| 116 | + tar xf "$1" --strip-components=1 -C "${update_dir}" |
| 117 | + |
| 118 | + system_dir="${PINN_TEMP}/system" |
| 119 | + |
| 120 | + rm -rf "${system_dir}" |
| 121 | + rm -f "${PINN_TEMP}/${SYSTEM_TAR}" |
| 122 | + mkdir -p "${system_dir}" |
| 123 | + ( |
| 124 | + cd "${update_dir}" |
| 125 | + mv "target/KERNEL" "${system_dir}/kernel.img" |
| 126 | + mv "target/SYSTEM" "${system_dir}" |
| 127 | + mv "3rdparty/bootloader/"* "${system_dir}" |
| 128 | + |
| 129 | + cd "${system_dir}" |
| 130 | + tar -acf "${PINN_TEMP}/${SYSTEM_TAR}" --owner=root:0 --group=root:0 -- * |
| 131 | + ) |
| 132 | + SYSTEM_SIZE=$(du -s -B 1000000 "${system_dir}" | awk '{print $1}' ) |
| 133 | + rm -rf "${system_dir}" "${update_dir}" |
| 134 | +} |
| 135 | + |
| 136 | +# args: device |
| 137 | +get_supported_models() |
| 138 | +{ |
| 139 | + case "$1" in |
| 140 | + RPi) echo '[ "Pi Model", "Compute Module Rev", "Pi Zero" ]';; |
| 141 | + RPi2) echo '[ "Pi 2", "Pi 3", "Pi Compute Module 3" ]';; |
| 142 | + RPi4) echo '[ "Pi 4" ]';; |
| 143 | + RPi5) echo '[ "Pi 5" ]';; |
| 144 | + *) |
| 145 | + echo "unsupported device $1" >&2 |
| 146 | + exit 1 |
| 147 | + ;; |
| 148 | + esac |
| 149 | +} |
| 150 | + |
| 151 | +# args: device |
| 152 | +get_target_models() |
| 153 | +{ |
| 154 | + case "$1" in |
| 155 | + RPi) echo "RPi 0/1";; |
| 156 | + RPi2) echo "RPi 2/3";; |
| 157 | + RPi4) echo "RPi 4";; |
| 158 | + RPi5) echo "RPi 5";; |
| 159 | + *) |
| 160 | + echo "unsupported device $1" >&2 |
| 161 | + exit 1 |
| 162 | + ;; |
| 163 | + esac |
| 164 | +} |
| 165 | + |
| 166 | +# args: update_tar |
| 167 | +get_release_date() |
| 168 | +{ |
| 169 | + stat -c %y "$1" | awk '{print $1}' |
| 170 | +} |
| 171 | + |
| 172 | +# args: device, dir, release date |
| 173 | +create_os_json() |
| 174 | +{ |
| 175 | + cat > "$2/os.json" << EOF |
| 176 | +{ |
| 177 | + "name": "LibreELEC_$1", |
| 178 | + "version": "${VERSION}", |
| 179 | + "release_date": "$3", |
| 180 | + "description": "${DESCRIPTION} for $(get_target_models "$1")", |
| 181 | + "username": "root", |
| 182 | + "password": "libreelec", |
| 183 | + "supports_backup": true, |
| 184 | + "group": "Media", |
| 185 | + "url": "https://libreelec.tv/", |
| 186 | + "supported_models": $(get_supported_models "$1") |
| 187 | +} |
| 188 | +EOF |
| 189 | +} |
| 190 | + |
| 191 | +# args: dir, systen size, system sha512 |
| 192 | +create_partitions_json() |
| 193 | +{ |
| 194 | +cat > "$1/partitions.json" << EOF |
| 195 | +{ |
| 196 | + "partitions": [ |
| 197 | + { |
| 198 | + "label": "System", |
| 199 | + "filesystem_type": "FAT", |
| 200 | + "partition_size_nominal": 512, |
| 201 | + "want_maximised": false, |
| 202 | + "uncompressed_tarball_size": $2, |
| 203 | + "sha512sum": "$3", |
| 204 | + "mkfs_options": "" |
| 205 | + }, |
| 206 | + { |
| 207 | + "label": "Storage", |
| 208 | + "empty_fs": true, |
| 209 | + "filesystem_type": "ext4", |
| 210 | + "partition_size_nominal": 512, |
| 211 | + "want_maximised": true, |
| 212 | + "uncompressed_tarball_size": 1, |
| 213 | + "mkfs_options": "-m 0" |
| 214 | + } |
| 215 | + ] |
| 216 | +} |
| 217 | +EOF |
| 218 | +} |
| 219 | + |
| 220 | +# args device, dir |
| 221 | +create_partition_setup() |
| 222 | +{ |
| 223 | + case "$1" in |
| 224 | + RPi5) cmdline_args="quiet console=ttyAMA10,115200 console=tty0";; |
| 225 | + *) cmdline_args="quiet console=tty0";; |
| 226 | + esac |
| 227 | + |
| 228 | + cat > "$2/partition_setup.sh" <<EOF |
| 229 | +#!/bin/sh |
| 230 | +set -ex |
| 231 | +
|
| 232 | +if [ -z "\${part1}" ] || [ -z "\${part2}" ] || [ -z "\${id1}" ] || [ -z "\${id2}" ]; then |
| 233 | + echo "error: missing environment variables id1/2 or part1/2" |
| 234 | + exit 1 |
| 235 | +fi |
| 236 | +
|
| 237 | +mkdir -p "/tmp/le-boot" |
| 238 | +mount "\${part1}" "/tmp/le-boot" |
| 239 | +if [ -e "/tmp/le-boot/cmdline.txt" ]; then |
| 240 | + sed -e 's/boot=[^ ]*/boot='"\${id1}/" -e 's/disk=[^ ]*/disk='"\${id2}/" -i "/tmp/le-boot/cmdline.txt" |
| 241 | +else |
| 242 | + echo "boot=\${id1} disk=\${id2} ${cmdline_args}" > "/tmp/le-boot/cmdline.txt" |
| 243 | +fi |
| 244 | +umount "/tmp/le-boot" |
| 245 | +rmdir "/tmp/le-boot" |
| 246 | +EOF |
| 247 | +} |
| 248 | + |
| 249 | +# args: device, update tar, dest dir, release date |
| 250 | +extract_device() |
| 251 | +{ |
| 252 | + device="$1" |
| 253 | + update_tar="$2" |
| 254 | + dest_dir="$3" |
| 255 | + release_date="$4" |
| 256 | + |
| 257 | + create_system_tar "${update_tar}" |
| 258 | + |
| 259 | + mkdir -p "${dest_dir}" |
| 260 | + |
| 261 | + system_tar_sha512=$(sha512sum "${PINN_TEMP}/${SYSTEM_TAR}" | awk '{print $1}') |
| 262 | + mv "${PINN_TEMP}/${SYSTEM_TAR}" "${dest_dir}" |
| 263 | + cp "${PINN_ICON_FILE}" "${PINN_MARKETING_FILE}" "${dest_dir}" |
| 264 | + |
| 265 | + create_os_json "${device}" "${dest_dir}" "${release_date}" |
| 266 | + create_partitions_json "${dest_dir}" "${SYSTEM_SIZE}" "${system_tar_sha512}" |
| 267 | + create_partition_setup "${device}" "${dest_dir}" |
| 268 | +} |
| 269 | + |
| 270 | +# args: device, release date, download size |
| 271 | +get_os_entry() |
| 272 | +{ |
| 273 | + url="${BASE_URL}/pinn/${VERSION}/${DEVICE}" |
| 274 | + cat << EOF |
| 275 | + { |
| 276 | + "os_name": "LibreELEC_$1", |
| 277 | + "description": "${DESCRIPTION} for $(get_target_models "$1")", |
| 278 | + "release_date": "$2", |
| 279 | + "version": "${VERSION}", |
| 280 | + "supported_models": $(get_supported_models "$1"), |
| 281 | + "url": "https://libreelec.tv/", |
| 282 | + "group": "Media", |
| 283 | + "download_size": $3, |
| 284 | + "os_info": "${url}/os.json", |
| 285 | + "partitions_info": "${url}/partitions.json", |
| 286 | + "icon": "${url}/${ICON_FILE}", |
| 287 | + "marketing_info": "${url}/${MARKETING_FILE}", |
| 288 | + "partition_setup": "${url}/partition_setup.sh", |
| 289 | + "tarballs": [ |
| 290 | + "${url}/${SYSTEM_TAR}" |
| 291 | + ], |
| 292 | + "nominal_size": 1024 |
| 293 | + } |
| 294 | +EOF |
| 295 | +} |
| 296 | +LIST_SEP=", |
| 297 | +" |
| 298 | + |
| 299 | +if [ "${COPY_TO_MAIN}" = "yes" ]; then |
| 300 | + echo "Releasing ${VERSION}" |
| 301 | +else |
| 302 | + echo "Creating ${VERSION} PINN files" |
| 303 | +fi |
| 304 | + |
| 305 | +base_setup |
| 306 | +prepare_common |
| 307 | +prepare_version |
| 308 | + |
| 309 | +OS_LIST="" |
| 310 | + |
| 311 | +EXTRACTED_FILES="no" |
| 312 | + |
| 313 | +for DEVICE in ${DEVICES}; do |
| 314 | + UPDATE_TAR="${BASE_DIR}/LibreELEC-${DEVICE}.aarch64-${VERSION}.tar" |
| 315 | + if [ ! -e "${UPDATE_TAR}" ]; then |
| 316 | + UPDATE_TAR="${BASE_DIR}/LibreELEC-${DEVICE}.arm-${VERSION}.tar" |
| 317 | + if [ ! -e "${UPDATE_TAR}" ]; then |
| 318 | + echo "error: no update tar for $DEVICE available, exiting" |
| 319 | + exit 1 |
| 320 | + fi |
| 321 | + fi |
| 322 | + |
| 323 | + release_date=$(get_release_date "${UPDATE_TAR}") |
| 324 | + |
| 325 | + PINN_DEVICE_DIR="${PINN_VERSION_DIR}/${DEVICE}" |
| 326 | + |
| 327 | + need_extract="yes" |
| 328 | + if [ -d "${PINN_DEVICE_DIR}" ]; then |
| 329 | + if [ "${FORCE}" = "yes" ]; then |
| 330 | + echo "fored re-extract of ${DEVICE}" |
| 331 | + rm -rf "${PINN_DEVICE_DIR}" |
| 332 | + else |
| 333 | + need_extract="no" |
| 334 | + echo "skipping extract of ${DEVICE}" |
| 335 | + fi |
| 336 | + else |
| 337 | + echo "extracting ${DEVICE}" |
| 338 | + fi |
| 339 | + |
| 340 | + if [ "${need_extract}" = "yes" ]; then |
| 341 | + extract_device "${DEVICE}" "${UPDATE_TAR}" "${PINN_DEVICE_DIR}" "${release_date}" |
| 342 | + EXTRACTED_FILES="yes" |
| 343 | + fi |
| 344 | + |
| 345 | + if [ ! -e "${PINN_DEVICE_DIR}/${SYSTEM_TAR}" ]; then |
| 346 | + echo "error: ${PINN_DEVICE_DIR}/${SYSTEM_TAR} is missing" |
| 347 | + exit 1 |
| 348 | + fi |
| 349 | + |
| 350 | + download_size=$(stat -c %s "${PINN_DEVICE_DIR}/${SYSTEM_TAR}" | awk '{print $1}') |
| 351 | + |
| 352 | + os_entry=$(get_os_entry "${DEVICE}" "${release_date}" "${download_size}") |
| 353 | + |
| 354 | + if [ -n "${OS_LIST}" ]; then |
| 355 | + OS_LIST="${OS_LIST}${LIST_SEP}" |
| 356 | + fi |
| 357 | + |
| 358 | + OS_LIST="${OS_LIST}${os_entry}" |
| 359 | +done |
| 360 | + |
| 361 | +if [ -z "${OS_LIST}" ]; then |
| 362 | + echo "error: empty OS list" |
| 363 | + exit 1 |
| 364 | +fi |
| 365 | + |
| 366 | +if [ "${EXTRACTED_FILES}" = "yes" ] || [ ! -e "${PINN_VERSION_DIR}/os_list.json" ]; then |
| 367 | + cat > "${PINN_VERSION_DIR}/os_list.json" << EOF |
| 368 | +{ |
| 369 | + "os_list": [ |
| 370 | +${OS_LIST} |
| 371 | + ] |
| 372 | +} |
| 373 | +EOF |
| 374 | + |
| 375 | + echo "wrote ${VERSION} OS list to ${PINN_VERSION_DIR}/os_list.json" |
| 376 | +fi |
| 377 | + |
| 378 | +if [ "${COPY_TO_MAIN}" = "yes" ]; then |
| 379 | + cp "${PINN_VERSION_DIR}/os_list.json" "${PINN_DIR}/os_list.json" |
| 380 | + echo "wrote release OS list to ${PINN_DIR}/os_list.json" |
| 381 | +fi |
| 382 | + |
| 383 | +echo "done." |
0 commit comments