├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── ScreenshotConfig.png ├── ScreenshotConfigCh.png ├── ScreenshotPluginConfig.png ├── fetch-stable-pre.sh ├── fetch-stable.sh ├── jellyfin-ffmpeg.sh ├── jellyfin-server.sh ├── jellyfin-web.sh ├── make.sh ├── package.json ├── package.sh ├── packaging ├── built_info ├── icons │ ├── jellyfin.gif │ ├── jellyfin_80.gif │ └── jellyfin_gray.gif ├── package_routines ├── qinstall.sh ├── qpkg.cfg └── shared │ ├── jellyfin-config.sh │ ├── jellyfin.sh │ └── user-config.sh.sample ├── plugins ├── .editorconfig ├── .gitignore ├── Jellyfin.Plugin.QnapConfiguration.sln ├── Jellyfin.Plugin.QnapConfiguration │ ├── Configuration │ │ ├── PluginConfiguration.cs │ │ └── configPage.html │ ├── Jellyfin.Plugin.QnapConfiguration.csproj │ ├── Plugin.cs │ └── copy.bat └── jellyfin.ruleset ├── prefetch-lib-legacy.sh ├── prefetch-lib.sh ├── push.sh └── unpack-lib.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: pdulvp 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.tmp 2 | /.cache 3 | /build 4 | /output 5 | /packaging/build 6 | /*.deb 7 | /*.buildinfo 8 | /init.sh 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logo Jellyfin for QNAP   (https://github.com/pdulvp/jellyfin-qnap/releases) ![](https://img.shields.io/github/downloads/pdulvp/jellyfin-qnap/total?label=All%20downloads&labelColor=green&color=BDE055&style=flat-square) 2 | 3 | ## compatibility icon Compatibility arrow 4 | - See [Compatibility list](https://github.com/pdulvp/jellyfin-qnap/issues/4) and please add a comment if compatible or not with yours. 5 | - Releases are created automatically from the latest jellyfin releases and are not field-tested nor official ones from Jellyfin or QNAP. 6 | - The latest release with my :+1: is deployed on my QNAP and working properly. 7 | 8 | ## hardware icon Installation arrow 9 | 10 | - Download the `.qpkg` from the [Releases](https://github.com/pdulvp/jellyfin-qnap/releases) 11 | - On the `Qnap QTS` > `App Center` > `Settings` > `Allow installation of applications without a valid digital signature` 12 | - On the `Qnap QTS` > `App Center` > `Install Manually`. then choose the downloaded `.qpkg`. 13 | - After installation, on the Jellyfin appearing in the App Center, just wait a bit (~30s) then open it using the `Open` button. It shall open a working web site. (note that if it doesn't open or raise an connection refused, try with default http default port 8096) 14 | - If it is the first time you open, you will have to choose a server (which is the IP of your NAS) and create an user 15 | - If you watch jellyfin on your TV (or so) using Jellyfin Android version (or other), your NAS should then be accessible from it. Just add the NAS ip to access it. 16 | 17 | ## hardware icon Enable Video Acceleration in Jellyfin arrow 18 | 19 | ![](ScreenshotConfig.png) ([中文](ScreenshotConfigCh.png)) 20 | 21 | Go to `Admin > Dashboard` 22 | `Server > Playback` 23 | - `Transcoding` > `Hardware acceleration` : `Video Acceleration API (VAAPI)` 24 | - `Transcoding` > `VA API Device`: `/dev/dri/renderD128` 25 | 26 | This shall be OK, but maybe not. 27 | 28 | ### Troubleshooting while trancoding 29 | 30 | Go to `Admin > Dashboard` 31 | 32 | * You can find a log of vainfo of your NAS under `Advanced > Logs > vainfo-*.log`. It will helps you to find which driver or options to enable. 33 | 34 | * A dedicated plugin is now installed by default on the Jellyfin server `(Plugins > QNAP.Configuration)`, you can change the default vaapi driver used while loading a video. (from `defaultValue` to `iHD` or `i965`). 35 | 36 | ![](ScreenshotPluginConfig.png) 37 | 38 | * Disable some unexpected enabled options `Server > Playback > Transcoding`: 39 | 40 | * On TS-253A, the option `Enable 10-Bit hardware decoding for HEVC` shall be disabled 41 | 42 | ## Network 43 | 44 | ### HTTP port 45 | 46 | Is it possible to change the default http port of Jellyfin. 47 | 48 | Go to `Administration > Dashboard > Advanced > Networking` 49 | 50 | - Change the `Server Address Settings > Local HTTP port number`, go to QNAP App Center and do Stop and Start on the Jellyfin. 51 | 52 | - Hopefully, if you open Jellyfin with the new port number, it shall open it. 53 | 54 | ### HTTPS 55 | 56 | Is it possible to enable HTTPS for Jellyfin. 57 | 58 | Go to `Administration > Dashboard > Advanced > Networking` 59 | 60 | - Enable the `HTTPS` accordingly and change the `Server Address Settings > Local HTTPS port number` if needed. 61 | 62 | - Put the path towards your p12 certificate if you have some. (if not, see below how to generate one for your Jellyfin server) 63 | 64 | - Go to QNAP App Center and do Stop and Start on the Jellyfin. 65 | 66 | - Hopefully, if you open Jellyfin with the new HTTPS port number, it shall open it. 67 | 68 | ### Generate a HTTPS certificate 69 | 70 | You can use directly the certificate of your NAS (download it and generate a p12 from it), or create a new one. Below are the steps to create a child certificate from the QNAP one. 71 | 72 | Download your QNAP NAS certificate 73 | 74 | `System > Security > SSH and certificates > Download certificate and private key` 75 | 76 | Create a private key for the new certificate 77 | 78 | `openssl genrsa -out newkey.key 2048` 79 | 80 | Create a configuration `openssl-cert.conf` 81 | 82 | ``` 83 | [req] 84 | distinguished_name = req_distinguished_name 85 | req_extensions = req_ext 86 | prompt = no 87 | 88 | [req_distinguished_name] 89 | C = FR 90 | ST = Paris 91 | L = Paris 92 | O = pdulvp 93 | CN = Jellyfin QNAP 94 | 95 | [req_ext] 96 | subjectAltName = @alt_names 97 | 98 | [alt_names] 99 | IP.1 = 192.168.1.xxx 100 | DNS.1 = nasfxxxxxx 101 | ``` 102 | 103 | Create a sign request 104 | 105 | `openssl req -new -key newkey.key -out newcsr.csr -config openssl-cert.conf` 106 | 107 | Create a certificate 108 | 109 | `openssl x509 -req -in newcsr.csr -CA SSLcertificate.crt -CAkey SSLprivatekey.key -CAcreateserial -out jellyfincert.crt -days 365 -extensions req_ext -extfile openssl-cert.conf` 110 | 111 | Package the certificate 112 | 113 | `openssl pkcs12 -export -out jellyfincert.p12 -inkey newkey.key -in jellyfincert.crt -certfile SSLcertificate.crt` 114 | 115 | Reference it in Jellyfin 116 | 117 | Start and stop Jellyfin in the QNAP App Center 118 | 119 | On your client, Install the certificate as Trusted. Whether the Jellyfin one (Right Click on CRT, Install Certificate > Local Machine (or current user) > Automatically select the certificate) or the QNAP one as Trusted Root Certificate (Right Click on CRT, Install Certificate > Local Machine (or current user) > Place all certificates on the following store > Trusted Root Certification Authorities). With this last option, all childs certificates of your NAS are trusted. (Restart your PC) 120 | 121 | Open Jellyfin, it shall work. 122 | 123 | Now you shall reconnect to the server using HTTPS 124 | 125 | 126 | ### Customization 127 | 128 | Now, you can customize the startup of Jellyfin by creating a new file `user-config.sh` in the installation folder, rather than editing provided scripts. It will not be erased after updates. (look at `user-config.sh.sample` provided aside) 129 | 130 | ## hybriddesk icon HybridDesk Station arrow 131 | 132 | To add a shortcut onto the HybridDesk Station, you can use **[@pdulvp/jellyfin-qnap-hd](https://github.com/pdulvp/jellyfin-qnap-hd)** 133 | 134 | ## updates icon Automatic updates arrow 135 | 136 | You can use my alternative store link on the App Center to retrieve automatic updates. 137 | See **[pdulvp.fr/qnap-store](https://pdulvp.fr/qstore.html)** 138 | 139 | ## build icon Build arrow 140 | 141 | ### Requirements 142 | - WSL debian bullseye with rsync and jq installed 143 | - QDK2 : https://github.com/qnap-dev/qdk2/releases 144 | - Visual Studio 2022 145 | 146 | ### How to 147 | - Launch a Release build of the `plugins/Jellyfin.Plugin.QnapConfiguration.sln` under Visual Studio. It will create one release, for net8.0 that will be embedded afterwards. 148 | - Launch `./make.sh` (note that the script will try to push it on this repository. `push.sh` can be disabled in subscripts `fetch-stable.sh` and `fetch-stable-pre.sh`) 149 | - If there is some 'File not found' while downloading dependencies, just launch a `sudo apt-get update` on your WSL and relaunch the build 150 | - The build is verbose and raises some logs on tar operations but shall not ring a bell 151 | 152 | ### Why this qpkg is so large 153 | 154 | It is large as it embeds Jellyfin Server, Jellyfin FFMPEG and Jellyfin Web Client and most of their dependencies. Jellyfin and ffmpeg require some dependencies that are not available on most QNAP NAS default installation (latest releases of libc or stuff) so it embeds almost all dependencies in it to be able to be launched. Dependencies and custom installed jellyfin plugin. 155 | -------------------------------------------------------------------------------- /ScreenshotConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdulvp/jellyfin-qnap/20888274fad86cd5224d4785e858a59ff9b8d42f/ScreenshotConfig.png -------------------------------------------------------------------------------- /ScreenshotConfigCh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdulvp/jellyfin-qnap/20888274fad86cd5224d4785e858a59ff9b8d42f/ScreenshotConfigCh.png -------------------------------------------------------------------------------- /ScreenshotPluginConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdulvp/jellyfin-qnap/20888274fad86cd5224d4785e858a59ff9b8d42f/ScreenshotPluginConfig.png -------------------------------------------------------------------------------- /fetch-stable-pre.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PRE_SERVER_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=/server)" | head -1` 4 | PRE_WEB_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=/web)" | head -1` 5 | PRE_FFMPEG_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=/ffmpeg)" | head -n 1` 6 | 7 | SERVER_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/server/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=.dsc)" | head -n 1` 8 | echo "SERVER_VERSION=$SERVER_VERSION" 9 | WEB_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/web/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=.dsc)" | head -n 1` 10 | echo "WEB_VERSION=$WEB_VERSION" 11 | FFMPEG_VERSION=`wget https://repo.jellyfin.org/releases/server/debian/stable-pre/ffmpeg/ -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=-bullseye_amd64.buildinfo)" | head -n 1` 12 | #FFMPEG_VERSION="4.4.1-4" 13 | echo "FFMPEG_VERSION=$FFMPEG_VERSION" 14 | 15 | CURRENT_VERSION=`cat package.json | grep -o -P "(?<=\"preversion\"\: \")([^\"])+"` 16 | CURRENT_SHA=`cat package.json | grep -o -P "(?<=\"presha\"\: \")([^\"])+"` 17 | echo "CURRENT_VERSION=$CURRENT_VERSION" 18 | echo "CURRENT_SHA=$CURRENT_SHA" 19 | 20 | rm -f *.sha256sum 21 | wget -q "https://repo.jellyfin.org/releases/server/debian/versions/jellyfin-ffmpeg/$FFMPEG_VERSION/jellyfin-ffmpeg5_"$FFMPEG_VERSION"-bullseye_amd64.deb.sha256sum" 22 | wget -q "https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/server/jellyfin-server_"$SERVER_VERSION"_amd64.deb.sha256sum" 23 | wget -q "https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/web/jellyfin-web_"$WEB_VERSION"_all.deb.sha256sum" 24 | 25 | NEXT_VERSION=`echo $SERVER_VERSION @ $WEB_VERSION @ $FFMPEG_VERSION | tr ".-" " " | tr "@" "." | tr "~" "-" | sed "s/ //g"` 26 | NEXT_SHA=`find . -maxdepth 1 -name "jelly*.deb.sha256sum" -exec cat {} \; | cut -d" " -f 1 | md5sum | cut -d" " -f 1` 27 | NEXT_SHA="${PRE_SERVER_VERSION}_${NEXT_SHA}" 28 | echo "NEXT_VERSION=$NEXT_VERSION" 29 | echo "NEXT_SHA=$NEXT_SHA" 30 | 31 | QPKG_VER=`echo $PRE_SERVER_VERSION | sed -r 's/([0-9a-z.]+)[-~]([0-9a-z])([0-9a-z.]+)([0-9a-z])/\1-\2\4/g'` 32 | echo "QPKG_VER=$QPKG_VER" 33 | 34 | if [ "$CURRENT_VERSION" == "$NEXT_VERSION" ] && [ "$CURRENT_SHA" == "$NEXT_SHA" ]; then 35 | echo -e "\033[0;36mNo new prerelease \033[0m" 36 | exit; 37 | fi 38 | echo -e "\033[0;32mDownload new prerelease \033[0m" 39 | 40 | rm -f jellyfin-server_*.deb* 41 | rm -f jellyfin-web_*.deb* 42 | rm -f jellyfin-ffmpeg*_*.deb* 43 | 44 | wget -q "https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/server/jellyfin-server_"$SERVER_VERSION"_amd64.deb" 45 | wget -q "https://repo.jellyfin.org/releases/server/debian/stable-pre/$PRE_SERVER_VERSION/web/jellyfin-web_"$WEB_VERSION"_all.deb" 46 | wget -q "https://repo.jellyfin.org/releases/server/debian/versions/jellyfin-ffmpeg/$FFMPEG_VERSION/jellyfin-ffmpeg5_"$FFMPEG_VERSION"-bullseye_amd64.deb" 47 | 48 | sed -i "s/^QPKG_VER=.*$/QPKG_VER=\"$QPKG_VER\"/" output/qpkg.cfg 49 | 50 | ./jellyfin-server.sh 51 | ./jellyfin-ffmpeg.sh 52 | ./package.sh 53 | 54 | sed -i "s/$CURRENT_VERSION/$NEXT_VERSION/g" package.json 55 | sed -i "s/$CURRENT_SHA/$NEXT_SHA/g" package.json 56 | 57 | DESC="Version based on: \`jellyfin-server_$PRE_SERVER_VERSION\` \`jellyfin-web_$PRE_WEB_VERSION\` \`jellyfin-ffmpeg5_$FFMPEG_VERSION\`" 58 | PKG=`find output/build/ -name "jellyfin_*${QPKG_VER:0:10}*.qpkg"` 59 | ./push.sh "${NEXT_VERSION}_${NEXT_SHA::-24}" "$PRE_SERVER_VERSION" "$DESC" "$PKG" "true" 60 | -------------------------------------------------------------------------------- /fetch-stable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ARCH=amd64 4 | SERVER_VERSION=$(wget https://repo.jellyfin.org/?path=/server/debian/latest-stable/$ARCH -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=\+deb.*_$ARCH.buildinfo)" | head -n 1) 5 | echo "SERVER_VERSION=$SERVER_VERSION" 6 | WEB_VERSION=$SERVER_VERSION 7 | echo "WEB_VERSION=$WEB_VERSION" 8 | FFMPEG_VERSION=$(wget https://repo.jellyfin.org/?path=/ffmpeg/debian/latest-7.x/$ARCH -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=-bullseye_$ARCH.buildinfo)" | head -n 1) 9 | #FFMPEG_VERSION="4.4.1-4" 10 | FFMPEG5_VERSION=$(wget https://repo.jellyfin.org/?path=/ffmpeg/debian/latest-5.x/$ARCH -q -O- | grep -o -P "([a-z0-9\-\.~]+)(?=-bullseye_$ARCH.deb)" | head -n 1) 11 | echo "FFMPEG_VERSION=$FFMPEG_VERSION" 12 | echo "FFMPEG5_VERSION=$FFMPEG5_VERSION" 13 | 14 | CURRENT_VERSION=$(cat package.json | grep -o -P "(?<=\"version\"\: \")([^\"])+") 15 | CURRENT_SHA=$(cat package.json | grep -o -P "(?<=\"sha\"\: \")([^\"])+") 16 | echo "CURRENT_VERSION=$CURRENT_VERSION" 17 | echo "CURRENT_SHA=$CURRENT_SHA" 18 | 19 | PREFIX="" 20 | NEXT_VERSION=$(echo $SERVER_VERSION @ $WEB_VERSION @ $FFMPEG_VERSION @ $FFMPEG5_VERSION | tr ".-" " " | tr "@" "." | tr "~" "-" | sed "s/ //g") 21 | NEXT_SHA=$(echo $NEXT_VERSION | md5sum | cut -d" " -f 1) 22 | echo "NEXT_VERSION=$NEXT_VERSION" 23 | echo "NEXT_SHA=$NEXT_SHA" 24 | 25 | SUFFIX=$(cat package.json | grep -o -P "(?<=\"suffix\"\: \")([^\"])+") 26 | if [ $SUFFIX != "" ]; then 27 | SUFFIX="-$SUFFIX" 28 | fi 29 | QPKG_VER=$SERVER_VERSION$SUFFIX 30 | echo "QPKG_VER=$QPKG_VER" 31 | 32 | if [ "$CURRENT_VERSION" == "$NEXT_VERSION" ] && [ "$CURRENT_SHA" == "$NEXT_SHA" ]; then 33 | echo -e "\033[0;36mNo new release \033[0m" 34 | exit; 35 | fi 36 | echo -e "\033[0;32mDownload new release \033[0m" 37 | 38 | get() { 39 | URL=$1 40 | KEY=${URL##*/} 41 | KEY=$(echo $KEY | sed "s/%2B/\+/g") 42 | echo $KEY 43 | if [ -f .cache/deb/$KEY ]; then 44 | cp .cache/deb/$KEY . 45 | echo -e "\033[0;32mGet from cache \033[0m" 46 | else 47 | wget -q $URL 48 | mkdir -p .cache/deb; 49 | cp $KEY .cache/deb 50 | fi 51 | } 52 | 53 | proceed() { 54 | ARCH=$1 55 | FFMPEG=$2 56 | echo "Procceed $ARCH $FFMPEG" 57 | rm -f *.buildinfo 58 | if [ "$FFMPEG" == "ffmpeg5" ]; then 59 | FFMPEG_INFO=latest-6.x/$ARCH/jellyfin-ffmpeg_$FFMPEG_VERSION-bullseye_$ARCH.buildinfo 60 | FFMPEG_DEB=latest-5.x/$ARCH/jellyfin-ffmpeg5_$FFMPEG5_VERSION-bullseye_$ARCH.deb 61 | FFMPEG_TAG=$FFMPEG5_VERSION 62 | elif [ "$FFMPEG" == "ffmpeg6" ]; then 63 | FFMPEG_INFO=latest-6.x/$ARCH/jellyfin-ffmpeg_$FFMPEG_VERSION-bullseye_$ARCH.buildinfo 64 | FFMPEG_DEB=latest-6.x/$ARCH/jellyfin-ffmpeg6_$FFMPEG_VERSION-bullseye_$ARCH.deb 65 | FFMPEG_TAG=$FFMPEG5_VERSION 66 | else 67 | FFMPEG_INFO=latest-7.x/$ARCH/jellyfin-ffmpeg_$FFMPEG_VERSION-bullseye_$ARCH.buildinfo 68 | FFMPEG_DEB=latest-7.x/$ARCH/jellyfin-ffmpeg7_$FFMPEG_VERSION-bullseye_$ARCH.deb 69 | FFMPEG_TAG=$FFMPEG_VERSION 70 | fi 71 | 72 | SERVER_INFO=latest-stable/$ARCH/jellyfin_$SERVER_VERSION%2Bdeb11_$ARCH.buildinfo 73 | SERVER_DEB=latest-stable/$ARCH/jellyfin-server_$SERVER_VERSION%2Bdeb11_$ARCH.deb 74 | WEB_DEB=latest-stable/$ARCH/jellyfin-web_$SERVER_VERSION%2Bdeb11_all.deb 75 | 76 | get "https://repo.jellyfin.org/files/ffmpeg/debian/$FFMPEG_INFO" 77 | get "https://repo.jellyfin.org/files/server/debian/$SERVER_INFO" 78 | 79 | rm -f jellyfin-server_*.deb* 80 | rm -f jellyfin-web_*.deb* 81 | rm -f jellyfin-ffmpeg*_*.deb* 82 | 83 | get "https://repo.jellyfin.org/files/ffmpeg/debian/$FFMPEG_DEB" 84 | get "https://repo.jellyfin.org/files/server/debian/$SERVER_DEB" 85 | get "https://repo.jellyfin.org/files/server/debian/$WEB_DEB" 86 | 87 | rm -rf .tmp 88 | rm -rf output 89 | mkdir output 90 | cp -rf packaging/* output 91 | 92 | if ! ./jellyfin-server.sh "$ARCH" "$SERVER_VERSION"; then 93 | exit $? 94 | fi 95 | 96 | if ! ./jellyfin-ffmpeg.sh "$ARCH" "$FFMPEG_TAG" "$FFMPEG"; then 97 | exit $? 98 | fi 99 | 100 | if ! ./unpack-lib.sh "$SERVER_VERSION-$FFMPEG_TAG-$ARCH"; then 101 | exit $? 102 | fi 103 | 104 | # move all libs under bin as jellyfin doesn't support other folders. 105 | mv .tmp/lib/lib/*-linux-*/* output/shared/jellyfin/bin/ 106 | mv .tmp/lib/usr/lib/*-linux-*/* output/shared/jellyfin/bin/ 107 | 108 | if ! ./jellyfin-web.sh; then 109 | exit $? 110 | fi 111 | 112 | mkdir -p output/build 113 | if ! ./package.sh $ARCH $FFMPEG $QPKG_VER; then 114 | exit $? 115 | fi 116 | } 117 | 118 | if ! proceed "amd64" "ffmpeg7"; then 119 | exit $? 120 | fi 121 | if ! proceed "amd64" "ffmpeg5"; then 122 | exit $? 123 | fi 124 | if ! proceed "arm64" "ffmpeg5"; then 125 | exit $? 126 | fi 127 | if ! proceed "arm64" "ffmpeg7"; then 128 | exit $? 129 | fi 130 | if ! proceed "armhf" "ffmpeg7"; then 131 | exit $? 132 | fi 133 | if ! proceed "armhf" "ffmpeg5"; then 134 | exit $? 135 | fi 136 | 137 | json=$(cat package.json | jq ".version = \"$NEXT_VERSION\"") 138 | json=$(echo $json | jq ".sha = \"$NEXT_SHA\"") 139 | json=$(echo $json | jq ".ffmpeg = \"$FFMPEG_VERSION\"") 140 | json=$(echo $json | jq ".ffmpeg5 = \"$FFMPEG5_VERSION\"") 141 | json=$(echo $json | jq ".server = \"$SERVER_VERSION\"") 142 | json=$(echo $json | jq ".web = \"$WEB_VERSION\"") 143 | printf '%s\n' "$json" > package.json 144 | -------------------------------------------------------------------------------- /jellyfin-ffmpeg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ARCH=$1 4 | FFMPEG_VERSION=$2 5 | FFMPEG_TYPE=$3 6 | if [ $(ls -1 jellyfin-ffmpeg*_*-bullseye_*.deb | wc -l) -ne 1 ]; then 7 | echo -e "jellyfin-ffmpeg*_XYZ-bullseye_*.deb not found or several releases." 1>&2 8 | exit 1 9 | fi 10 | 11 | FFMPEG=$(ls -1 jellyfin-ffmpeg*_*-bullseye_*.deb) 12 | echo $FFMPEG found. 13 | 14 | FFMPEG_INFO=`ls -1 jellyfin-ffmpeg*.buildinfo` 15 | echo $FFMPEG_INFO found. 16 | 17 | case "$ARCH" in 18 | armhf) LD_LIB="ld-linux-armhf.so.3" ;; 19 | arm64) LD_LIB="ld-linux-aarch64.so.1" ;; 20 | *) LD_LIB="ld-linux-x86-64.so.2" ;; 21 | esac 22 | 23 | if [ "$ARCH" == "$NEXT_VERSION" ] && [ "$CURRENT_SHA" == "$NEXT_SHA" ]; then 24 | echo -e "\033[0;36mNo new release \033[0m" 25 | exit; 26 | fi 27 | 28 | # Unzip jellyfin-ffmpeg.deb/data.tar.xz/./usr/lib/ into output/shared/ 29 | mkdir .tmp/ffmpeg 30 | cd .tmp/ffmpeg 31 | ar x ../../$FFMPEG data.tar.xz 32 | tar xf data.tar.xz ./usr/lib/ 33 | cd ../.. 34 | rm -rf output/shared/jellyfin-ffmpeg 35 | mv .tmp/ffmpeg/usr/lib/jellyfin-ffmpeg output/shared/ 36 | rm -rf .tmp/ffmpeg 37 | 38 | # Create ffmpeg and ffprobe versions that will rely on required jellyfin-ffmpeg/lib/ld-linux-x86-64.so.2 rather than default one 39 | mv output/shared/jellyfin-ffmpeg/ffmpeg output/shared/jellyfin-ffmpeg/ffmpeg2 40 | mv output/shared/jellyfin-ffmpeg/ffprobe output/shared/jellyfin-ffmpeg/ffprobe2 41 | mv output/shared/jellyfin-ffmpeg/vainfo output/shared/jellyfin-ffmpeg/vainfo2 42 | 43 | cat >output/shared/jellyfin-ffmpeg/ffmpeg <output/shared/jellyfin-ffmpeg/ffprobe <output/shared/jellyfin-ffmpeg/vainfo <output/shared/jellyfin/bin/jellyfin < /tmp/update_process 155 | } 156 | set_progress_begin(){ 157 | set_progress 0 158 | } 159 | set_progress_before_install(){ 160 | set_progress 1 161 | } 162 | set_progress_after_install(){ 163 | set_progress 2 164 | } 165 | set_progress_end(){ 166 | set_progress 3 167 | } 168 | set_progress_fail(){ 169 | set_progress -1 170 | } 171 | 172 | ##################################### 173 | # Message to terminal and system log 174 | ##################################### 175 | log() { 176 | local write_msg="$CMD_LOG_TOOL -t0 -uSystem -p127.0.0.1 -mlocalhost -a" 177 | [ -n "$1" ] && $CMD_ECHO "$1" && $write_msg "$1" 178 | } 179 | 180 | ############################################# 181 | # Warning message to terminal and system log 182 | ############################################# 183 | warn_log() { 184 | local write_warn="$CMD_LOG_TOOL -t1 -uSystem -p127.0.0.1 -mlocalhost -a" 185 | [ -n "$1" ] && $CMD_ECHO "$1" 1>&2 && $write_warn "$1" 186 | } 187 | 188 | ################################################################### 189 | # Error message to terminal and system log. Also cleans up after a 190 | # failed installation. This function terminates the installation. 191 | ################################################################### 192 | err_log(){ 193 | local write_err="$CMD_LOG_TOOL -t2 -uSystem -p127.0.0.1 -mlocalhost -a" 194 | local message="$1" 195 | $CMD_ECHO "$message" 1>&2 196 | $write_err "$message" 197 | 198 | # Any backed up configuration files are restored to be available for 199 | # a new upgrade attempt. 200 | restore_config 201 | 202 | set_progress_fail 203 | exit 1 204 | } 205 | 206 | #################### 207 | # Extract data file 208 | #################### 209 | extract_data(){ 210 | [ -n "$1" ] || return 1 211 | local archive="$1" 212 | local root_dir="${2:-$SYS_QPKG_DIR}" 213 | case "$archive" in 214 | *.gz|*.bz2) 215 | $CMD_TAR xvf "$archive" -C "$root_dir" 2>/dev/null >>$SYS_QPKG_DIR/.list || if [ -x "/usr/local/sbin/notify" ]; then /usr/local/sbin/notify send -A A039 -C C001 -M 35 -l error -t 3 "[{0}] {1} install failed du to data file error." "$PREFIX" "$QPKG_DISPLAY_NAME";set_progress_fail;exit 1;else err_log "$SYS_MSG_FILE_ERROR";fi 216 | 217 | ;; 218 | *.7z) 219 | $CMD_7Z x -so "$archive" 2>/dev/null | $CMD_TAR xv -C "$root_dir" 2>/dev/null >>$SYS_QPKG_DIR/.list || if [ -x "/usr/local/sbin/notify" ]; then /usr/local/sbin/notify send -A A039 -C C001 -M 35 -l error -t 3 "[{0}] {1} install failed du to data file error." "$PREFIX" "$QPKG_DISPLAY_NAME";set_progress_fail;exit 1;else err_log "$SYS_MSG_FILE_ERROR";fi 220 | ;; 221 | *) 222 | if [ -x "/usr/local/sbin/notify" ]; then 223 | /usr/local/sbin/notify send -A A039 -C C001 -M 35 -l error -t 3 "[{0}] {1} install failed du to data file error." "$PREFIX" "$QPKG_DISPLAY_NAME" 224 | set_progress_fail 225 | exit 1 226 | else 227 | err_log "$SYS_MSG_FILE_ERROR" 228 | fi 229 | esac 230 | } 231 | 232 | ############################# 233 | # Extract extra config files 234 | ############################# 235 | extract_config(){ 236 | if [ -f $SYS_QPKG_DATA_CONFIG_FILE ]; then 237 | $CMD_TAR xvf $SYS_QPKG_DATA_CONFIG_FILE -C / 2>/dev/null | $CMD_SED 's/\.//' 2>/dev/null >>$SYS_QPKG_DIR/.list || if [ -x "/usr/local/sbin/notify" ]; then /usr/local/sbin/notify send -A A039 -C C001 -M 35 -l error -t 3 "[{0}] {1} install failed du to data file error." "$PREFIX" "$QPKG_DISPLAY_NAME";set_progress_fail;exit 1;else err_log "$SYS_MSG_FILE_ERROR";fi 238 | fi 239 | } 240 | 241 | #################################### 242 | # Restore saved configuration files 243 | #################################### 244 | restore_config(){ 245 | if [ -f "$SYS_TAR_CONFIG" ]; then 246 | $CMD_TAR xf $SYS_TAR_CONFIG -C / 2>/dev/null || warn_log "$SYS_MSG_FAILED_CONFIG_RESTORE" 247 | $CMD_RM $SYS_TAR_CONFIG 248 | fi 249 | } 250 | 251 | ############################ 252 | # Store configuration files 253 | ############################ 254 | store_config(){ 255 | # Tag configuration files for later removal. 256 | $CMD_SED -i "/\[$QPKG_NAME\]/,/^\[/s/^cfg:/^&/" $SYS_QPKG_CONFIG_FILE 2>/dev/null 257 | 258 | SYS_TAR_CONFIG="$SYS_QPKG_INSTALL_PATH/${QPKG_NAME}_$$.tar" 259 | local new_md5sum= 260 | local orig_md5sum= 261 | local current_md5sum= 262 | local qpkg_config=$($CMD_SED -n '/^QPKG_CONFIG/s/QPKG_CONFIG="\(.*\)"/\1/p' qpkg.cfg) 263 | for file in $qpkg_config 264 | do 265 | new_md5sum=$($CMD_GETCFG "" "$file" -f $SYS_QPKG_DATA_MD5SUM_FILE) 266 | orig_md5sum=$($CMD_GETCFG "$QPKG_NAME" "^cfg:$file" -f $SYS_QPKG_CONFIG_FILE) 267 | set_qpkg_config "$file" "$new_md5sum" 268 | # Files relative to QPKG directory are changed to full path. 269 | [ -z "${file##/*}" ] || file="$SYS_QPKG_DIR/$file" 270 | current_md5sum=$($CMD_MD5SUM "$file" 2>/dev/null | $CMD_CUT -d' ' -f1) 271 | if [ "$orig_md5sum" = "$current_md5sum" ] || [ "$new_md5sum" = "$current_md5sum" ]; then 272 | : Use new file 273 | elif [ -f $file ]; then 274 | if [ -z "$orig_md5sum" ]; then 275 | $CMD_MV $file ${file}.qdkorig 276 | if [ -x "/usr/local/sbin/notify" ]; then 277 | /usr/local/sbin/notify send -A A039 -C C001 -M 38 -l info -t 3 "[{0}] {1} action: {2} is saved as {2}.qdkorig" "$PREFIX" "$QPKG_DISPLAY_NAME" "$file" 278 | else 279 | log "[$PREFIX] $QPKG_DISPLAY_NAME saved ${file} as ${file}.qdkorig." 280 | fi 281 | elif [ "$orig_md5sum" = "$new_md5sum" ]; then 282 | $CMD_TAR rf $SYS_TAR_CONFIG $file 2>/dev/null 283 | else 284 | $CMD_MV $file ${file}.qdksave 285 | if [ -x "/usr/local/sbin/notify" ]; then 286 | /usr/local/sbin/notify send -A A039 -C C001 -M 39 -l info -t 3 "[{0}] {1} action: {2} is saved as {2}.qdksave" "$PREFIX" "$QPKG_DISPLAY_NAME" "$file" 287 | else 288 | log "[$PREFIX] $QPKG_DISPLAY_NAME saved ${file} as ${file}.qdksave." 289 | fi 290 | fi 291 | fi 292 | done 293 | 294 | # Remove obsolete configuration files. 295 | $CMD_SED -i "/\[$QPKG_NAME\]/,/^\[/{/^^cfg:/d}" $SYS_QPKG_CONFIG_FILE 2>/dev/null 296 | } 297 | 298 | ############################ 299 | # Store built version 300 | ############################ 301 | store_built_version(){ 302 | # Tag buildVer for later removal. 303 | $CMD_SED -i "/\[$QPKG_NAME\]/,/^\[/s/^buildVer:/^&/" $SYS_QPKG_CONFIG_FILE 2>/dev/null 304 | 305 | local built_time= 306 | local built_svn= 307 | 308 | if [ -f $SYS_QPKG_DATA_BUILTVER_FILE ]; then 309 | built_time=$($CMD_GETCFG "" "time" -f $SYS_QPKG_DATA_BUILTVER_FILE) 310 | set_qpkg_field "buildVer:time" "$built_time" 311 | built_svn=$($CMD_GETCFG "" "svn" -f $SYS_QPKG_DATA_BUILTVER_FILE) 312 | set_qpkg_field "buildVer:svn" "$built_svn" 313 | fi 314 | 315 | # Remove obsolete buildVer. 316 | $CMD_SED -i "/\[$QPKG_NAME\]/,/^\[/{/^^buildVer:/d}" $SYS_QPKG_CONFIG_FILE 2>/dev/null 317 | } 318 | 319 | ############################ 320 | # Store built information 321 | ############################ 322 | store_built_information(){ 323 | local built_time= 324 | 325 | if [ -f $SYS_QPKG_DATA_BUILTINFO_FILE ]; then 326 | built_time=$($CMD_GETCFG "" "time" -f $SYS_QPKG_DATA_BUILTINFO_FILE) 327 | set_qpkg_field "Build" "$built_time" 328 | fi 329 | } 330 | 331 | ##################################################################### 332 | # Add given configuration file and md5sum to SYS_QPKG_CONFIG_FILE if 333 | # not already added. 334 | ###################################################################### 335 | add_qpkg_config(){ 336 | [ -n "$1" ] && [ -n "$2" ] || return 1 337 | local file="$1" 338 | local md5sum="$2" 339 | 340 | $CMD_ECHO "$file" >>$SYS_QPKG_DIR/.list 341 | $CMD_GETCFG "$QPKG_NAME" "cfg:$file" -f $SYS_QPKG_CONFIG_FILE >/dev/null || \ 342 | set_qpkg_config $file $md5sum 343 | } 344 | 345 | ################################################# 346 | # Remove specified file or directory (if empty). 347 | ################################################# 348 | remove_file_and_empty_dir(){ 349 | [ -n "$1" ] || return 1 350 | local file= 351 | # Files relative to QPKG directory are changed to full path. 352 | if [ -n "${1##/*}" ]; then 353 | file="$SYS_QPKG_DIR/$1" 354 | else 355 | file="$1" 356 | fi 357 | if [ -f "$file" ]; then 358 | $CMD_RM -f "$file" 359 | elif [ -d "$file" ]; then 360 | $CMD_RMDIR "$file" 2>/dev/null 361 | fi 362 | } 363 | 364 | ############################# 365 | # Check QTS minimum version. 366 | ############################# 367 | check_qts_version(){ 368 | NOW_VERSION=`/sbin/getcfg System Version -f /etc/config/uLinux.conf|cut -c 1,3,5` 369 | if [ -e $QTS_MINI_VERSION ]; then 370 | MINI_VERSION=0 371 | else 372 | MINI_VERSION=`echo "$QTS_MINI_VERSION"|cut -c 1,3,5` 373 | fi 374 | if [ -e $QTS_MAX_VERSION ]; then 375 | MAX_VERSION=1000 376 | else 377 | MAX_VERSION=`echo "$QTS_MAX_VERSION"|cut -c 1,3,5` 378 | fi 379 | 380 | if [ ${MINI_VERSION} -gt ${NOW_VERSION} ]; then 381 | if [ -x "/usr/local/sbin/notify" ]; then 382 | /usr/local/sbin/notify send -A A039 -C C001 -M 40 -l error -t 3 "[{0}] {1} install failed du to the QTS firmware is not compatible, please upgrade QTS to {2} or newer version." "$PREFIX" "$QPKG_DISPLAY_NAME" "$QTS_MINI_VERSION" 383 | set_progress_fail 384 | exit 1 385 | else 386 | err_log "[$PREFIX] Failed to install $QPKG_DISPLAY_NAME. Upgrade QTS to $QTS_MINI_VERSION or a newer compatible version." 387 | fi 388 | elif [ ${MAX_VERSION} -lt ${NOW_VERSION} ]; then 389 | if [ -x "/usr/local/sbin/notify" ]; then 390 | /usr/local/sbin/notify send -A A039 -C C001 -M 41 -l error -t 3 "[{0}] {1} install failed du to the QTS firmware is not compatible, please downgrade QTS to {2} or newer version." "$PREFIX" "$QPKG_DISPLAY_NAME" "$QTS_MAX_VERSION" 391 | set_progress_fail 392 | exit 1 393 | else 394 | err_log "[$PREFIX] Failed to install $QPKG_DISPLAY_NAME. Downgrade QTS to $QTS_MAX_VERSION or an older compatible version." 395 | fi 396 | else 397 | echo "Firmware check is fine." 398 | fi 399 | } 400 | 401 | ################################################################ 402 | # Remove obsolete files by comparing old and new list of files. 403 | ################################################################ 404 | remove_obsolete_files(){ 405 | if [ -f $SYS_QPKG_DIR/.list ] && [ -f $SYS_QPKG_DIR/.oldlist ]; then 406 | local obsolete_files=$($CMD_AWK ' 407 | BEGIN{ 408 | while ( getline < "'$SYS_QPKG_DIR'/.list" > 0 ){ 409 | files[$0]=1 410 | } 411 | while ( getline < "'$SYS_QPKG_DIR'/.oldlist" > 0 ){ 412 | if ( !( $0 in files )){ 413 | print 414 | } 415 | } 416 | }') 417 | for file in $obsolete_files 418 | do 419 | remove_file_and_empty_dir "$file" 420 | done 421 | $CMD_RM -f $SYS_QPKG_DIR/.oldlist 422 | fi 423 | } 424 | 425 | ############################################################################### 426 | # Determine location of given share and assign to variable in second argument. 427 | ############################################################################### 428 | get_share_path(){ 429 | [ -n "$1" ] && [ -n "$2" ] || return 1 430 | local share="$1" 431 | local path="$2" 432 | 433 | # Get location from smb.conf 434 | local location=$($CMD_GETCFG "$share" path -f $SYS_CONFIG_DIR/smb.conf) 435 | 436 | [ -n "$location" ] || return 1 437 | eval $path=\"$location\" 438 | } 439 | 440 | #################################################### 441 | # Determine name and location for all system shares 442 | #################################################### 443 | init_share_settings(){ 444 | SYS_PUBLIC_SHARE=$($CMD_GETCFG SHARE_DEF defPublic -d Public -f $SYS_CONFIG_DIR/def_share.info) 445 | SYS_DOWNLOAD_SHARE=$($CMD_GETCFG SHARE_DEF defDownload -d Qdownload -f $SYS_CONFIG_DIR/def_share.info) 446 | SYS_MULTIMEDIA_SHARE=$($CMD_GETCFG SHARE_DEF defMultimedia -d Qmultimedia -f $SYS_CONFIG_DIR/def_share.info) 447 | SYS_RECORDINGS_SHARE=$($CMD_GETCFG SHARE_DEF defRecordings -d Qrecordings -f $SYS_CONFIG_DIR/def_share.info) 448 | SYS_USB_SHARE=$($CMD_GETCFG SHARE_DEF defUsb -d Qusb -f $SYS_CONFIG_DIR/def_share.info) 449 | SYS_WEB_SHARE=$($CMD_GETCFG SHARE_DEF defWeb -d Qweb -f $SYS_CONFIG_DIR/def_share.info) 450 | 451 | get_share_path $SYS_PUBLIC_SHARE SYS_PUBLIC_PATH 452 | get_share_path $SYS_DOWNLOAD_SHARE SYS_DOWNLOAD_PATH 453 | get_share_path $SYS_MULTIMEDIA_SHARE SYS_MULTIMEDIA_PATH 454 | get_share_path $SYS_RECORDINGS_SHARE SYS_RECORDINGS_PATH 455 | get_share_path $SYS_USB_SHARE SYS_USB_PATH 456 | get_share_path $SYS_WEB_SHARE SYS_WEB_PATH 457 | } 458 | 459 | ################################################################## 460 | # Determine BASE installation location and assign to SYS_QPKG_DIR 461 | ################################################################## 462 | assign_base(){ 463 | SYS_QPKG_INSTALL_PATH="$(dirname ${PWD})" 464 | SYS_QPKG_DIR="$SYS_QPKG_INSTALL_PATH/$QPKG_NAME" 465 | } 466 | 467 | ##################################################################### 468 | # Determine the architecture for the device the QPKG is installed on 469 | ##################################################################### 470 | assign_arch(){ 471 | case "$(/bin/uname -m)" in 472 | armv5tejl) 473 | SYS_CPU_ARCH="arm-x09" 474 | ;; 475 | armv5tel) 476 | SYS_CPU_ARCH="arm-x19" 477 | ;; 478 | armv7l) 479 | SYS_CPU_ARCH="arm-x41" 480 | ;; 481 | aarch64) 482 | SYS_CPU_ARCH="arm_64" 483 | ;; 484 | i*86) 485 | SYS_CPU_ARCH="x86" 486 | ;; 487 | x86_64) 488 | SYS_CPU_ARCH="x86_64" 489 | ;; 490 | *) 491 | SYS_CPU_ARCH= 492 | ;; 493 | esac 494 | } 495 | 496 | ################################# 497 | # Link service start/stop script 498 | ################################# 499 | link_start_stop_script(){ 500 | if [ -n "$QPKG_SERVICE_PROGRAM" ]; then 501 | $CMD_ECHO "Link service start/stop script: $QPKG_SERVICE_PROGRAM" 502 | [ -f "$SYS_QPKG_DIR/$QPKG_SERVICE_PROGRAM" ] || err_log "$QPKG_SERVICE_PROGRAM: no such file" 503 | $CMD_LN -sf "$SYS_QPKG_DIR/$QPKG_SERVICE_PROGRAM" "$SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM" 504 | $CMD_LN -sf "$SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM" "$SYS_STARTUP_DIR/QS${QPKG_RC_NUM}${QPKG_NAME}" 505 | local shutdown_rc_num="$(printf "%03d" $($CMD_EXPR 1000 - $QPKG_RC_NUM))" 506 | $CMD_LN -sf "$SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM" "$SYS_SHUTDOWN_DIR/QK${shutdown_rc_num}${QPKG_NAME}" 507 | $CMD_CHMOD 755 "$SYS_QPKG_DIR/$QPKG_SERVICE_PROGRAM" 508 | fi 509 | 510 | # Only applied on TS-109/209/409 for chrooted env 511 | if [ -d "${QPKG_ROOTFS-/mnt/HDA_ROOT/rootfs_2_3_6}" ]; then 512 | if [ -n "$QPKG_SERVICE_PROGRAM_CHROOT" ]; then 513 | $CMD_MV $SYS_QPKG_DIR/$QPKG_SERVICE_PROGRAM_CHROOT $QPKG_ROOTFS/etc/init.d 514 | $CMD_CHMOD 755 $QPKG_ROOTFS/etc/init.d/$QPKG_SERVICE_PROGRAM_CHROOT 515 | fi 516 | fi 517 | } 518 | 519 | ############################# 520 | # Start and stop the service 521 | ############################# 522 | start_service(){ 523 | if [ -x $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM ]; then 524 | $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM start 525 | $CMD_SLEEP 5 526 | $CMD_SYNC 527 | fi 528 | } 529 | stop_service(){ 530 | if [ -x $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM ]; then 531 | $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM stop 532 | $CMD_SLEEP 5 533 | $CMD_SYNC 534 | fi 535 | } 536 | 537 | #################################################################### 538 | # Assign given value to specified field (optional section, defaults 539 | # to QPKG_NAME). 540 | #################################################################### 541 | set_qpkg_field(){ 542 | [ -n "$1" ] && [ -n "$2" ] || return 1 543 | local field="$1" 544 | local value="$2" 545 | local section="${3:-$QPKG_NAME}" 546 | 547 | $CMD_SETCFG "$section" "$field" "$value" -f $SYS_QPKG_CONFIG_FILE 548 | } 549 | enable_qpkg(){ 550 | set_qpkg_field $SYS_QPKG_CONF_FIELD_ENABLE "TRUE" 551 | } 552 | disable_qpkg(){ 553 | set_qpkg_field $SYS_QPKG_CONF_FIELD_ENABLE "FALSE" 554 | } 555 | set_qpkg_name(){ 556 | set_qpkg_field $SYS_QPKG_CONF_FIELD_NAME "$QPKG_NAME" 557 | set_qpkg_field $SYS_QPKG_CONF_FIELD_DISPLAY_NAME "$QPKG_DISPLAY_NAME" 558 | } 559 | set_qpkg_version(){ 560 | set_qpkg_field $SYS_QPKG_CONF_FIELD_VERSION "$QPKG_VER" 561 | } 562 | set_qpkg_author(){ 563 | set_qpkg_field $SYS_QPKG_CONF_FIELD_AUTHOR "$QPKG_AUTHOR" 564 | } 565 | set_qpkg_install_date(){ 566 | set_qpkg_field $SYS_QPKG_CONF_FIELD_DATE $($CMD_DATE +%F) 567 | } 568 | set_qpkg_install_path(){ 569 | set_qpkg_field $SYS_QPKG_CONF_FIELD_INSTALL_PATH $SYS_QPKG_DIR 570 | } 571 | set_qpkg_file_name(){ 572 | set_qpkg_field $SYS_QPKG_CONF_FIELD_QPKGFILE "${QPKG_QPKG_FILE:-${QPKG_NAME}.qpkg}" 573 | } 574 | set_qpkg_config_path(){ 575 | [ -z "$QPKG_CONFIG_PATH" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_CONFIG_PATH "$QPKG_CONFIG_PATH" 576 | } 577 | set_qpkg_service_path(){ 578 | [ -z "$QPKG_SERVICE_PROGRAM" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_SHELL "$SYS_QPKG_DIR/$QPKG_SERVICE_PROGRAM" 579 | } 580 | set_qpkg_service_port(){ 581 | [ -z "$QPKG_SERVICE_PORT" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_SERVICEPORT "$QPKG_SERVICE_PORT" 582 | } 583 | set_qpkg_service_pid(){ 584 | [ -z "$QPKG_SERVICE_PIDFILE" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_SERVICE_PIDFILE "$QPKG_SERVICE_PIDFILE" 585 | } 586 | set_qpkg_web_url(){ 587 | [ -z "$QPKG_WEBUI" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_WEBUI "$QPKG_WEBUI" 588 | } 589 | set_qpkg_web_port(){ 590 | if [ -n "$QPKG_WEB_PORT" ]; then 591 | set_qpkg_field $SYS_QPKG_CONF_FIELD_WEBPORT "$QPKG_WEB_PORT" 592 | [ -n "$QPKG_WEBUI" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_WEBUI "/" 593 | fi 594 | } 595 | set_qpkg_volume_select(){ 596 | if [ -n "$QPKG_VOLUME_SELECT" ]; then 597 | set_qpkg_field $SYS_QPKG_CONF_FIELD_VOLUME_SELECT "$QPKG_VOLUME_SELECT" 598 | fi 599 | } 600 | set_qpkg_web_ssl_port(){ 601 | if [ -n "$QPKG_WEB_SSL_PORT" ]; then 602 | set_qpkg_field $SYS_QPKG_CONF_FIELD_WEB_SSL_PORT "$QPKG_WEB_SSL_PORT" 603 | [ -n "$QPKG_WEBUI" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_WEBUI "/" 604 | fi 605 | } 606 | set_qpkg_status(){ 607 | if [ "$SYS_QPKG_SERVICE_ENABLED" = "TRUE" ]; then 608 | enable_qpkg 609 | start_service || disable_qpkg 610 | else 611 | disable_qpkg 612 | fi 613 | } 614 | set_qpkg_config(){ 615 | [ -n "$1" ] && [ -n "$2" ] || return 1 616 | local file="$1" 617 | local md5sum="$2" 618 | 619 | set_qpkg_field "cfg:$file" "$md5sum" 620 | } 621 | set_qpkg_sys_app(){ 622 | if [ -n "$QPKG_SYS_APP" ]; then 623 | set_qpkg_field $SYS_QPKG_CONF_FIELD_SYSAPP "$QPKG_SYS_APP" 624 | fi 625 | } 626 | set_qpkg_rc_number(){ 627 | [ -z "$QPKG_RC_NUM" ] || set_qpkg_field $SYS_QPKG_CONF_FIELD_RC_NUMBER "$QPKG_RC_NUM" 628 | } 629 | set_qpkg_desktop_app(){ 630 | if [ -n "$QPKG_DESKTOP_APP" ]; then 631 | set_qpkg_field $SYS_QPKG_CONF_FIELD_DESKTOPAPP "$QPKG_DESKTOP_APP" 632 | fi 633 | if [ -n "$QPKG_DESKTOP_APP_WIN_WIDTH" ]; then 634 | set_qpkg_field $SYS_QPKG_CONF_FIELD_DESKTOPAPP_WIN_WIDTH "$QPKG_DESKTOP_APP_WIN_WIDTH" 635 | fi 636 | if [ -n "$QPKG_DESKTOP_APP_WIN_HEIGHT" ]; then 637 | set_qpkg_field $SYS_QPKG_CONF_FIELD_DESKTOPAPP_WIN_HEIGHT "$QPKG_DESKTOP_APP_WIN_HEIGHT" 638 | fi 639 | } 640 | set_qpkg_use_proxy(){ 641 | if [ -n "$QPKG_USE_PROXY" ]; then 642 | set_qpkg_field $SYS_QPKG_CONF_FIELD_USE_PROXY "$QPKG_USE_PROXY" 643 | fi 644 | } 645 | set_qpkg_proxy_path(){ 646 | if [ -n "$QPKG_PROXY_PATH" ]; then 647 | set_qpkg_field $SYS_QPKG_CONF_FIELD_PROXY_PATH "$QPKG_PROXY_PATH" 648 | fi 649 | } 650 | set_qpkg_timeout(){ 651 | if [ -n "$QPKG_TIMEOUT" ]; then 652 | set_qpkg_field $SYS_QPKG_CONF_FIELD_TIMEOUT "$QPKG_TIMEOUT" 653 | fi 654 | } 655 | set_qpkg_visible(){ 656 | if [ -n "$QPKG_VISIBLE" ]; then 657 | set_qpkg_field $SYS_QPKG_CONF_FIELD_VISIBLE "$QPKG_VISIBLE" 658 | fi 659 | } 660 | set_qpkg_fw_ver_min(){ 661 | if [ -n "$QTS_MINI_VERSION" ]; then 662 | set_qpkg_field $SYS_QPKG_CONF_FIELD_FW_VER_MIN "$QTS_MINI_VERSION" 663 | fi 664 | } 665 | set_qpkg_fw_ver_max(){ 666 | if [ -n "$QTS_MAX_VERSION" ]; then 667 | set_qpkg_field $SYS_QPKG_CONF_FIELD_FW_VER_MAX "$QTS_MAX_VERSION" 668 | fi 669 | } 670 | 671 | 672 | ############################################################ 673 | # Store the current status of the QPKG to be able to 674 | # restore it later. 675 | ############################################################ 676 | get_qpkg_status(){ 677 | SYS_QPKG_SERVICE_ENABLED="$($CMD_GETCFG $QPKG_NAME $SYS_QPKG_CONF_FIELD_ENABLE -d "TRUE" -f $SYS_QPKG_CONFIG_FILE)" 678 | } 679 | 680 | ####################### 681 | # Set QPKG information 682 | ####################### 683 | register_qpkg(){ 684 | $CMD_ECHO "Set QPKG information in $SYS_QPKG_CONFIG_FILE" 685 | [ -f $SYS_QPKG_CONFIG_FILE ] || $CMD_TOUCH $SYS_QPKG_CONFIG_FILE 686 | 687 | set_qpkg_name 688 | set_qpkg_version 689 | set_qpkg_author 690 | 691 | set_qpkg_file_name 692 | set_qpkg_install_date 693 | set_qpkg_service_path 694 | set_qpkg_service_port 695 | set_qpkg_volume_select 696 | set_qpkg_service_pid 697 | set_qpkg_install_path 698 | set_qpkg_config_path 699 | set_qpkg_web_url 700 | set_qpkg_web_port 701 | set_qpkg_web_ssl_port 702 | set_qpkg_rc_number 703 | set_qpkg_sys_app 704 | set_qpkg_desktop_app 705 | set_qpkg_use_proxy 706 | set_qpkg_proxy_path 707 | set_qpkg_timeout 708 | set_qpkg_visible 709 | set_qpkg_fw_ver_min 710 | set_qpkg_fw_ver_max 711 | } 712 | 713 | ################## 714 | # Copy QPKG icons 715 | ################## 716 | copy_qpkg_icons() 717 | { 718 | $CMD_RM -fr $SYS_RSS_IMG_DIR/${QPKG_NAME}.gif 719 | $CMD_CP -af $SYS_QPKG_DIR/.qpkg_icon.gif $SYS_RSS_IMG_DIR/${QPKG_NAME}.gif 2>/dev/null 720 | 721 | $CMD_RM -fr $SYS_RSS_IMG_DIR/${QPKG_NAME}_80.gif 722 | $CMD_CP -af $SYS_QPKG_DIR/.qpkg_icon_80.gif $SYS_RSS_IMG_DIR/${QPKG_NAME}_80.gif 2>/dev/null 723 | 724 | $CMD_RM -fr $SYS_RSS_IMG_DIR/${QPKG_NAME}_gray.gif 725 | $CMD_CP -af $SYS_QPKG_DIR/.qpkg_icon_gray.gif $SYS_RSS_IMG_DIR/${QPKG_NAME}_gray.gif 2>/dev/null 726 | } 727 | 728 | ################################################################## 729 | # Split MAJOR.MINOR.BUILD version into individual parts adding an 730 | # optional prefix to definition 731 | # 732 | # The values are available in ${PREFIX}MAJOR, ${PREFIX}MINOR, 733 | # and ${PREFIX}BUILD 734 | ################################################################## 735 | split_version(){ 736 | [ -n "$1" ] || return 1 737 | local version="$1" 738 | local prefix="$2" 739 | 740 | local major=$($CMD_EXPR "$version" : '\([^.]*\)') 741 | local minor=$($CMD_EXPR "$version" : '[^.]*[.]\([^.]*\)') 742 | local build=$($CMD_EXPR "$version" : '[^.]*[.][^.]*[.]\([^.]*\)') 743 | eval ${prefix}MAJOR=${major:-0} 744 | eval ${prefix}MINOR=${minor:-0} 745 | eval ${prefix}BUILD=${build:-0} 746 | } 747 | 748 | ################################################################## 749 | # Check if versions are equal 750 | # 751 | # Returns 0 if versions are equal, otherwise it returns 1. 752 | ################################################################## 753 | is_equal(){ 754 | [ -n "$1" ] && [ -n "$2" ] || return 1 755 | 756 | split_version $1 757 | split_version $2 REQ_ 758 | 759 | if $CMD_EXPR $MAJOR != $REQ_MAJOR >/dev/null || 760 | $CMD_EXPR $MINOR != $REQ_MINOR >/dev/null || 761 | $CMD_EXPR $BUILD != $REQ_BUILD >/dev/null; then 762 | return 1 763 | fi 764 | } 765 | 766 | ################################################################## 767 | # Check if versions are unequal 768 | # 769 | # Returns 0 if versions are unequal, otherwise it returns 1. 770 | ################################################################## 771 | is_unequal(){ 772 | [ -n "$1" ] && [ -n "$2" ] || return 1 773 | 774 | split_version $1 775 | split_version $2 REQ_ 776 | 777 | if $CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null && 778 | $CMD_EXPR $MINOR = $REQ_MINOR >/dev/null && 779 | $CMD_EXPR $BUILD = $REQ_BUILD >/dev/null; then 780 | return 1 781 | fi 782 | } 783 | 784 | ################################################################## 785 | # Check if one version is less than or equal to another version 786 | # 787 | # Returns 0 if VERSION1 is less than or equal to VERSION2, 788 | # otherwise it returns 1. 789 | ################################################################## 790 | is_less_or_equal(){ 791 | [ -n "$1" ] && [ -n "$2" ] || return 1 792 | 793 | split_version $1 794 | split_version $2 REQ_ 795 | 796 | if $CMD_EXPR $MAJOR \> $REQ_MAJOR >/dev/null || 797 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 798 | $CMD_EXPR $MINOR \> $REQ_MINOR >/dev/null) || 799 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 800 | ($CMD_EXPR $MINOR = $REQ_MINOR >/dev/null) && 801 | $CMD_EXPR $BUILD \> $REQ_BUILD >/dev/null); then 802 | return 1 803 | fi 804 | } 805 | 806 | ################################################################## 807 | # Check if one version is less than another version 808 | # 809 | # Returns 0 if VERSION1 is less than VERSION2, 810 | # otherwise it returns 1. 811 | ################################################################## 812 | is_less(){ 813 | [ -n "$1" ] && [ -n "$2" ] || return 1 814 | 815 | split_version $1 816 | split_version $2 REQ_ 817 | 818 | if $CMD_EXPR $MAJOR \> $REQ_MAJOR >/dev/null || 819 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 820 | $CMD_EXPR $MINOR \> $REQ_MINOR >/dev/null) || 821 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 822 | ($CMD_EXPR $MINOR = $REQ_MINOR >/dev/null) && 823 | $CMD_EXPR $BUILD \>= $REQ_BUILD >/dev/null); then 824 | return 1 825 | fi 826 | } 827 | 828 | ################################################################## 829 | # Check if one version is greater than another version 830 | # 831 | # Returns 0 if VERSION1 is greater than VERSION2, 832 | # otherwise it returns 1. 833 | ################################################################## 834 | is_greater(){ 835 | [ -n "$1" ] && [ -n "$2" ] || return 1 836 | 837 | split_version $1 838 | split_version $2 REQ_ 839 | 840 | if $CMD_EXPR $MAJOR \< $REQ_MAJOR >/dev/null || 841 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 842 | $CMD_EXPR $MINOR \< $REQ_MINOR >/dev/null) || 843 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 844 | ($CMD_EXPR $MINOR = $REQ_MINOR >/dev/null) && 845 | $CMD_EXPR $BUILD \<= $REQ_BUILD >/dev/null); then 846 | return 1 847 | fi 848 | } 849 | 850 | ################################################################## 851 | # Check if one version is greater than or equal to another version 852 | # 853 | # Returns 0 if VERSION1 is greater than or equal to VERSION2, 854 | # otherwise it returns 1. 855 | ################################################################## 856 | is_greater_or_equal(){ 857 | [ -n "$1" ] && [ -n "$2" ] || return 1 858 | 859 | split_version $1 860 | split_version $2 REQ_ 861 | 862 | if $CMD_EXPR $MAJOR \< $REQ_MAJOR >/dev/null || 863 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 864 | $CMD_EXPR $MINOR \< $REQ_MINOR >/dev/null) || 865 | (($CMD_EXPR $MAJOR = $REQ_MAJOR >/dev/null) && 866 | ($CMD_EXPR $MINOR = $REQ_MINOR >/dev/null) && 867 | $CMD_EXPR $BUILD \< $REQ_BUILD >/dev/null); then 868 | return 1 869 | fi 870 | } 871 | 872 | ############################################################ 873 | # Check that given QPKG package isn't installed or that 874 | # specified Optware package isn't installed. An optional 875 | # version check can also be performed. 876 | # 877 | # Returns 0 if package is not installed, otherwise it 878 | # returns 1. 879 | ############################################################ 880 | is_qpkg_not_installed(){ 881 | [ -n "$1" ] || return 1 882 | local qpkg_name="$1" 883 | local op="$2" 884 | local conflict="$3" 885 | 886 | local available= 887 | local pkg="$($CMD_EXPR "$qpkg_name" : "OPT/\(.*\)")" 888 | if [ "$qpkg_name" = "QNAP_FW" ] && [ -n "$op" ] && [ -n "$conflict" ]; then 889 | available=1 890 | elif [ -z "$pkg" ]; then 891 | available=$($CMD_GETCFG "$qpkg_name" "$SYS_QPKG_CONF_FIELD_NAME" -f $SYS_QPKG_CONFIG_FILE) 892 | elif [ -n "$CMD_PKG_TOOL" ]; then 893 | available=$($CMD_PKG_TOOL status $pkg | $CMD_GREP "^Version:") 894 | else 895 | return 0 896 | fi 897 | local status=0 898 | if [ -n "$available" ]; then 899 | status=1 900 | local installed= 901 | if [ "$qpkg_name" = "QNAP_FW" ]; then 902 | installed=$($CMD_GETCFG "System" "Version") 903 | elif [ -z "$pkg" ]; then 904 | installed=$($CMD_GETCFG "$qpkg_name" "$SYS_QPKG_CONF_FIELD_VERSION" -d "" -f $SYS_QPKG_CONFIG_FILE) 905 | else 906 | installed="$($CMD_PKG_TOOL status $pkg | $CMD_SED -n 's/^Version: \(.*\)/\1/p')" 907 | fi 908 | 909 | if [ -n "$conflict" ] && [ -n "$installed" ]; then 910 | case "$op" in 911 | =|==) 912 | is_equal $installed $conflict || status=0 ;; 913 | !=) 914 | is_unequal $installed $conflict || status=0 ;; 915 | \<=) 916 | is_less_or_equal $installed $conflict || status=0 ;; 917 | \<) 918 | is_less $installed $conflict || status=0 ;; 919 | \>) 920 | is_greater $installed $conflict || status=0 ;; 921 | \>=) 922 | is_greater_or_equal $installed $conflict || status=0 ;; 923 | *) 924 | status=1 925 | ;; 926 | esac 927 | fi 928 | else 929 | [ "$qpkg_name" = "$QPKG_NAME" ] && [ -d $SYS_QPKG_DIR ] && status=1 930 | fi 931 | 932 | return $status 933 | } 934 | 935 | ############################################################ 936 | # Check if given QPKG package exists and is enabled or that 937 | # specified Optware package exists. An optional version 938 | # check can also be performed. 939 | # 940 | # Returns 0 if package is valid, otherwise it returns 1. 941 | ############################################################ 942 | is_qpkg_enabled(){ 943 | [ -n "$1" ] || return 1 944 | local qpkg_name="$1" 945 | local op="$2" 946 | local required="$3" 947 | 948 | local enabled="FALSE" 949 | local pkg="$($CMD_EXPR "$qpkg_name" : "OPT/\(.*\)")" 950 | if [ "$qpkg_name" = "QNAP_FW" ] && [ -n "$op" ] && [ -n "$required" ]; then 951 | enabled="TRUE" 952 | elif [ -z "$pkg" ]; then 953 | enabled=$($CMD_GETCFG "$qpkg_name" "$SYS_QPKG_CONF_FIELD_ENABLE" -d "FALSE" -f $SYS_QPKG_CONFIG_FILE) 954 | elif [ -n "$CMD_PKG_TOOL" ]; then 955 | $CMD_PKG_TOOL status $pkg | $CMD_GREP -q "^Version:" && enabled="TRUE" 956 | else 957 | return 1 958 | fi 959 | local status=1 960 | if [ "$enabled" = "TRUE" ]; then 961 | status=0 962 | local installed= 963 | if [ "$qpkg_name" = "QNAP_FW" ]; then 964 | installed=$($CMD_GETCFG "System" "Version") 965 | elif [ -z "$pkg" ]; then 966 | installed=$($CMD_GETCFG "$qpkg_name" "$SYS_QPKG_CONF_FIELD_VERSION" -d "" -f $SYS_QPKG_CONFIG_FILE) 967 | else 968 | installed="$($CMD_PKG_TOOL status $pkg | $CMD_SED -n 's/^Version: \(.*\)/\1/p')" 969 | # Installed packages with no specific version check shall always be upgraded 970 | # if a new version is available. 971 | [ -z "$required" ] && [ -z "$SYS_PKG_INSTALLED" ] && status=1 972 | fi 973 | if [ -n "$required" ] && [ -n "$installed" ]; then 974 | case "$op" in 975 | =|==) 976 | is_equal $installed $required || status=1 ;; 977 | !=) 978 | is_unequal $installed $required || status=1 ;; 979 | \<=) 980 | is_less_or_equal $installed $required || status=1 ;; 981 | \<) 982 | is_less $installed $required || status=1 ;; 983 | \>) 984 | is_greater $installed $required || status=1 ;; 985 | \>=) 986 | is_greater_or_equal $installed $required || status=1 ;; 987 | *) 988 | status=1 989 | ;; 990 | esac 991 | fi 992 | fi 993 | # Try to install the latest version and then re-check the requirement. 994 | if [ $status -eq 1 ] && [ -n "$pkg" ] && [ -z "$SYS_PKG_INSTALLED" ]; then 995 | if [ -z "$SYS_PKG_UPDATED" ]; then 996 | $CMD_PKG_TOOL $SYS_PKG_TOOL_OPTS update || warn_log "$CMD_PKG_TOOL update failed" 997 | SYS_PKG_UPDATED="TRUE" 998 | fi 999 | $CMD_PKG_TOOL $SYS_PKG_TOOL_OPTS install $pkg || warn_log "$CMD_PKG_TOOL install $pkg failed" 1000 | # Avoid never-ending loop... 1001 | SYS_PKG_INSTALLED="TRUE" 1002 | is_qpkg_enabled "$qpkg_name" $op $required && status=0 1003 | fi 1004 | SYS_PKG_INSTALLED= 1005 | [ $status -eq 0 ] && adjust_rc_num $qpkg_name 1006 | return $status 1007 | } 1008 | 1009 | ##################################################################### 1010 | # Check requirements routines 1011 | # 1012 | # Only returns if all requirements are fulfilled, otherwise err_log 1013 | # is called with a relevant error message. 1014 | ##################################################################### 1015 | check_requirements(){ 1016 | local install_msg= 1017 | local fw_install_msg= 1018 | local remove_msg= 1019 | local fw_remove_msg= 1020 | if [ -n "$QPKG_REQUIRE" ]; then 1021 | OLDIFS="$IFS"; IFS=, 1022 | set $QPKG_REQUIRE 1023 | IFS="$OLDIFS" 1024 | for require 1025 | do 1026 | local statusOK="FALSE" 1027 | OLDIFS="$IFS"; IFS=\| 1028 | set $require 1029 | IFS="$OLDIFS" 1030 | for statement 1031 | do 1032 | set $($CMD_ECHO "$statement" | $CMD_SED 's/\(.*[^=<>!]\)\([=<>!]\+\)\(.*\)/\1 \2 \3/') 1033 | qpkg=$1 1034 | op=$2 1035 | version=$3 1036 | statusOK="TRUE" 1037 | is_qpkg_enabled "$qpkg" $op $version && break 1038 | statusOK="FALSE" 1039 | done 1040 | [ "$statusOK" = "TRUE" ] || if [ -x "/usr/local/sbin/notify" ]; then /usr/local/sbin/notify send -A A039 -C C001 -M 44 -l error -t 3 "[{0}] {1} {2} install failed. The following QPKG must be installed and enabled: {3}." "$PREFIX" "$QPKG_DISPLAY_NAME" "$QPKG_VER" "$QPKG_REQUIRE"; set_progress_fail;exit 1;else err_log "[$PREFIX] Failed to install $QPKG_DISPLAY_NAME $QPKG_VER. You must first install and enable $QPKG_REQUIRE.";fi 1041 | done 1042 | fi 1043 | if [ -n "$QPKG_CONFLICT" ]; then 1044 | OLDIFS="$IFS"; IFS=, 1045 | set $QPKG_CONFLICT 1046 | IFS="$OLDIFS" 1047 | for conflict 1048 | do 1049 | set $($CMD_ECHO "$conflict" | $CMD_SED 's/\(.*[^=<>!]\)\([=<>!]\+\)\(.*\)/\1 \2 \3/') 1050 | qpkg=$1 1051 | op=$2 1052 | version=$3 1053 | is_qpkg_not_installed "$qpkg" $op $version || if [ -x "/usr/local/sbin/notify" ]; then /usr/local/sbin/notify send -A A039 -C C001 -M 45 -l error -t 3 "[{0}] {1} {2} install failed. The following QPKG must be removed: {3}." "$PREFIX" "$QPKG_DISPLAY_NAME" "$QPKG_VER" "$QPKG_CONFLICT";set_progress_fail;exit 1;else err_log "[$PREFIX] Failed to install $QPKG_DISPLAY_NAME $QPKG_VER. You must first remove $QPKG_CONFLICT.";fi 1054 | done 1055 | fi 1056 | local err_msg= 1057 | [ -n "$fw_install_msg" ] && err_msg="${err_msg}The following firmware requirement must be fulfilled: ${fw_install_msg}. " 1058 | [ -n "$fw_remove_msg" ] && err_msg="${err_msg}The following firmware conflict must be resolved: ${fw_remove_msg}. " 1059 | [ -n "$install_msg" ] && err_msg="${err_msg}The following QPKG must be installed and enabled: ${install_msg}. " 1060 | [ -n "$remove_msg" ] && err_msg="${err_msg}The following QPKG must be removed: ${remove_msg}. " 1061 | [ -n "$err_msg" ] && err_log "$err_msg" 1062 | 1063 | # Package specific routines as defined in package_routines. 1064 | call_defined_routine pkg_check_requirement 1065 | } 1066 | 1067 | ##################################################################### 1068 | # If necessary, change QPKG_RC_NUM to make sure the installed QPKG 1069 | # application starts after given QPKG application. 1070 | ##################################################################### 1071 | adjust_rc_num(){ 1072 | if [ -n "$1" ]; then 1073 | rc_num="$($CMD_GETCFG $1 $SYS_QPKG_CONF_FIELD_RC_NUMBER -f $SYS_QPKG_CONFIG_FILE)" 1074 | [ ${QPKG_RC_NUM:-0} -gt ${rc_num:-99} ] || QPKG_RC_NUM="$($CMD_EXPR ${rc_num:-99} + 1)" 1075 | fi 1076 | } 1077 | 1078 | ################################## 1079 | # Create uninstall script 1080 | ################################## 1081 | create_uninstall_script(){ 1082 | local uninstall_script="$SYS_QPKG_DIR/.uninstall.sh" 1083 | 1084 | # Re-source package_routines to include any run-time variables 1085 | # in the remove functions. 1086 | source package_routines 1087 | 1088 | # Save stdout to fd 5. 1089 | exec 5>&1 1090 | 1091 | # Redirect all output to uninstall script. 1092 | exec > "$uninstall_script" 1093 | 1094 | $CMD_CAT <<-EOF 1095 | #!/bin/sh 1096 | 1097 | # Stop the service before we begin the removal. 1098 | if [ -x $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM ]; then 1099 | $SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM stop 1100 | $CMD_SLEEP 5 1101 | $CMD_SYNC 1102 | fi 1103 | 1104 | # Package specific routines as defined in package_routines. 1105 | $PKG_PRE_REMOVE 1106 | 1107 | # Remove QPKG directory, init-scripts, and icons. 1108 | $CMD_RM -fr "$SYS_QPKG_DIR" 1109 | $CMD_RM -f "$SYS_INIT_DIR/$QPKG_SERVICE_PROGRAM" 1110 | $CMD_FIND $SYS_STARTUP_DIR -type l -name 'QS*${QPKG_NAME}' | $CMD_XARGS $CMD_RM -f 1111 | $CMD_FIND $SYS_SHUTDOWN_DIR -type l -name 'QK*${QPKG_NAME}' | $CMD_XARGS $CMD_RM -f 1112 | $CMD_RM -f "$SYS_RSS_IMG_DIR/${QPKG_NAME}.gif" 1113 | $CMD_RM -f "$SYS_RSS_IMG_DIR/${QPKG_NAME}_80.gif" 1114 | $CMD_RM -f "$SYS_RSS_IMG_DIR/${QPKG_NAME}_gray.gif" 1115 | 1116 | # Package specific routines as defined in package_routines. 1117 | $PKG_MAIN_REMOVE 1118 | 1119 | # Package specific routines as defined in package_routines. 1120 | $PKG_POST_REMOVE 1121 | EOF 1122 | 1123 | # Restore stdout and close fd 5. 1124 | exec 1>&5 5>&- 1125 | 1126 | $CMD_CHMOD 755 "$uninstall_script" 1127 | } 1128 | 1129 | ############################################################ 1130 | # Call package specific routine if it is defined 1131 | ############################################################ 1132 | call_defined_routine(){ 1133 | [ -n "$(command -v $1)" ] && $1 1134 | cd $SYS_EXTRACT_DIR 1135 | } 1136 | 1137 | ################################################# 1138 | # Rename configuration files that use old format 1139 | ################################################# 1140 | add_config_prefix(){ 1141 | local qpkg_config=$($CMD_SED -n '/^QPKG_CONFIG/s/QPKG_CONFIG="\(.*\)"/\1/p' qpkg.cfg) 1142 | for file in $qpkg_config 1143 | do 1144 | $CMD_GETCFG "$QPKG_NAME" "$file" -f $SYS_QPKG_CONFIG_FILE >/dev/null && \ 1145 | $CMD_SED -i "/\[$QPKG_NAME\]/,/^\[/s*^$file*cfg:&*" $SYS_QPKG_CONFIG_FILE 1146 | done 1147 | } 1148 | 1149 | ############### 1150 | # Init routine 1151 | ############### 1152 | init(){ 1153 | if [ -n "$CMD_PKG_TOOL" ] && [ -f $SYS_QPKG_DATA_PACKAGES_FILE ]; then 1154 | $CMD_ECHO "src/gz _qdk file://$(pwd)" > ipkg.conf 1155 | SYS_PKG_TOOL_OPTS="$SYS_PKG_TOOL_OPTS -f ipkg.conf" 1156 | $CMD_PKG_TOOL $SYS_PKG_TOOL_OPTS update || warn_log "$CMD_PKG_TOOL update failed" 1157 | SYS_PKG_UPDATED="TRUE" 1158 | fi 1159 | 1160 | init_share_settings 1161 | assign_base 1162 | assign_arch 1163 | 1164 | if [ -f $SYS_QPKG_DIR/.list ]; then 1165 | $CMD_SORT -r $SYS_QPKG_DIR/.list > $SYS_QPKG_DIR/.oldlist 1166 | $CMD_RM $SYS_QPKG_DIR/.list 1167 | fi 1168 | 1169 | add_config_prefix 1170 | 1171 | source package_routines 1172 | 1173 | # Package specific routines as defined in package_routines. 1174 | call_defined_routine pkg_init 1175 | } 1176 | 1177 | ################################## 1178 | # Pre-install routine 1179 | ################################## 1180 | pre_install(){ 1181 | if [ -d $SYS_QPKG_DIR ]; then 1182 | local current_qpkg_ver="$($CMD_GETCFG $QPKG_NAME $SYS_QPKG_CONF_FIELD_VERSION -f $SYS_QPKG_CONFIG_FILE)" 1183 | $CMD_ECHO "$QPKG_NAME $current_qpkg_ver is already installed. Setup will now perform package upgrading." 1184 | fi 1185 | 1186 | check_qts_version 1187 | store_config 1188 | store_built_version 1189 | store_built_information 1190 | get_qpkg_status 1191 | stop_service 1192 | 1193 | $CMD_MKDIR -p $SYS_QPKG_DIR 1194 | 1195 | # Package specific routines as defined in package_routines. 1196 | call_defined_routine pkg_pre_install 1197 | } 1198 | 1199 | ################################## 1200 | # Install routines 1201 | ################################## 1202 | install(){ 1203 | extract_data "$SYS_QPKG_DATA_FILE" 1204 | extract_config 1205 | restore_config 1206 | 1207 | # Package specific routines as defined in package_routines. 1208 | call_defined_routine pkg_install 1209 | } 1210 | 1211 | ################################## 1212 | # Post-install routine 1213 | ################################## 1214 | post_install(){ 1215 | remove_obsolete_files 1216 | copy_qpkg_icons 1217 | link_start_stop_script 1218 | register_qpkg 1219 | 1220 | # Package specific routines as defined in package_routines. 1221 | call_defined_routine pkg_post_install 1222 | } 1223 | 1224 | 1225 | ################################## 1226 | # Main installation 1227 | ################################## 1228 | main(){ 1229 | set_progress_begin 1230 | if [ -z "$QPKG_DISPLAY_NAME" ]; then 1231 | QPKG_DISPLAY_NAME=$QPKG_NAME 1232 | fi 1233 | if [ -f $SYS_QPKG_DATA_FILE_GZIP ]; then 1234 | SYS_QPKG_DATA_FILE=$SYS_QPKG_DATA_FILE_GZIP 1235 | elif [ -f $SYS_QPKG_DATA_FILE_BZIP2 ]; then 1236 | SYS_QPKG_DATA_FILE=$SYS_QPKG_DATA_FILE_BZIP2 1237 | elif [ -f $SYS_QPKG_DATA_FILE_7ZIP ]; then 1238 | SYS_QPKG_DATA_FILE=$SYS_QPKG_DATA_FILE_7ZIP 1239 | else 1240 | if [ -x "/usr/local/sbin/notify" ]; then 1241 | /usr/local/sbin/notify send -A A039 -C C001 -M 34 -l error -t 3 "[{0}] {1} install failed du to cannot find the data file." "$PREFIX" "$QPKG_DISPLAY_NAME" 1242 | set_progress_fail 1243 | exit 1 1244 | else 1245 | err_log "[$PREFIX] Failed to install $QPKG_DISPLAY_NAME. Data file is missing." 1246 | fi 1247 | fi 1248 | 1249 | init 1250 | 1251 | check_requirements 1252 | 1253 | pre_install 1254 | set_progress_before_install 1255 | install 1256 | set_progress_after_install 1257 | post_install 1258 | 1259 | create_uninstall_script 1260 | 1261 | 1262 | $CMD_SYNC 1263 | 1264 | ##system pop up log when QPKG has installed 1265 | 1266 | if [ -x "/usr/local/sbin/notify" ]; then 1267 | /usr/local/sbin/notify send -A A039 -C C001 -M 46 -l info -t 3 "[{0}] {1} {2} has been installed in {3} successfully." "$PREFIX" "$QPKG_DISPLAY_NAME" "$QPKG_VER" "$SYS_QPKG_DIR" 1268 | else 1269 | log "[$PREFIX] Installed $QPKG_DISPLAY_NAME $QPKG_VER in $SYS_QPKG_DIR." 1270 | fi 1271 | 1272 | # This also starts the service program if the QPKG is enabled. 1273 | set_qpkg_status 1274 | 1275 | ##system pop up log after QPKG has installed and app was enable 1276 | 1277 | if is_qpkg_enabled "$QPKG_NAME"; then 1278 | if [ -x "/usr/local/sbin/notify" ]; then 1279 | /usr/local/sbin/notify send -A A039 -C C001 -M 47 -l info -t 3 "[{0}] {1} enabled." "$PREFIX" "$QPKG_DISPLAY_NAME" 1280 | else 1281 | log "[$PREFIX] Enabled $QPKG_DISPLAY_NAME." 1282 | fi 1283 | fi 1284 | set_progress_end 1285 | } 1286 | 1287 | main 1288 | -------------------------------------------------------------------------------- /packaging/qpkg.cfg: -------------------------------------------------------------------------------- 1 | # Name of the packaged application. 2 | QPKG_NAME="jellyfin" 3 | # Name of the display application. 4 | QPKG_DISPLAY_NAME="jellyfin" 5 | # Version of the packaged application. 6 | QPKG_VER="10.9.2" 7 | # Author or maintainer of the package 8 | QPKG_AUTHOR="pdulvp" 9 | # License for the packaged application 10 | QPKG_LICENSE="GPL-2.0" 11 | # One-line description of the packaged application 12 | QPKG_SUMMARY="Free Software Media System that puts you in control of managing and streaming your media." 13 | 14 | # Preferred number in start/stop sequence. 15 | QPKG_RC_NUM="161" 16 | # Init-script used to control the start and stop of the installed application. 17 | QPKG_SERVICE_PROGRAM="jellyfin.sh" 18 | 19 | # Optional 1 is enable. Path of starting/ stopping shall script. (no start/stop on App Center) 20 | #QPKG_DISABLE_APPCENTER_UI_SERVICE=1 21 | 22 | # Specifies any packages required for the current package to operate. 23 | #QPKG_REQUIRE="Python >= 2.7" 24 | # Specifies what packages cannot be installed if the current package 25 | # is to operate properly. 26 | #QPKG_CONFLICT="Python" 27 | # Name of configuration file (multiple definitions are allowed). 28 | #QPKG_CONFIG="myApp.conf" 29 | #QPKG_CONFIG="/etc/config/myApp.conf" 30 | # Port number used by service program. 31 | #QPKG_SERVICE_PORT="" 32 | # Location of file with running service's PID 33 | #QPKG_SERVICE_PIDFILE="" 34 | # Relative path to web interface 35 | QPKG_WEBUI="/web/" 36 | # Port number for the web interface. 37 | QPKG_WEB_PORT="8096" 38 | # Port number for the SSL web interface. 39 | QPKG_WEB_SSL_PORT="8920" 40 | 41 | # Use QTS HTTP Proxy and set Proxy_Path in the qpkg.conf. 42 | # When the QPKG has its own HTTP service port, and want clients to connect via QTS HTTP port (default 8080). 43 | # Usually use this option when the QPKG need to connect via myQNAPcloud service. 44 | #QPKG_USE_PROXY="1" 45 | #QPKG_PROXY_PATH="/qpkg_name" 46 | 47 | #Desktop Application (since 4.1) 48 | # Set value to 1 means to open the QPKG's Web UI inside QTS desktop instead of new window. 49 | #QPKG_DESKTOP_APP="1" 50 | # Desktop Application Window default inner width (since 4.1) (not over 1178) 51 | #QPKG_DESKTOP_APP_WIN_WIDTH="" 52 | # Desktop Application Window default inner height (since 4.1) (not over 600) 53 | #QPKG_DESKTOP_APP_WIN_HEIGHT="" 54 | 55 | # Minimum QTS version requirement 56 | QTS_MINI_VERSION="4.3.3" 57 | # Maximum QTS version requirement 58 | QTS_MAX_VERSION="6.0.0" 59 | 60 | # Select volume 61 | # 1: support installation 62 | # 2: support migration 63 | # 3 (1+2): support both installation and migration 64 | QPKG_VOLUME_SELECT="3" 65 | 66 | # Set timeout for QPKG enable and QPKG disable (since 4.1.0) 67 | # Format in seconds (enable, disable) 68 | #QPKG_TIMEOUT="10,30" 69 | 70 | # Visible setting for the QPKG that has web UI, show this QPKG on the Main menu of 71 | # 1(default): administrators, 2: all NAS users. 72 | #QPKG_VISIBLE="2" 73 | 74 | # Location of icons for the packaged application. 75 | #QDK_DATA_DIR_ICONS="icons" 76 | # Location of files specific to arm-x19 packages. 77 | #QDK_DATA_DIR_X19="arm-x19" 78 | # Location of files specific to arm-x31 packages. 79 | #QDK_DATA_DIR_X31="arm-x31" 80 | # Location of files specific to arm-x41 packages. 81 | #QDK_DATA_DIR_X41="arm_al" 82 | # Location of files specific to x86 packages. 83 | #QDK_DATA_DIR_X86="x86" 84 | # Location of files specific to x86 (64-bit) packages. 85 | #QDK_DATA_DIR_X86_64="x86_64" 86 | # Location of files common to all architectures. 87 | #QDK_DATA_DIR_SHARED="shared" 88 | # Location of configuration files. 89 | #QDK_DATA_DIR_CONFIG="config" 90 | # Name of local data package. 91 | #QDK_DATA_FILE="" 92 | # Name of extra package (multiple definitions are allowed). 93 | #QDK_EXTRA_FILE="" 94 | # For QNAP code signing (currently can be done only inside QNAP) 95 | # Uncomment the following four options if you want to enable code signing for this QPKG 96 | #QNAP_CODE_SIGNING="0" 97 | #QNAP_CODE_SIGNING_SERVER_IP="codesigning.qnap.com.tw" 98 | #QNAP_CODE_SIGNING_SERVER_PORT="5001" 99 | #QNAP_CODE_SIGNING_CSV="build_sign.csv" -------------------------------------------------------------------------------- /packaging/shared/jellyfin-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CONF=/etc/config/qpkg.conf 3 | QPKG_NAME="jellyfin" 4 | QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}` 5 | CMD_SETCFG="/sbin/setcfg" 6 | 7 | set_qpkg_field(){ 8 | qpkg_field=$1 9 | value=$2 10 | current_value=`/sbin/getcfg ${QPKG_NAME} ${qpkg_field} -f ${CONF}` 11 | [ ! -z "${value}" ] && [ "${current_value}" != "${value}" ] && `${CMD_SETCFG} "${QPKG_NAME}" "${qpkg_field}" "${value}" -f "${CONF}"` 12 | } 13 | 14 | get_from_config(){ 15 | config_file=$1 16 | config_field=$2 17 | value=$(cat "$QPKG_ROOT/${config_file}" | grep "${config_field}" | cut -f2 -d\> | cut -f1 -d\< ) 18 | echo "${value}" 19 | } 20 | 21 | set_from_config(){ 22 | config_file=$1 23 | config_field=$2 24 | qpkg_field=$3 25 | default_value=$4 26 | value=$(get_from_config "${config_file}" "${config_field}") 27 | [ -z "${value}" ] && value="${default_value}" 28 | set_qpkg_field "${qpkg_field}" "${value}" 29 | } 30 | 31 | default_config(){ 32 | set_from_config "conf/network.xml" "InternalHttpPort" "Web_Port" "8096" 33 | 34 | https_enabled=$(get_from_config "conf/network.xml" "EnableHttps") 35 | if [ "$https_enabled" == "True" ]; then 36 | set_from_config "conf/network.xml" "InternalHttpsPort" "Web_SSL_Port" "8920" 37 | else 38 | set_qpkg_field "Web_SSL_Port" "-1" 39 | fi 40 | 41 | export TMPDIR="$QPKG_ROOT/cache/tmp" 42 | export QPKGS_PATHS="" 43 | if [ -d /opt/NVIDIA_GPU_DRV/usr/nvidia ]; then 44 | QPKGS_PATHS=":/opt/NVIDIA_GPU_DRV/usr/nvidia" 45 | fi 46 | export QPKGS_PATHS=":/opt/NVIDIA_GPU_DRV/usr/nvidia" 47 | } 48 | 49 | load_config(){ 50 | default_config 51 | if [ -f "$QPKG_ROOT/user-config.sh" ]; then 52 | source $QPKG_ROOT/user-config.sh; 53 | user_config 54 | fi 55 | } 56 | 57 | jellyfin_ffmpeg_start() { 58 | ## Look at the config for which VaapiDriver to use from the Jellyfin.Plugin.QnapConfiguration if installed 59 | LIBVA_FROM_CONFIG=`[ -f $QPKG_ROOT/database/plugins/configurations/Jellyfin.Plugin.QnapConfiguration.xml ] && cat $QPKG_ROOT/database/plugins/configurations/Jellyfin.Plugin.QnapConfiguration.xml | grep -E "([^<]+)" | cut -d">" -f2 | cut -d"<" -f1` 60 | if [ ! -z "${LIBVA_FROM_CONFIG}" ]; then 61 | if [ "$LIBVA_FROM_CONFIG" != "defaultValue" ]; then 62 | export LIBVA_DRIVER_NAME_JELLYFIN="$LIBVA_FROM_CONFIG" 63 | export LIBVA_DRIVER_NAME="$LIBVA_FROM_CONFIG" 64 | fi 65 | fi 66 | return 0 67 | } 68 | 69 | read_ini_file() { 70 | local ini_file=$1 71 | local section=$2 72 | local key=$3 73 | 74 | value=$(awk -F ' = ' -v section="$section" -v key="$key" ' 75 | $0 ~ "\\[" section "\\]" { in_section=1; next } 76 | $0 ~ "^\\[" { in_section=0 } 77 | in_section && $1 == key { print $2; exit } 78 | ' "$ini_file") 79 | echo "$value" 80 | } 81 | 82 | find_store() { 83 | local ini_file="/etc/config/3rd_pkg_v2.conf" 84 | local url=$1 85 | 86 | SECTIONS=`cat $ini_file | grep "\[.*\]"` 87 | for section in ${SECTIONS//[\[\]]}; do 88 | store_url=$(read_ini_file "$ini_file" "$section" "u") 89 | if [ $store_url == "$url" ]; then 90 | echo $section 91 | exit 92 | fi 93 | done 94 | } 95 | 96 | jellyfin_ffprobe_start() { 97 | ## Look at the config for which VaapiDriver to use from the Jellyfin.Plugin.QnapConfiguration if installed 98 | LIBVA_FROM_CONFIG=`[ -f $QPKG_ROOT/database/plugins/configurations/Jellyfin.Plugin.QnapConfiguration.xml ] && cat $QPKG_ROOT/database/plugins/configurations/Jellyfin.Plugin.QnapConfiguration.xml | grep -E "([^<]+)" | cut -d">" -f2 | cut -d"<" -f1` 99 | if [ ! -z "${LIBVA_FROM_CONFIG}" ]; then 100 | if [ "$LIBVA_FROM_CONFIG" != "defaultValue" ]; then 101 | export LIBVA_DRIVER_NAME_JELLYFIN="$LIBVA_FROM_CONFIG" 102 | export LIBVA_DRIVER_NAME="$LIBVA_FROM_CONFIG" 103 | fi 104 | fi 105 | return 0 106 | } 107 | 108 | jellyfin_vainfo_start() { 109 | return 0 110 | } 111 | 112 | jellyfin_server_start() { 113 | return 0 114 | } 115 | -------------------------------------------------------------------------------- /packaging/shared/jellyfin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CONF=/etc/config/qpkg.conf 3 | QPKG_NAME="jellyfin" 4 | QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}` 5 | CMD_SETCFG="/sbin/setcfg" 6 | 7 | PDULVP_STORE="https://pdulvp.github.io/qnap-store/repos.xml" 8 | PDULVP_PRE_STORE="https://pdulvp.github.io/qnap-store/repos-prereleases.xml" 9 | 10 | export PATH=$QPKG_ROOT/jellyfin/bin:$QPKG_ROOT/jellyfin-ffmpeg:$PATH 11 | 12 | source $QPKG_ROOT/jellyfin-config.sh 13 | 14 | jellyfin_start(){ 15 | /bin/ln -sf $QPKG_ROOT /opt/$QPKG_NAME 16 | /bin/ln -sf $QPKG_ROOT/jellyfin-ffmpeg /usr/lib/jellyfin-ffmpeg 17 | 18 | ENABLED=$(/sbin/getcfg $QPKG_NAME Enable -u -d FALSE -f $CONF) 19 | 20 | load_config 21 | mkdir -p $QPKG_ROOT/logs 22 | $QPKG_ROOT/jellyfin-ffmpeg/vainfo > $QPKG_ROOT/logs/vainfo-$(date -d "today" +"%Y%m%d%H%M").log 23 | $QPKG_ROOT/jellyfin/bin/jellyfin --datadir=$QPKG_ROOT/database --cachedir=$QPKG_ROOT/cache --webdir=$QPKG_ROOT/jellyfin-web --configdir=$QPKG_ROOT/conf --logdir=$QPKG_ROOT/logs --ffmpeg=$QPKG_ROOT/jellyfin-ffmpeg/ffmpeg --package-name=pdulvp & 24 | sleep 10 25 | } 26 | 27 | jellyfin_stop(){ 28 | ps aux | grep -ie jellyfin/bin/ld-linux | grep -v grep | awk '{print $1}' | xargs kill -9 29 | rm -rf /opt/$QPKG_NAME 30 | rm -rf /usr/lib/jellyfin-ffmpeg 31 | } 32 | 33 | link_to_store(){ 34 | store=$(find_store $1) 35 | ${CMD_SETCFG} "${QPKG_NAME}" "store" "$store" -f "${CONF}" 36 | } 37 | 38 | case "$1" in 39 | start) 40 | ENABLED=$(/sbin/getcfg $QPKG_NAME Enable -u -d FALSE -f $CONF) 41 | if [ "$ENABLED" != "TRUE" ]; then 42 | echo "$QPKG_NAME is disabled." 43 | exit 1 44 | fi 45 | jellyfin_start 46 | ;; 47 | 48 | stop) 49 | jellyfin_stop 50 | ;; 51 | 52 | restart) 53 | $0 stop 54 | $0 start 55 | ;; 56 | 57 | link_to_default_store) 58 | link_to_store "$PDULVP_STORE" 59 | ;; 60 | 61 | link_to_prerelease_store) 62 | link_to_store "$PDULVP_PRE_STORE" 63 | ;; 64 | 65 | *) 66 | echo "Usage: $0 {start|stop|restart|link_to_default_store|link_to_prerelease_store}" 67 | exit 1 68 | esac 69 | 70 | exit 0 71 | -------------------------------------------------------------------------------- /packaging/shared/user-config.sh.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | CONF=/etc/config/qpkg.conf 3 | QPKG_NAME="jellyfin" 4 | QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}` 5 | 6 | source $QPKG_ROOT/jellyfin-config.sh 7 | 8 | user_config(){ 9 | echo "Load user-configuration" 10 | 11 | #Access to jellyfin with the QTS port 12 | #set_qpkg_field "Use_Proxy" "1" 13 | #set_qpkg_field "Proxy_Path" "/jellyfin/" 14 | 15 | #export http_proxy="http://xxxx:xx" 16 | #export https_proxy="http://xxxx:xx" 17 | #export no_proxy= "192.168.168.0/24,localhost,127.0.0.1" 18 | return 0 19 | } 20 | -------------------------------------------------------------------------------- /plugins/.editorconfig: -------------------------------------------------------------------------------- 1 | # With more recent updates Visual Studio 2017 supports EditorConfig files out of the box 2 | # Visual Studio Code needs an extension: https://github.com/editorconfig/editorconfig-vscode 3 | # For emacs, vim, np++ and other editors, see here: https://github.com/editorconfig 4 | ############################### 5 | # Core EditorConfig Options # 6 | ############################### 7 | root = true 8 | # All files 9 | [*] 10 | indent_style = space 11 | indent_size = 4 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | end_of_line = lf 16 | max_line_length = off 17 | 18 | # YAML indentation 19 | [*.{yml,yaml}] 20 | indent_size = 2 21 | 22 | # XML indentation 23 | [*.{csproj,xml}] 24 | indent_size = 2 25 | 26 | ############################### 27 | # .NET Coding Conventions # 28 | ############################### 29 | [*.{cs,vb}] 30 | # Organize usings 31 | dotnet_sort_system_directives_first = true 32 | # this. preferences 33 | dotnet_style_qualification_for_field = false:silent 34 | dotnet_style_qualification_for_property = false:silent 35 | dotnet_style_qualification_for_method = false:silent 36 | dotnet_style_qualification_for_event = false:silent 37 | # Language keywords vs BCL types preferences 38 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 39 | dotnet_style_predefined_type_for_member_access = true:silent 40 | # Parentheses preferences 41 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 42 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 43 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 44 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 45 | # Modifier preferences 46 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 47 | dotnet_style_readonly_field = true:suggestion 48 | # Expression-level preferences 49 | dotnet_style_object_initializer = true:suggestion 50 | dotnet_style_collection_initializer = true:suggestion 51 | dotnet_style_explicit_tuple_names = true:suggestion 52 | dotnet_style_null_propagation = true:suggestion 53 | dotnet_style_coalesce_expression = true:suggestion 54 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent 55 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 57 | dotnet_style_prefer_auto_properties = true:silent 58 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 59 | dotnet_style_prefer_conditional_expression_over_return = true:silent 60 | 61 | ############################### 62 | # Naming Conventions # 63 | ############################### 64 | # Style Definitions (From Roslyn) 65 | 66 | # Non-private static fields are PascalCase 67 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion 68 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields 69 | dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style 70 | 71 | dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field 72 | dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected 73 | dotnet_naming_symbols.non_private_static_fields.required_modifiers = static 74 | 75 | dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case 76 | 77 | # Constants are PascalCase 78 | dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion 79 | dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants 80 | dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style 81 | 82 | dotnet_naming_symbols.constants.applicable_kinds = field, local 83 | dotnet_naming_symbols.constants.required_modifiers = const 84 | 85 | dotnet_naming_style.constant_style.capitalization = pascal_case 86 | 87 | # Static fields are camelCase and start with s_ 88 | dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion 89 | dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields 90 | dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style 91 | 92 | dotnet_naming_symbols.static_fields.applicable_kinds = field 93 | dotnet_naming_symbols.static_fields.required_modifiers = static 94 | 95 | dotnet_naming_style.static_field_style.capitalization = camel_case 96 | dotnet_naming_style.static_field_style.required_prefix = _ 97 | 98 | # Instance fields are camelCase and start with _ 99 | dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion 100 | dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields 101 | dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style 102 | 103 | dotnet_naming_symbols.instance_fields.applicable_kinds = field 104 | 105 | dotnet_naming_style.instance_field_style.capitalization = camel_case 106 | dotnet_naming_style.instance_field_style.required_prefix = _ 107 | 108 | # Locals and parameters are camelCase 109 | dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion 110 | dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters 111 | dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style 112 | 113 | dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local 114 | 115 | dotnet_naming_style.camel_case_style.capitalization = camel_case 116 | 117 | # Local functions are PascalCase 118 | dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion 119 | dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions 120 | dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style 121 | 122 | dotnet_naming_symbols.local_functions.applicable_kinds = local_function 123 | 124 | dotnet_naming_style.local_function_style.capitalization = pascal_case 125 | 126 | # By default, name items with PascalCase 127 | dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion 128 | dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members 129 | dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style 130 | 131 | dotnet_naming_symbols.all_members.applicable_kinds = * 132 | 133 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 134 | 135 | ############################### 136 | # C# Coding Conventions # 137 | ############################### 138 | [*.cs] 139 | # var preferences 140 | csharp_style_var_for_built_in_types = true:silent 141 | csharp_style_var_when_type_is_apparent = true:silent 142 | csharp_style_var_elsewhere = true:silent 143 | # Expression-bodied members 144 | csharp_style_expression_bodied_methods = false:silent 145 | csharp_style_expression_bodied_constructors = false:silent 146 | csharp_style_expression_bodied_operators = false:silent 147 | csharp_style_expression_bodied_properties = true:silent 148 | csharp_style_expression_bodied_indexers = true:silent 149 | csharp_style_expression_bodied_accessors = true:silent 150 | # Pattern matching preferences 151 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 152 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 153 | # Null-checking preferences 154 | csharp_style_throw_expression = true:suggestion 155 | csharp_style_conditional_delegate_call = true:suggestion 156 | # Modifier preferences 157 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion 158 | # Expression-level preferences 159 | csharp_prefer_braces = true:silent 160 | csharp_style_deconstructed_variable_declaration = true:suggestion 161 | csharp_prefer_simple_default_expression = true:suggestion 162 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 163 | csharp_style_inlined_variable_declaration = true:suggestion 164 | 165 | ############################### 166 | # C# Formatting Rules # 167 | ############################### 168 | # New line preferences 169 | csharp_new_line_before_open_brace = all 170 | csharp_new_line_before_else = true 171 | csharp_new_line_before_catch = true 172 | csharp_new_line_before_finally = true 173 | csharp_new_line_before_members_in_object_initializers = true 174 | csharp_new_line_before_members_in_anonymous_types = true 175 | csharp_new_line_between_query_expression_clauses = true 176 | # Indentation preferences 177 | csharp_indent_case_contents = true 178 | csharp_indent_switch_labels = true 179 | csharp_indent_labels = flush_left 180 | # Space preferences 181 | csharp_space_after_cast = false 182 | csharp_space_after_keywords_in_control_flow_statements = true 183 | csharp_space_between_method_call_parameter_list_parentheses = false 184 | csharp_space_between_method_declaration_parameter_list_parentheses = false 185 | csharp_space_between_parentheses = false 186 | csharp_space_before_colon_in_inheritance_clause = true 187 | csharp_space_after_colon_in_inheritance_clause = true 188 | csharp_space_around_binary_operators = before_and_after 189 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 190 | csharp_space_between_method_call_name_and_opening_parenthesis = false 191 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 192 | # Wrapping preferences 193 | csharp_preserve_single_line_statements = true 194 | csharp_preserve_single_line_blocks = true 195 | -------------------------------------------------------------------------------- /plugins/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | .vs/ 4 | .idea/ 5 | artifacts 6 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.1.32228.430 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Plugin.QnapConfiguration", "Jellyfin.Plugin.QnapConfiguration\Jellyfin.Plugin.QnapConfiguration.csproj", "{D921B930-CF91-406F-ACBC-08914DCD0D34}" 6 | EndProject 7 | Project("{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}") = "jellyfin-1080", "C:\Tools\jellyfin\jellyfin-server_1080\jellyfin-1080.exe", "{39686033-B849-4675-BB74-A76A281AE2D9}" 8 | ProjectSection(DebuggerProjectSystem) = preProject 9 | PortSupplier = 00000000-0000-0000-0000-000000000000 10 | Executable = C:\Tools\jellyfin\jellyfin-server_1080\jellyfin-1080.exe 11 | RemoteMachine = 12 | StartingDirectory = C:\Tools\jellyfin\jellyfin-server_1080 13 | Arguments = --datadir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1080\database --cachedir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1080\cache --webdir=.\jellyfin-web --configdir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1080\conf --logdir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1080\logs 14 | Environment = Par d�faut 15 | LaunchingEngine = 2e36f1d4-b23c-435d-ab41-18e608940038 16 | UseLegacyDebugEngines = No 17 | LaunchSQLEngine = No 18 | AttachLaunchAction = No 19 | IORedirection = Auto 20 | EndProjectSection 21 | EndProject 22 | Project("{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}") = "jellyfin-1077", "C:\Tools\jellyfin\jellyfin-server_1077\jellyfin-1077.exe", "{39686033-B849-4675-BB74-A76A281AE2D5}" 23 | ProjectSection(DebuggerProjectSystem) = preProject 24 | PortSupplier = 00000000-0000-0000-0000-000000000000 25 | Executable = C:\Tools\jellyfin\jellyfin-server_1077\jellyfin-1077.exe 26 | RemoteMachine = 27 | StartingDirectory = C:\Tools\jellyfin\jellyfin-server_1077 28 | Arguments = --datadir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1077\database --cachedir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1077\cache --webdir=.\jellyfin-web --configdir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1077\conf --logdir=C:\Works\jellyfin-qnap\plugins\Jellyfin.Plugin.QnapConfiguration\bin\server-1077\logs 29 | Environment = Par d�faut 30 | LaunchingEngine = 2e36f1d4-b23c-435d-ab41-18e608940038 31 | UseLegacyDebugEngines = No 32 | LaunchSQLEngine = No 33 | AttachLaunchAction = No 34 | IORedirection = Auto 35 | EndProjectSection 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Debug|x64 = Debug|x64 41 | Release|Any CPU = Release|Any CPU 42 | Release|x64 = Release|x64 43 | EndGlobalSection 44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 45 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|x64.ActiveCfg = Debug|Any CPU 48 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Debug|x64.Build.0 = Debug|Any CPU 49 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|x64.ActiveCfg = Release|Any CPU 52 | {D921B930-CF91-406F-ACBC-08914DCD0D34}.Release|x64.Build.0 = Release|Any CPU 53 | {39686033-B849-4675-BB74-A76A281AE2D9}.Debug|Any CPU.ActiveCfg = Release|x64 54 | {39686033-B849-4675-BB74-A76A281AE2D9}.Debug|x64.ActiveCfg = Release|x64 55 | {39686033-B849-4675-BB74-A76A281AE2D9}.Release|Any CPU.ActiveCfg = Release|x64 56 | {39686033-B849-4675-BB74-A76A281AE2D9}.Release|x64.ActiveCfg = Release|x64 57 | {39686033-B849-4675-BB74-A76A281AE2D5}.Debug|Any CPU.ActiveCfg = Release|x64 58 | {39686033-B849-4675-BB74-A76A281AE2D5}.Debug|x64.ActiveCfg = Release|x64 59 | {39686033-B849-4675-BB74-A76A281AE2D5}.Release|Any CPU.ActiveCfg = Release|x64 60 | {39686033-B849-4675-BB74-A76A281AE2D5}.Release|x64.ActiveCfg = Release|x64 61 | EndGlobalSection 62 | GlobalSection(SolutionProperties) = preSolution 63 | HideSolutionNode = FALSE 64 | EndGlobalSection 65 | GlobalSection(ExtensibilityGlobals) = postSolution 66 | SolutionGuid = {F386BAC8-2A2F-4B1C-A665-44DCD3500EBE} 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration/Configuration/PluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | using MediaBrowser.Model.Plugins; 2 | 3 | namespace Jellyfin.Plugin.QnapConfiguration.Configuration 4 | { 5 | /// 6 | /// The configuration options. 7 | /// 8 | public enum SomeOptions 9 | { 10 | /// 11 | /// Second option. 12 | /// 13 | defaultValue, 14 | 15 | /// 16 | /// Option one. 17 | /// 18 | iHD, 19 | 20 | /// 21 | /// Second option. 22 | /// 23 | i965, 24 | } 25 | 26 | /// 27 | /// Plugin configuration. 28 | /// 29 | public class PluginConfiguration : BasePluginConfiguration 30 | { 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | public PluginConfiguration() 35 | { 36 | // set default options here 37 | VaapiDriver = SomeOptions.defaultValue; 38 | } 39 | 40 | /// 41 | /// Gets or sets an enum option. 42 | /// 43 | public SomeOptions VaapiDriver { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration/Configuration/configPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jellyfin.QnapConfiguration 6 | 7 | 8 |
9 |
10 |
11 |
12 |
13 | 14 | 19 |
20 |
21 | 24 |
25 |
26 |
27 |
28 | 55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration/Jellyfin.Plugin.QnapConfiguration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Jellyfin.Plugin.QnapConfiguration 6 | 1.0.1.1 7 | 1.0.1.1 8 | false 9 | true 10 | enable 11 | AllEnabledByDefault 12 | ../jellyfin.ruleset 13 | bin\ 14 | en-US 15 | Jellyfin.Plugin.QnapConfiguration 16 | obj\ 17 | 18 | 19 | 20 | True 21 | 22 | 23 | 24 | True 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration/Plugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using MediaBrowser.Common.Configuration; 5 | using MediaBrowser.Common.Plugins; 6 | using MediaBrowser.Model.Plugins; 7 | using MediaBrowser.Model.Serialization; 8 | using Jellyfin.Plugin.QnapConfiguration.Configuration; 9 | 10 | namespace Jellyfin.Plugin.QnapConfiguration 11 | { 12 | /// 13 | /// The main plugin. 14 | /// 15 | public class Plugin : BasePlugin, IHasWebPages 16 | { 17 | 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// Instance of the interface. 22 | /// Instance of the interface. 23 | public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) 24 | : base(applicationPaths, xmlSerializer) 25 | { 26 | Instance = this; 27 | } 28 | 29 | /// 30 | public override string Name => "QNAP.Configuration"; 31 | 32 | /// 33 | public override Guid Id => Guid.Parse("a575773e-ff2f-4bf9-8dc1-d3ce1d957a7f"); 34 | 35 | /// 36 | /// Gets the current plugin instance. 37 | /// 38 | public static Plugin? Instance { get; private set; } 39 | 40 | /// 41 | public IEnumerable GetPages() 42 | { 43 | return new[] 44 | { 45 | new PluginPageInfo 46 | { 47 | Name = this.Name, 48 | EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.html", GetType().Namespace) 49 | } 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /plugins/Jellyfin.Plugin.QnapConfiguration/copy.bat: -------------------------------------------------------------------------------- 1 | mkdir "bin\server-10100\database\plugins\Jellyfin.Plugin.QnapConfiguration" 2 | 3 | copy "bin\Debug\net8.0\Jellyfin.Plugin.QnapConfiguration.*" "bin\server-10100\database\plugins\Jellyfin.Plugin.QnapConfiguration" 4 | -------------------------------------------------------------------------------- /plugins/jellyfin.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | -------------------------------------------------------------------------------- /prefetch-lib-legacy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="$1" 4 | BUILD_DEB="$2" 5 | ARCH=$3 6 | 7 | mkdir -p .tmp/lib; 8 | if [ -d ".cache/$VERSION-$ARCH/deb" ]; then 9 | echo -e "\033[0;32mPrefetch dependencies $BUILD_DEB from cache \033[0m" 10 | cp .cache/$VERSION-$ARCH/deb/* .tmp/lib 11 | exit 0 12 | fi 13 | 14 | echo "Prefetch dependencies $BUILD_DEB" 15 | mkdir -p .tmp/lib-unary; 16 | cp "$BUILD_DEB" .tmp/lib-unary 17 | cd .tmp/lib-unary 18 | ls *.deb | cat > root.txt 19 | 20 | getDependencies() { 21 | DEPENDENCIES=$(dpkg-deb -I "$1" | grep "^ Depends:" | cut -c 11- | sed "s/[ ]//g" | sed "s/[(][^)]*[)]//g" ) 22 | echo "$DEPENDENCIES" | sed -E "s/[|]([a-zA-Z0-9\.\-]+,)/,/g" | tr -s "," "\n" | sort | uniq 23 | } 24 | 25 | getInnerLibs() { 26 | FILE=$(basename $1) 27 | mkdir "f_$FILE" 28 | cd "f_$FILE" 29 | ar x "../$1" data.tar.xz 30 | tar xf data.tar.xz ./usr/lib/x86_64-linux-gnu/ 2>/dev/null 31 | tar xf data.tar.xz ./lib/x86_64-linux-gnu/ 2>/dev/null 32 | LIBS=$(find . -name "lib*.so*" -exec basename {} \; | grep -v "libLLVM") 33 | cd .. 34 | rm -rf "f_$FILE" 35 | echo "$LIBS" | sort | uniq 36 | } 37 | 38 | #for all .deb recursively in the current folder, we fetch dependencies 39 | echo "" > done.txt 40 | while true; do 41 | cat currentDependencies.txt > dependencies.txt 42 | rm -f currentDependencies.txt 43 | echo " ######################### " 44 | cat dependencies.txt 45 | while read line; do 46 | ISDONE=$(cat done.txt | grep -q $line; echo $?) 47 | if [ $ISDONE -ne 0 ]; then 48 | LIBS=$(getInnerLibs $line) 49 | HASLIBS=$(echo $LIBS | grep -q .so; echo $?) 50 | LIBFILE=$(basename "$line") 51 | ISROOT=$(cat root.txt | grep -q "${LIBFILE}$"; echo $?) 52 | if [ $ISROOT -eq 0 ]; then 53 | for var in $(getDependencies $line); do 54 | apt-get download "$var" -o APT::Architecture=$ARCH 55 | done 56 | 57 | elif [ $HASLIBS -ne 0 ]; then 58 | rm -f "$line" 59 | else 60 | for var in $(getDependencies $line); do 61 | apt-get download "$var" -o APT::Architecture=$ARCH 62 | done 63 | fi 64 | echo "$line" >> done.txt 65 | fi 66 | done < dependencies.txt 67 | 68 | find . -type f -name "*.deb" > currentDependencies.txt 69 | 70 | F1=$(md5sum dependencies.txt | cut -d" " -f1) 71 | F2=$(md5sum currentDependencies.txt | cut -d" " -f1) 72 | echo $F1 $F2 73 | [[ "$F1" != "$F2" ]] || break; 74 | done 75 | 76 | cd ../.. 77 | rm .tmp/lib-unary/$BUILD_DEB 78 | mkdir -p .cache/$VERSION-$ARCH/deb; 79 | cp .tmp/lib-unary/* .cache/$VERSION-$ARCH/deb 80 | # Remove some debs that doesnt contain libs 81 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*cross* 82 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*dbg* 83 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*doc* 84 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*dev* 85 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*pic* 86 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*eabi* 87 | rm -rf .cache/$VERSION-$ARCH/deb/perl* 88 | rm -rf .cache/$VERSION-$ARCH/deb/python* 89 | rm -rf .cache/$VERSION-$ARCH/deb/man-db* 90 | rm -rf .cache/$VERSION-$ARCH/deb/gcc-* 91 | rm -rf .cache/$VERSION-$ARCH/deb/cpp-* 92 | cp .cache/$VERSION-$ARCH/deb/* .tmp/lib 93 | rm -rf .tmp/lib-unary 94 | exit 0 95 | -------------------------------------------------------------------------------- /prefetch-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="$1" 4 | BUILD_INFO="$2" 5 | ARCH=$3 6 | 7 | mkdir -p .tmp/lib; 8 | if [ -d ".cache/$VERSION-$ARCH/deb" ]; then 9 | echo -e "\033[0;32mPrefetch dependencies $BUILD_INFO from cache \033[0m" 10 | cp .cache/$VERSION-$ARCH/deb/* .tmp/lib 11 | exit 0 12 | fi 13 | 14 | echo "Prefetch dependencies $BUILD_INFO" 15 | mkdir -p .tmp/lib-unary; 16 | cp "$BUILD_INFO" .tmp/lib-unary 17 | cd .tmp/lib-unary 18 | 19 | getDependencies() { 20 | echo $(awk ' 21 | /^Installed-Build-Depends:/ || /^ / && deps { 22 | sub(/^[^ ]+: /, "") 23 | deps = 1 24 | dep_str = dep_str ", " $0 25 | next 26 | } 27 | { deps=0 } 28 | END { 29 | split(dep_str, dep_array, /[,|] */) 30 | for (d in dep_array) { 31 | dep = dep_array[d] 32 | gsub(/[^a-z0-9_.-].*$/, "", dep) 33 | if (dep && !seen[dep]++) print dep 34 | } 35 | }' $1) | sort | uniq 36 | echo "libpciaccess0" #vainfo 37 | echo "libstdc++" #jellyfin 38 | echo "libgnutls30" #jellyfin-ffmpeg 39 | } 40 | 41 | for var in $(getDependencies $BUILD_INFO); do 42 | apt-get download "$var" -o APT::Architecture=$ARCH 2>>output.err 43 | done 44 | 45 | cd ../.. 46 | mkdir -p .cache/$VERSION-$ARCH/deb 47 | cp .tmp/lib-unary/* .cache/$VERSION-$ARCH/deb 48 | # Remove some debs that doesnt contain libs 49 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*cross* 50 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*dbg* 51 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*doc* 52 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*dev* 53 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*pic* 54 | rm -rf .cache/$VERSION-$ARCH/deb/libstdc*eabi* 55 | rm -rf .cache/$VERSION-$ARCH/deb/perl* 56 | rm -rf .cache/$VERSION-$ARCH/deb/python* 57 | rm -rf .cache/$VERSION-$ARCH/deb/man-db* 58 | rm -rf .cache/$VERSION-$ARCH/deb/gcc-* 59 | rm -rf .cache/$VERSION-$ARCH/deb/cpp-* 60 | cp .cache/$VERSION-$ARCH/deb/* .tmp/lib 61 | rm -rf .tmp/lib-unary 62 | 63 | echo "Finished prefetch dependencies $VERSION" 64 | exit 0 65 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=$(cat package.json | jq -r .version) 4 | VERSION_SHA=$(cat package.json | jq -r .sha) 5 | SERVER_VERSION=$(cat package.json | jq -r .server) 6 | WEB_VERSION=$(cat package.json | jq -r .web) 7 | FFMPEG_VERSION=$(cat package.json | jq -r .ffmpeg) 8 | FFMPEG5_VERSION=$(cat package.json | jq -r .ffmpeg5) 9 | SUFFIX=$(cat package.json | jq -r .suffix) 10 | PRERELEASE=true 11 | if [ $SUFFIX != "" ]; then 12 | SUFFIX="-$SUFFIX" 13 | fi 14 | RELEASE_NAME="${SERVER_VERSION}${SUFFIX}" 15 | TAG_VERSION="${VERSION}${SUFFIX}_${VERSION_SHA:0:8}" 16 | 17 | LABEL=$RELEASE_NAME 18 | DESC="Version based on: \`jellyfin-server_$SERVER_VERSION\` \`jellyfin-web_$WEB_VERSION\` \`jellyfin-ffmpeg_$FFMPEG_VERSION\` \`jellyfin-ffmpeg5_$FFMPEG5_VERSION\`" 19 | 20 | git pull bot HEAD 21 | git config user.email "pdulvp-bot@laposte.net" 22 | git config user.name "pdulvp-bot" 23 | git add "package.json" 24 | git add "jellyfin/qpkg.cfg" 25 | git commit -m "$TAG_VERSION ($SERVER_VERSION)" 26 | git tag "$TAG_VERSION" 27 | git push bot HEAD:master 28 | git push bot "$TAG_VERSION" 29 | git config user.email "pdulvp@laposte.net" 30 | git config user.name "pdulvp" 31 | 32 | RELEASE_ID=`curl -i -X POST -H "Content-Type:application/json" -H "Authorization: token $GITHUB_BOT_TOKEN" https://api.github.com/repos/pdulvp/jellyfin-qnap/releases -d "{\"tag_name\":\"$TAG_VERSION\", \"target_commitish\":\"master\",\"name\": \"$RELEASE_NAME\", \"body\": \"$DESC\", \"draft\": false, \"prerelease\": $PRERELEASE}" ` 33 | echo "$RELEASE_ID" 34 | RELEASE_ID=`echo $RELEASE_ID | grep -o -P "(?<=\"id\": )\d+" | head -n 1` 35 | echo "RELEASE=$RELEASE_ID" 36 | 37 | for FILE in $(find build/ -name "jellyfin_*$RELEASE_NAME*.qpkg"); do 38 | NAME=$(basename $FILE); 39 | echo "Publish $FILE" 40 | curl -X POST \ 41 | -H "Authorization: token $GITHUB_BOT_TOKEN" \ 42 | -H "Content-Type: $(file -b --mime-type $FILE)" \ 43 | --data-binary @$FILE \ 44 | "https://uploads.github.com/repos/pdulvp/jellyfin-qnap/releases/$RELEASE_ID/assets?name=$NAME&label=$NAME" | cat 45 | 46 | if [ -f $FILE.md5 ]; then 47 | echo "Publish $FILE.md5" 48 | curl -X POST \ 49 | -H "Authorization: token $GITHUB_BOT_TOKEN" \ 50 | -H "Content-Type: $(file -b --mime-type $FILE.md5)" \ 51 | --data-binary @$FILE.md5 \ 52 | "https://uploads.github.com/repos/pdulvp/jellyfin-qnap/releases/$RELEASE_ID/assets?name=$NAME.md5&label=$NAME.md5" | cat 53 | fi 54 | done 55 | -------------------------------------------------------------------------------- /unpack-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KEY=$1 4 | if [ -d .cache/$KEY ]; then 5 | echo -e "\033[0;32mUnpack dependencies $1 from cache \033[0m" 6 | cp -r .cache/$KEY/lib .cache/$KEY/usr .tmp/lib 7 | exit 0 8 | fi 9 | 10 | echo "Unpack dependencies $1 from debs" 11 | mkdir -p .tmp/lib/lib; 12 | mkdir -p .tmp/lib/usr/lib; 13 | cd .tmp/lib 14 | 15 | for file in $(find . -type f -name "*.deb"); do 16 | echo "--- $file" 17 | ar x $file data.tar.xz 18 | tar xf data.tar.xz ./usr/lib/x86_64-linux-gnu/ 2>/dev/null 19 | tar xf data.tar.xz ./lib/x86_64-linux-gnu/ 2>/dev/null 20 | tar xf data.tar.xz ./usr/lib/aarch64-linux-gnu/ 2>/dev/null 21 | tar xf data.tar.xz ./lib/aarch64-linux-gnu/ 2>/dev/null 22 | tar xf data.tar.xz ./usr/lib/arm-linux-gnueabihf/ 2>/dev/null 23 | tar xf data.tar.xz ./lib/arm-linux-gnueabihf/ 2>/dev/null 24 | rm -f *.tar.xz 25 | done 26 | 27 | cd ../.. 28 | mkdir -p .cache/$KEY; 29 | cp -r .tmp/lib/lib .tmp/lib/usr .cache/$KEY 30 | exit 0 31 | --------------------------------------------------------------------------------