diff --git a/apps/librarr/1.1.3/.env.sample b/apps/librarr/1.1.3/.env.sample new file mode 100644 index 000000000..545c7ab97 --- /dev/null +++ b/apps/librarr/1.1.3/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_HTTP=5050 +APP_DATA_DIR=./data/librarr +APP_EBOOK_DIR=./data/ebooks +APP_AUDIOBOOK_DIR=./data/audiobooks +APP_MANGA_DIR=./data/manga +CONTAINER_NAME=librarr-compose-check diff --git a/apps/librarr/1.1.3/data.yml b/apps/librarr/1.1.3/data.yml new file mode 100644 index 000000000..a92aed031 --- /dev/null +++ b/apps/librarr/1.1.3/data.yml @@ -0,0 +1,83 @@ +additionalProperties: + formFields: + - default: 5050 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: ./data/librarr + edit: true + envKey: APP_DATA_DIR + labelEn: Application data directory + labelZh: 应用数据目录 + label: + en: Application data directory + zh: 应用数据目录 + zh-Hant: 應用資料目錄 + ja: アプリケーションデータディレクトリ + ko: 애플리케이션 데이터 디렉터리 + ru: Каталог данных приложения + ms: Direktori data aplikasi + pt-br: Diretório de dados do aplicativo + required: true + type: text + - default: ./data/ebooks + edit: true + envKey: APP_EBOOK_DIR + labelEn: Ebook directory + labelZh: 电子书目录 + label: + en: Ebook directory + zh: 电子书目录 + zh-Hant: 電子書目錄 + ja: 電子書籍ディレクトリ + ko: 전자책 디렉터리 + ru: Каталог электронных книг + ms: Direktori e-buku + pt-br: Diretório de ebooks + required: true + type: text + - default: ./data/audiobooks + edit: true + envKey: APP_AUDIOBOOK_DIR + labelEn: Audiobook directory + labelZh: 有声书目录 + label: + en: Audiobook directory + zh: 有声书目录 + zh-Hant: 有聲書目錄 + ja: オーディオブックディレクトリ + ko: 오디오북 디렉터리 + ru: Каталог аудиокниг + ms: Direktori buku audio + pt-br: Diretório de audiolivros + required: true + type: text + - default: ./data/manga + edit: true + envKey: APP_MANGA_DIR + labelEn: Manga directory + labelZh: 漫画目录 + label: + en: Manga directory + zh: 漫画目录 + zh-Hant: 漫畫目錄 + ja: 漫画ディレクトリ + ko: 만화 디렉터리 + ru: Каталог манги + ms: Direktori manga + pt-br: Diretório de mangás + required: true + type: text diff --git a/apps/librarr/1.1.3/docker-compose.yml b/apps/librarr/1.1.3/docker-compose.yml new file mode 100644 index 000000000..145eeeadc --- /dev/null +++ b/apps/librarr/1.1.3/docker-compose.yml @@ -0,0 +1,39 @@ +services: + librarr: + image: "ghcr.io/jeremiahm37/librarr:v1.1.3" + container_name: ${CONTAINER_NAME} + restart: always + read_only: true + tmpfs: + - /tmp:size=512M + security_opt: + - no-new-privileges:true + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5050" + volumes: + - "${APP_DATA_DIR}:/data" + - "${APP_EBOOK_DIR}:/books/ebooks" + - "${APP_AUDIOBOOK_DIR}:/books/audiobooks" + - "${APP_MANGA_DIR}:/books/manga" + environment: + - LIBRARR_PORT=5050 + - LIBRARR_DB_PATH=/data/librarr.db + - EBOOK_DIR=/books/ebooks + - AUDIOBOOK_DIR=/books/audiobooks + - MANGA_DIR=/books/manga + - WISHLIST_CLEANUP_ENABLED=false + - OIDC_PROXY_HEADERS_ENABLED=false + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:5050/api/health"] + interval: 10s + timeout: 5s + retries: 9 + start_period: 15s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/librarr/1.1.3/scripts/init.sh b/apps/librarr/1.1.3/scripts/init.sh new file mode 100755 index 000000000..42c30dcfb --- /dev/null +++ b/apps/librarr/1.1.3/scripts/init.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +LIBRARR_UID=1000 +LIBRARR_GID=1000 + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value="" + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +reject_symlink_components() { + local path="$1" + local current="" + local component + IFS='/' read -r -a components <<< "${path#/}" + for component in "${components[@]}"; do + [[ -n "$component" ]] || continue + current="${current}/${component}" + [[ ! -L "$current" ]] || fail "$path must not contain symbolic-link components" + done +} + +prepare_media_dir() { + local key="$1" + local raw="" + local candidate="" + local resolved="" + local scope="" + local existed=false + + raw="$(read_env_value "$key")" + [[ -n "$raw" ]] || fail "$key must not be empty" + [[ "$raw" =~ ^[A-Za-z0-9._/\ -]+$ ]] || fail "$key contains unsupported characters" + + if [[ "$raw" == /* ]]; then + candidate="$raw" + scope="absolute" + else + candidate="${ROOT_DIR}/${raw#./}" + scope="application" + fi + + reject_symlink_components "$candidate" + resolved="$(realpath -m -- "$candidate")" || fail "Unable to resolve $key" + [[ "$resolved" != "/" ]] || fail "$key must not be the filesystem root" + if [[ "$scope" == "application" ]]; then + case "$resolved" in + "${ROOT_DIR}"/*) ;; + *) fail "Relative $key must stay inside the application version directory" ;; + esac + fi + [[ ! -e "$resolved" || -d "$resolved" ]] || fail "$key must be a directory" + [[ -d "$resolved" ]] && existed=true + + if [[ "$scope" == "absolute" && "$existed" == "true" ]]; then + local incompatible_owner="" + incompatible_owner="$(find "$resolved" -xdev \( ! -uid "$LIBRARR_UID" -o ! -gid "$LIBRARR_GID" \) -print -quit)" || \ + fail "Unable to inspect $key ownership" + [[ -z "$incompatible_owner" ]] || fail "Existing absolute $key tree must be owned by ${LIBRARR_UID}:${LIBRARR_GID}" + fi + + install -d -m 0750 -o "$LIBRARR_UID" -g "$LIBRARR_GID" -- "$resolved" || \ + fail "Unable to prepare $key" + local verified="" + verified="$(realpath -e -- "$resolved")" || fail "Unable to verify $key" + [[ "$verified" == "$resolved" ]] || fail "$key changed while it was being prepared" + if [[ "$scope" == "application" ]]; then + case "$verified" in + "${ROOT_DIR}"/*) ;; + *) fail "$key resolves outside the application version directory" ;; + esac + chown -R --no-dereference "$LIBRARR_UID:$LIBRARR_GID" "$verified" || \ + fail "Unable to set $key ownership" + elif [[ "$existed" == "false" ]]; then + chown "$LIBRARR_UID:$LIBRARR_GID" "$verified" || fail "Unable to set $key ownership" + fi + chmod 0750 "$verified" || fail "Unable to set $key permissions" + printf '%s\n' "$verified" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Librarr init must run as root to prepare media-directory ownership" + +data_dir="$(prepare_media_dir APP_DATA_DIR)" +ebook_dir="$(prepare_media_dir APP_EBOOK_DIR)" +audiobook_dir="$(prepare_media_dir APP_AUDIOBOOK_DIR)" +manga_dir="$(prepare_media_dir APP_MANGA_DIR)" + +[[ "$data_dir" != "$ebook_dir" && "$data_dir" != "$audiobook_dir" && "$data_dir" != "$manga_dir" && \ + "$ebook_dir" != "$audiobook_dir" && "$ebook_dir" != "$manga_dir" && "$audiobook_dir" != "$manga_dir" ]] || \ + fail "Librarr data and media directories must be distinct" diff --git a/apps/librarr/1.1.3/scripts/uninstall.sh b/apps/librarr/1.1.3/scripts/uninstall.sh new file mode 100755 index 000000000..201fab266 --- /dev/null +++ b/apps/librarr/1.1.3/scripts/uninstall.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Media directories contain user-managed files and are never deleted here. +exit 0 diff --git a/apps/librarr/1.1.3/scripts/upgrade.sh b/apps/librarr/1.1.3/scripts/upgrade.sh new file mode 100755 index 000000000..57b2d551a --- /dev/null +++ b/apps/librarr/1.1.3/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Librarr applies SQLite migrations in the upstream binary during startup. +exit 0 diff --git a/apps/librarr/README.md b/apps/librarr/README.md new file mode 100644 index 000000000..f4aed9d73 --- /dev/null +++ b/apps/librarr/README.md @@ -0,0 +1,48 @@ +## 产品介绍 + +**Librarr** 是一个面向电子书、有声书和漫画的自托管媒体管理器。它提供统一的 Web 界面,用于管理本地媒体库、愿望单、搜索和下载流程。 + +## 主要功能 + +- 分别管理电子书、有声书和漫画目录。 +- 维护愿望单、阅读记录、标签和作者监控。 +- 支持上传本地媒体文件并整理到对应媒体目录。 +- 可按需连接 qBittorrent、Audiobookshelf、Kavita、Komga 等外部服务。 + +## 访问说明 + +- 安装完成后打开 Web 端口。 +- 首次访问时注册管理员账号;首个用户会自动成为管理员。 +- 媒体目录默认为应用版本目录下的 `data/ebooks`、`data/audiobooks` 和 `data/manga`,也可在安装表单中改为其他宿主机路径。 + +## 数据与升级 + +- 用户、愿望单、设置和活动记录保存在应用数据目录中的 SQLite 数据库里。 +- 三类媒体文件保存在安装表单所选的宿主机目录中。 +- 应用限制为单实例,以避免多个实例争用同一数据目录。 +- 卸载脚本不会删除媒体目录。卸载或升级前请备份数据库卷和媒体目录。 +- `latest` 为浮动镜像,适合通过容器更新工具跟踪上游发布;固定版本用于可重复部署。 + +## 默认工作流 + +首次注册管理员后,可以添加和删除愿望单条目,也可以上传一个有效的电子书文件并在上传记录中查看。该流程可用于确认身份验证、SQLite 持久化、媒体目录写入和重启恢复均正常。 + +## 参考资料 + +- 项目仓库: +- 容器镜像: + +## Introduction + +Librarr is a self-hosted media manager for ebooks, audiobooks, and manga. It provides a unified Web interface for local libraries, wishlists, search, and download workflows. + +## Features + +- Manage separate ebook, audiobook, and manga directories. +- Maintain wishlists, reading history, tags, and monitored authors. +- Upload local media files and organize them into the selected directories. +- Optionally integrate qBittorrent, Audiobookshelf, Kavita, Komga, and other services. + +## First use + +Open the Web port and register the first account, which becomes the administrator. The application data path and three media paths can be selected during installation. diff --git a/apps/librarr/README_en.md b/apps/librarr/README_en.md new file mode 100644 index 000000000..c1ff427df --- /dev/null +++ b/apps/librarr/README_en.md @@ -0,0 +1,33 @@ +## Introduction + +**Librarr** is a self-hosted media manager for ebooks, audiobooks, and manga. It provides a unified Web interface for managing local libraries, wishlists, search, and download workflows. + +## Features + +- Manage separate ebook, audiobook, and manga directories. +- Maintain wishlists, reading history, tags, and monitored authors. +- Upload local media files and organize them into the selected directories. +- Optionally integrate qBittorrent, Audiobookshelf, Kavita, Komga, and other services. + +## First use + +- Open the Web port after installation. +- Register the administrator account on first use. The first user automatically becomes an administrator. +- Media directories default to `data/ebooks`, `data/audiobooks`, and `data/manga` under the application version directory. You can select other host paths in the installation form. + +## Data and upgrades + +- Users, wishlists, settings, and activity are stored in a SQLite database in the selected application data directory. +- Media files are stored in the three host directories selected during installation. +- This package is limited to one instance to prevent multiple installations from competing for the same data directory. +- The uninstall script does not remove media directories. Back up both the database volume and media directories before uninstalling or upgrading. +- `latest` is intentionally movable for users who track releases with container update tools; the numbered version provides a reproducible deployment. + +## Default workflow + +After registering the first administrator, add and remove a wishlist item, then upload a valid ebook and view its upload record. This exercises authentication, SQLite persistence, media-directory writes, and recovery after a restart. + +## Links + +- Project: +- Container image: diff --git a/apps/librarr/data.yml b/apps/librarr/data.yml new file mode 100644 index 000000000..d18d6e8a9 --- /dev/null +++ b/apps/librarr/data.yml @@ -0,0 +1,30 @@ +name: Librarr +tags: + - Media +title: 自托管的电子书、有声书和漫画管理器 +description: 自托管的电子书、有声书和漫画管理器 +additionalProperties: + key: librarr + name: Librarr + description: + en: A self-hosted manager for ebooks, audiobooks, and manga + zh: 自托管的电子书、有声书和漫画管理器 + zh-Hant: 自託管的電子書、有聲書和漫畫管理器 + ja: 電子書籍、オーディオブック、漫画向けのセルフホスト型管理ツール + ko: 전자책, 오디오북 및 만화를 위한 자체 호스팅 관리자 + ru: Самостоятельно размещаемый менеджер электронных и аудиокниг и манги + ms: Pengurus hos sendiri untuk e-buku, buku audio dan manga + pt-br: Gerenciador auto-hospedado de ebooks, audiolivros e mangás + tags: + - Media + shortDescZh: 自托管的电子书、有声书和漫画管理器 + shortDescEn: A self-hosted manager for ebooks, audiobooks, and manga + type: tool + crossVersionUpdate: true + limit: 1 + recommend: 0 + website: https://github.com/JeremiahM37/librarr + github: https://github.com/JeremiahM37/librarr + document: https://github.com/JeremiahM37/librarr#readme + architectures: + - amd64 diff --git a/apps/librarr/latest/.env.sample b/apps/librarr/latest/.env.sample new file mode 100644 index 000000000..545c7ab97 --- /dev/null +++ b/apps/librarr/latest/.env.sample @@ -0,0 +1,6 @@ +PANEL_APP_PORT_HTTP=5050 +APP_DATA_DIR=./data/librarr +APP_EBOOK_DIR=./data/ebooks +APP_AUDIOBOOK_DIR=./data/audiobooks +APP_MANGA_DIR=./data/manga +CONTAINER_NAME=librarr-compose-check diff --git a/apps/librarr/latest/data.yml b/apps/librarr/latest/data.yml new file mode 100644 index 000000000..a92aed031 --- /dev/null +++ b/apps/librarr/latest/data.yml @@ -0,0 +1,83 @@ +additionalProperties: + formFields: + - default: 5050 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: HTTP-порт + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: ./data/librarr + edit: true + envKey: APP_DATA_DIR + labelEn: Application data directory + labelZh: 应用数据目录 + label: + en: Application data directory + zh: 应用数据目录 + zh-Hant: 應用資料目錄 + ja: アプリケーションデータディレクトリ + ko: 애플리케이션 데이터 디렉터리 + ru: Каталог данных приложения + ms: Direktori data aplikasi + pt-br: Diretório de dados do aplicativo + required: true + type: text + - default: ./data/ebooks + edit: true + envKey: APP_EBOOK_DIR + labelEn: Ebook directory + labelZh: 电子书目录 + label: + en: Ebook directory + zh: 电子书目录 + zh-Hant: 電子書目錄 + ja: 電子書籍ディレクトリ + ko: 전자책 디렉터리 + ru: Каталог электронных книг + ms: Direktori e-buku + pt-br: Diretório de ebooks + required: true + type: text + - default: ./data/audiobooks + edit: true + envKey: APP_AUDIOBOOK_DIR + labelEn: Audiobook directory + labelZh: 有声书目录 + label: + en: Audiobook directory + zh: 有声书目录 + zh-Hant: 有聲書目錄 + ja: オーディオブックディレクトリ + ko: 오디오북 디렉터리 + ru: Каталог аудиокниг + ms: Direktori buku audio + pt-br: Diretório de audiolivros + required: true + type: text + - default: ./data/manga + edit: true + envKey: APP_MANGA_DIR + labelEn: Manga directory + labelZh: 漫画目录 + label: + en: Manga directory + zh: 漫画目录 + zh-Hant: 漫畫目錄 + ja: 漫画ディレクトリ + ko: 만화 디렉터리 + ru: Каталог манги + ms: Direktori manga + pt-br: Diretório de mangás + required: true + type: text diff --git a/apps/librarr/latest/docker-compose.yml b/apps/librarr/latest/docker-compose.yml new file mode 100644 index 000000000..1b8cd41fa --- /dev/null +++ b/apps/librarr/latest/docker-compose.yml @@ -0,0 +1,39 @@ +services: + librarr: + image: "ghcr.io/jeremiahm37/librarr:latest" + container_name: ${CONTAINER_NAME} + restart: always + read_only: true + tmpfs: + - /tmp:size=512M + security_opt: + - no-new-privileges:true + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5050" + volumes: + - "${APP_DATA_DIR}:/data" + - "${APP_EBOOK_DIR}:/books/ebooks" + - "${APP_AUDIOBOOK_DIR}:/books/audiobooks" + - "${APP_MANGA_DIR}:/books/manga" + environment: + - LIBRARR_PORT=5050 + - LIBRARR_DB_PATH=/data/librarr.db + - EBOOK_DIR=/books/ebooks + - AUDIOBOOK_DIR=/books/audiobooks + - MANGA_DIR=/books/manga + - WISHLIST_CLEANUP_ENABLED=false + - OIDC_PROXY_HEADERS_ENABLED=false + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:5050/api/health"] + interval: 10s + timeout: 5s + retries: 9 + start_period: 15s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/librarr/latest/scripts/init.sh b/apps/librarr/latest/scripts/init.sh new file mode 100755 index 000000000..42c30dcfb --- /dev/null +++ b/apps/librarr/latest/scripts/init.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}" +LIBRARR_UID=1000 +LIBRARR_GID=1000 + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value="" + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +reject_symlink_components() { + local path="$1" + local current="" + local component + IFS='/' read -r -a components <<< "${path#/}" + for component in "${components[@]}"; do + [[ -n "$component" ]] || continue + current="${current}/${component}" + [[ ! -L "$current" ]] || fail "$path must not contain symbolic-link components" + done +} + +prepare_media_dir() { + local key="$1" + local raw="" + local candidate="" + local resolved="" + local scope="" + local existed=false + + raw="$(read_env_value "$key")" + [[ -n "$raw" ]] || fail "$key must not be empty" + [[ "$raw" =~ ^[A-Za-z0-9._/\ -]+$ ]] || fail "$key contains unsupported characters" + + if [[ "$raw" == /* ]]; then + candidate="$raw" + scope="absolute" + else + candidate="${ROOT_DIR}/${raw#./}" + scope="application" + fi + + reject_symlink_components "$candidate" + resolved="$(realpath -m -- "$candidate")" || fail "Unable to resolve $key" + [[ "$resolved" != "/" ]] || fail "$key must not be the filesystem root" + if [[ "$scope" == "application" ]]; then + case "$resolved" in + "${ROOT_DIR}"/*) ;; + *) fail "Relative $key must stay inside the application version directory" ;; + esac + fi + [[ ! -e "$resolved" || -d "$resolved" ]] || fail "$key must be a directory" + [[ -d "$resolved" ]] && existed=true + + if [[ "$scope" == "absolute" && "$existed" == "true" ]]; then + local incompatible_owner="" + incompatible_owner="$(find "$resolved" -xdev \( ! -uid "$LIBRARR_UID" -o ! -gid "$LIBRARR_GID" \) -print -quit)" || \ + fail "Unable to inspect $key ownership" + [[ -z "$incompatible_owner" ]] || fail "Existing absolute $key tree must be owned by ${LIBRARR_UID}:${LIBRARR_GID}" + fi + + install -d -m 0750 -o "$LIBRARR_UID" -g "$LIBRARR_GID" -- "$resolved" || \ + fail "Unable to prepare $key" + local verified="" + verified="$(realpath -e -- "$resolved")" || fail "Unable to verify $key" + [[ "$verified" == "$resolved" ]] || fail "$key changed while it was being prepared" + if [[ "$scope" == "application" ]]; then + case "$verified" in + "${ROOT_DIR}"/*) ;; + *) fail "$key resolves outside the application version directory" ;; + esac + chown -R --no-dereference "$LIBRARR_UID:$LIBRARR_GID" "$verified" || \ + fail "Unable to set $key ownership" + elif [[ "$existed" == "false" ]]; then + chown "$LIBRARR_UID:$LIBRARR_GID" "$verified" || fail "Unable to set $key ownership" + fi + chmod 0750 "$verified" || fail "Unable to set $key permissions" + printf '%s\n' "$verified" +} + +[[ -f "$ENV_FILE" ]] || fail "$ENV_FILE not found" +[[ ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must not be a symbolic link" +[[ "$(id -u)" -eq 0 ]] || fail "Librarr init must run as root to prepare media-directory ownership" + +data_dir="$(prepare_media_dir APP_DATA_DIR)" +ebook_dir="$(prepare_media_dir APP_EBOOK_DIR)" +audiobook_dir="$(prepare_media_dir APP_AUDIOBOOK_DIR)" +manga_dir="$(prepare_media_dir APP_MANGA_DIR)" + +[[ "$data_dir" != "$ebook_dir" && "$data_dir" != "$audiobook_dir" && "$data_dir" != "$manga_dir" && \ + "$ebook_dir" != "$audiobook_dir" && "$ebook_dir" != "$manga_dir" && "$audiobook_dir" != "$manga_dir" ]] || \ + fail "Librarr data and media directories must be distinct" diff --git a/apps/librarr/latest/scripts/uninstall.sh b/apps/librarr/latest/scripts/uninstall.sh new file mode 100755 index 000000000..201fab266 --- /dev/null +++ b/apps/librarr/latest/scripts/uninstall.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Media directories contain user-managed files and are never deleted here. +exit 0 diff --git a/apps/librarr/latest/scripts/upgrade.sh b/apps/librarr/latest/scripts/upgrade.sh new file mode 100755 index 000000000..57b2d551a --- /dev/null +++ b/apps/librarr/latest/scripts/upgrade.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Librarr applies SQLite migrations in the upstream binary during startup. +exit 0 diff --git a/apps/librarr/logo.png b/apps/librarr/logo.png new file mode 100644 index 000000000..9aadb5aab Binary files /dev/null and b/apps/librarr/logo.png differ