├── .flake8
├── .github
├── CODEOWNERS
├── renovate.json
└── workflows
│ ├── preview-build-winonly.yaml
│ ├── preview-build.yaml
│ ├── release-build.yaml
│ └── unstable-build.yaml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── build.py
├── build.yaml
├── checkout.py
├── debian
├── changelog.in
├── compat
├── conf
│ ├── jellyfin
│ ├── jellyfin.init
│ ├── jellyfin.service
│ ├── jellyfin.service.conf
│ ├── jellyfin.upstart
│ └── logging.json
├── control
├── copyright
├── docker
│ ├── Dockerfile
│ └── build.sh
├── gbp.conf
├── jellyfin-server.conffiles
├── jellyfin-server.install
├── jellyfin-server.postinst
├── jellyfin-server.postrm
├── jellyfin-server.preinst
├── jellyfin-server.prerm
├── jellyfin-web.conffiles
├── jellyfin-web.install
├── po
│ ├── POTFILES.in
│ └── templates.pot
├── rules
└── source
│ ├── format
│ └── options
├── docker
└── Dockerfile
├── portable
├── Dockerfile
└── build.sh
└── requirements.txt
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | # We ignore the following errors:
3 | # * W503 (line break before binary operator): Black moves these to new lines
4 | # * E501 (line too long): Long lines are a fact of life in comment blocks; Black handles active instances of this
5 | # * E203 (whitespace before ':'): Black recommends this as disabled
6 | ignore = W503, E501
7 | extend-ignore = E203
8 | # Set the max line length to 88 for Black
9 | max-line-length = 88
10 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Joshua must review all changes to deployment and packaging
2 | * @joshuaboniface
3 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["github>jellyfin/.github//renovate-presets/default"]
4 | }
5 |
--------------------------------------------------------------------------------
/.github/workflows/preview-build-winonly.yaml:
--------------------------------------------------------------------------------
1 | name: "Preview Build Windows-Only"
2 |
3 | on:
4 | workflow_dispatch:
5 | # Manual trigger (from bot)
6 | inputs:
7 | version:
8 | required: true
9 | type: string
10 | description: 'The server and web stable release tag (i.e. "vX.Y.Z")'
11 | preview_id:
12 | required: true
13 | type: string
14 | description: 'The numerical preview ID (e.g. 1 for `rc1`)'
15 |
16 | env:
17 | SDK_VERSION: "9.0.x"
18 |
19 | permissions:
20 | contents: read
21 |
22 | jobs:
23 | Windows:
24 | runs-on: ubuntu-24.04
25 | strategy:
26 | fail-fast: false
27 | matrix:
28 | arch:
29 | - amd64
30 | - arm64
31 | outputs:
32 | JELLYFIN_VERSION: ${{ steps.version.outputs.JELLYFIN_VERSION }}
33 | JELLYFIN_RELEASE_TYPE: ${{ steps.version.outputs.JELLYFIN_RELEASE_TYPE }}
34 | continue-on-error: false # true in prod, false for testing
35 | steps:
36 | - name: "Set dated version for unstable builds"
37 | id: version
38 | run: |-
39 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
40 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_OUTPUT
41 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
42 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_OUTPUT
43 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
44 | echo "DEBUG_FLAG=--debug" >>$GITHUB_OUTPUT
45 |
46 | - name: "Install dependencies"
47 | run: |-
48 | sudo apt-get update
49 | sudo apt-get install --yes python3-git python3-yaml
50 |
51 | - name: "Checkout repository"
52 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
53 |
54 | - name: "Prepare repository"
55 | run: |-
56 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
57 |
58 | - name: "Run builder for ${{ matrix.arch }}"
59 | run: |-
60 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} windows ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
61 |
62 | - name: "Upload artifacts to repository server"
63 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
64 | with:
65 | host: "${{ secrets.REPO_HOST }}"
66 | username: "${{ secrets.REPO_USER }}"
67 | key: "${{ secrets.REPO_KEY }}"
68 | source: "out/windows/*"
69 | strip_components: 2
70 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
71 |
72 | - name: "Move artifacts into repository"
73 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
74 | with:
75 | host: "${{ secrets.REPO_HOST }}"
76 | username: "${{ secrets.REPO_USER }}"
77 | key: "${{ secrets.REPO_KEY }}"
78 | debug: false
79 | script: |
80 | set -e
81 | export BASEDIR="/srv/repository/main/server/windows"
82 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
83 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
84 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
85 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
86 |
87 | - name: "Store artifacts for next stage"
88 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
89 | with:
90 | name: windows-artifacts
91 | retention-days: 1
92 | if-no-files-found: ignore # Ignore for arm64 build, as no file will be found
93 | path: out/windows/jellyfin_*-amd64.zip
94 |
95 | WindowsInstaller:
96 | needs:
97 | - Windows
98 | runs-on: windows-latest
99 | strategy:
100 | fail-fast: false
101 | matrix:
102 | arch:
103 | - amd64
104 | continue-on-error: false # true in prod, false for testing
105 | env:
106 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
107 | steps:
108 | - name: "Set dated version for unstable builds"
109 | id: version
110 | shell: pwsh
111 | run: |
112 | $version = "${{ needs.Windows.outputs.JELLYFIN_VERSION }}"
113 | $cleanVersion = $version.Substring(1)
114 | echo "JELLYFIN_VERSION=$cleanVersion" | Out-File -FilePath $env:GITHUB_ENV -Append
115 |
116 | - name: "Checkout repository"
117 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
118 |
119 | - name: "Install dependencies"
120 | run: |-
121 | python -m pip install --upgrade gitpython
122 |
123 | - name: "Install NSIS"
124 | uses: negrutiu/nsis-install@f3339c88dba6fd08910d5275a943f8f746d94876 # v2
125 | with:
126 | distro: official
127 |
128 | - name: "Prepare repository"
129 | run: |-
130 | python ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
131 |
132 | - name: "Fetch artifacts from previous stage"
133 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
134 | with:
135 | name: windows-artifacts
136 | path: ./jellyfin-server-windows
137 |
138 | - name: "Clone UX repository"
139 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
140 | with:
141 | repository: jellyfin/jellyfin-ux
142 | path: .\jellyfin-server-windows\jellyfin-ux
143 |
144 | - name: "Extract Jellyfin server archive"
145 | working-directory: ./jellyfin-server-windows
146 | run: |
147 | New-Item -ItemType Directory -Path jellyfin
148 | Expand-Archive -Path 'jellyfin_*-amd64.zip' -DestinationPath jellyfin -Force
149 | Copy-Item ".\Support Files\LICENSE" -Destination $(Resolve-Path .\jellyfin\jellyfin)
150 |
151 | - name: "Add NSSM"
152 | working-directory: ./jellyfin-server-windows
153 | run: |
154 | Invoke-WebRequest 'https://repo.jellyfin.org/files/other/nssm.zip' -OutFile 'nssm.zip'
155 | Expand-Archive 'nssm.zip'
156 | Copy-Item ".\nssm\nssm.exe" -Destination $(Resolve-Path .\jellyfin\jellyfin)
157 |
158 | - name: "Publish tray"
159 | working-directory: ./jellyfin-server-windows
160 | run: |
161 | New-Item -Path .\jellyfin\jellyfin\jellyfin-windows-tray -ItemType Directory
162 | dotnet publish -c Release -r win-x64 -f net472 --no-self-contained --output $(Resolve-Path .\jellyfin\jellyfin\jellyfin-windows-tray)
163 |
164 | - name: "Build installer"
165 | working-directory: ./jellyfin-server-windows
166 | run: |
167 | $env:InstallLocation = $(Resolve-Path .\jellyfin\jellyfin)
168 | makensis /Dx64 /DUXPATH=$(Resolve-Path .\jellyfin-ux) $(Join-Path -Path $(Resolve-Path .\nsis) -ChildPath jellyfin.nsi)
169 |
170 | - name: "Rename installer"
171 | working-directory: ./jellyfin-server-windows/nsis
172 | run: |
173 | Rename-Item -Path .\jellyfin_*_windows-x64.exe -NewName ("jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe")
174 |
175 | - name: "Store artifacts for next stage"
176 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
177 | with:
178 | name: windows-installer-artifacts
179 | retention-days: 1
180 | if-no-files-found: error
181 | path: ./jellyfin-server-windows/nsis/jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe
182 |
183 | WindowsInstallerUpload:
184 | needs:
185 | - Windows
186 | - WindowsInstaller
187 | runs-on: ubuntu-24.04
188 | strategy:
189 | fail-fast: false
190 | matrix:
191 | arch:
192 | - amd64
193 | continue-on-error: false # true in prod, false for testing
194 | steps:
195 | - name: "Set version from previous job"
196 | id: version
197 | run: |-
198 | echo "JELLYFIN_VERSION=${{ needs.Windows.outputs.JELLYFIN_VERSION }}" >> $GITHUB_ENV
199 | echo "JELLYFIN_RELEASE_TYPE=${{ needs.Windows.outputs.JELLYFIN_RELEASE_TYPE }}" >> $GITHUB_ENV
200 |
201 | - name: "Fetch artifact from previous stage"
202 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
203 | with:
204 | name: windows-installer-artifacts
205 |
206 | - name: "Upload artifacts to repository server"
207 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
208 | with:
209 | host: "${{ secrets.REPO_HOST }}"
210 | username: "${{ secrets.REPO_USER }}"
211 | key: "${{ secrets.REPO_KEY }}"
212 | source: "*.exe"
213 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
214 |
215 | - name: "Move artifacts into repository"
216 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
217 | with:
218 | host: "${{ secrets.REPO_HOST }}"
219 | username: "${{ secrets.REPO_USER }}"
220 | key: "${{ secrets.REPO_KEY }}"
221 | debug: false
222 | script: |
223 | set -e
224 | export BASEDIR="/srv/repository/main/server/windows"
225 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}/*.exe
226 |
--------------------------------------------------------------------------------
/.github/workflows/preview-build.yaml:
--------------------------------------------------------------------------------
1 | name: "Preview Build"
2 |
3 | on:
4 | workflow_dispatch:
5 | # Manual trigger (from bot)
6 | inputs:
7 | version:
8 | required: true
9 | type: string
10 | description: 'The server and web stable release tag (i.e. "vX.Y.Z")'
11 | preview_id:
12 | required: true
13 | type: string
14 | description: 'The numerical preview ID (e.g. 1 for `rc1`)'
15 |
16 | env:
17 | SDK_VERSION: "9.0.x"
18 |
19 | permissions:
20 | contents: read
21 |
22 | jobs:
23 | Docker:
24 | runs-on: ubuntu-24.04
25 | continue-on-error: false # true in prod, false for testing
26 | steps:
27 | - name: "Set dated version for unstable builds"
28 | id: version
29 | run: |-
30 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
31 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
32 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
33 |
34 | - name: "Install dependencies"
35 | run: |-
36 | sudo apt-get update
37 | sudo apt-get install --yes python3-git python3-yaml
38 |
39 | - name: "Checkout repository"
40 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
41 |
42 | - name: "Prepare repository"
43 | run: |-
44 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
45 |
46 | - name: "Run builder for Docker containers"
47 | env:
48 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
49 | DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
50 | GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
51 | GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
52 | run: |-
53 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} docker ${{ env.DEBUG_FLAG }}
54 |
55 | Debian:
56 | runs-on: ubuntu-24.04
57 | strategy:
58 | fail-fast: false
59 | matrix:
60 | release:
61 | - bullseye
62 | - bookworm
63 | - trixie
64 | arch:
65 | - amd64
66 | - arm64
67 | continue-on-error: false # true in prod, false for testing
68 | steps:
69 | - name: "Set dated version for unstable builds"
70 | id: version
71 | run: |-
72 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
73 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
74 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
75 |
76 | - name: "Install dependencies"
77 | run: |-
78 | sudo apt-get update
79 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
80 |
81 | - name: "Checkout repository"
82 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
83 |
84 | - name: "Prepare repository"
85 | run: |-
86 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
87 |
88 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
89 | run: |-
90 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} debian ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
91 | sudo chown --recursive $USER out/debian
92 |
93 | - name: "Import repository signing GPG key"
94 | run: |
95 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
96 |
97 | - name: "Sign Debian package and source files"
98 | run: |
99 | for file in out/debian/*.deb; do
100 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
101 | done
102 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/debian/*.changes
103 |
104 | - name: "Remove repository signing GPG key"
105 | run: |
106 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
107 |
108 | - name: "Upload artifacts to repository server"
109 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
110 | with:
111 | host: "${{ secrets.REPO_HOST }}"
112 | username: "${{ secrets.REPO_USER }}"
113 | key: "${{ secrets.REPO_KEY }}"
114 | source: "out/debian/*"
115 | strip_components: 2
116 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}"
117 |
118 | - name: "Move artifacts into repository"
119 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
120 | with:
121 | host: "${{ secrets.REPO_HOST }}"
122 | username: "${{ secrets.REPO_USER }}"
123 | key: "${{ secrets.REPO_KEY }}"
124 | debug: false
125 | script: |
126 | export BASEDIR="/srv/repository/main/server/debian"
127 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
128 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
129 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
130 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
131 |
132 | Ubuntu:
133 | runs-on: ubuntu-24.04
134 | strategy:
135 | fail-fast: false
136 | matrix:
137 | release:
138 | - focal
139 | - jammy
140 | - noble
141 | arch:
142 | - amd64
143 | - arm64
144 | continue-on-error: false # true in prod, false for testing
145 | steps:
146 | - name: "Set dated version for unstable builds"
147 | id: version
148 | run: |-
149 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
150 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
151 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
152 |
153 | - name: "Install dependencies"
154 | run: |-
155 | sudo apt-get update
156 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
157 |
158 | - name: "Checkout repository"
159 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
160 |
161 | - name: "Prepare repository"
162 | run: |-
163 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
164 |
165 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
166 | run: |-
167 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} ubuntu ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
168 | sudo chown --recursive $USER out/ubuntu
169 |
170 | - name: "Import repository signing GPG key"
171 | run: |
172 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
173 |
174 | - name: "Sign Ubuntu package and source files"
175 | run: |
176 | for file in out/ubuntu/*.deb; do
177 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
178 | done
179 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/ubuntu/*.changes
180 |
181 | - name: "Remove repository signing GPG key"
182 | run: |
183 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
184 |
185 | - name: "Upload artifacts to repository server"
186 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
187 | with:
188 | host: "${{ secrets.REPO_HOST }}"
189 | username: "${{ secrets.REPO_USER }}"
190 | key: "${{ secrets.REPO_KEY }}"
191 | source: "out/ubuntu/*"
192 | strip_components: 2
193 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}"
194 |
195 | - name: "Move artifacts into repository"
196 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
197 | with:
198 | host: "${{ secrets.REPO_HOST }}"
199 | username: "${{ secrets.REPO_USER }}"
200 | key: "${{ secrets.REPO_KEY }}"
201 | debug: false
202 | script: |
203 | export BASEDIR="/srv/repository/main/server/ubuntu"
204 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
205 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
206 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
207 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
208 |
209 | Linux:
210 | runs-on: ubuntu-24.04
211 | strategy:
212 | fail-fast: false
213 | matrix:
214 | arch:
215 | - amd64
216 | - amd64-musl
217 | - arm64
218 | - arm64-musl
219 | continue-on-error: false # true in prod, false for testing
220 | steps:
221 | - name: "Set dated version for unstable builds"
222 | id: version
223 | run: |-
224 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
225 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
226 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
227 |
228 | - name: "Install dependencies"
229 | run: |-
230 | sudo apt-get update
231 | sudo apt-get install --yes python3-git python3-yaml
232 |
233 | - name: "Checkout repository"
234 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
235 |
236 | - name: "Prepare repository"
237 | run: |-
238 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
239 |
240 | - name: "Run builder for ${{ matrix.arch }}"
241 | run: |-
242 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} linux ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
243 |
244 | - name: "Upload artifacts to repository server"
245 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
246 | with:
247 | host: "${{ secrets.REPO_HOST }}"
248 | username: "${{ secrets.REPO_USER }}"
249 | key: "${{ secrets.REPO_KEY }}"
250 | source: "out/linux/*"
251 | strip_components: 2
252 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.arch }}"
253 |
254 | - name: "Move artifacts into repository"
255 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
256 | with:
257 | host: "${{ secrets.REPO_HOST }}"
258 | username: "${{ secrets.REPO_USER }}"
259 | key: "${{ secrets.REPO_KEY }}"
260 | debug: false
261 | script: |
262 | set -e
263 | export BASEDIR="/srv/repository/main/server/linux"
264 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
265 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
266 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
267 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
268 |
269 | Windows:
270 | runs-on: ubuntu-24.04
271 | strategy:
272 | fail-fast: false
273 | matrix:
274 | arch:
275 | - amd64
276 | - arm64
277 | outputs:
278 | JELLYFIN_VERSION: ${{ steps.version.outputs.JELLYFIN_VERSION }}
279 | JELLYFIN_RELEASE_TYPE: ${{ steps.version.outputs.JELLYFIN_RELEASE_TYPE }}
280 | continue-on-error: false # true in prod, false for testing
281 | steps:
282 | - name: "Set dated version for unstable builds"
283 | id: version
284 | run: |-
285 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
286 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_OUTPUT
287 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
288 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_OUTPUT
289 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
290 | echo "DEBUG_FLAG=--debug" >>$GITHUB_OUTPUT
291 |
292 | - name: "Install dependencies"
293 | run: |-
294 | sudo apt-get update
295 | sudo apt-get install --yes python3-git python3-yaml
296 |
297 | - name: "Checkout repository"
298 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
299 |
300 | - name: "Prepare repository"
301 | run: |-
302 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
303 |
304 | - name: "Run builder for ${{ matrix.arch }}"
305 | run: |-
306 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} windows ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
307 |
308 | - name: "Upload artifacts to repository server"
309 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
310 | with:
311 | host: "${{ secrets.REPO_HOST }}"
312 | username: "${{ secrets.REPO_USER }}"
313 | key: "${{ secrets.REPO_KEY }}"
314 | source: "out/windows/*"
315 | strip_components: 2
316 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
317 |
318 | - name: "Move artifacts into repository"
319 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
320 | with:
321 | host: "${{ secrets.REPO_HOST }}"
322 | username: "${{ secrets.REPO_USER }}"
323 | key: "${{ secrets.REPO_KEY }}"
324 | debug: false
325 | script: |
326 | set -e
327 | export BASEDIR="/srv/repository/main/server/windows"
328 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
329 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
330 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
331 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
332 |
333 | - name: "Store artifacts for next stage"
334 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
335 | with:
336 | name: windows-artifacts
337 | retention-days: 1
338 | if-no-files-found: ignore # Ignore for arm64 build, as no file will be found
339 | path: out/windows/jellyfin_*-amd64.zip
340 |
341 | WindowsInstaller:
342 | needs:
343 | - Windows
344 | runs-on: windows-latest
345 | strategy:
346 | fail-fast: false
347 | matrix:
348 | arch:
349 | - amd64
350 | continue-on-error: false # true in prod, false for testing
351 | env:
352 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
353 | steps:
354 | - name: "Set dated version for unstable builds"
355 | id: version
356 | shell: pwsh
357 | run: |
358 | $version = "${{ needs.Windows.outputs.JELLYFIN_VERSION }}"
359 | $cleanVersion = $version.Substring(1)
360 | echo "JELLYFIN_VERSION=$cleanVersion" | Out-File -FilePath $env:GITHUB_ENV -Append
361 |
362 | - name: "Checkout repository"
363 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
364 |
365 | - name: "Install dependencies"
366 | run: |-
367 | python -m pip install --upgrade gitpython
368 |
369 | - name: "Install NSIS"
370 | uses: negrutiu/nsis-install@f3339c88dba6fd08910d5275a943f8f746d94876 # v2
371 | with:
372 | distro: official
373 |
374 | - name: "Prepare repository"
375 | run: |-
376 | python ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
377 |
378 | - name: "Fetch artifacts from previous stage"
379 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
380 | with:
381 | name: windows-artifacts
382 | path: ./jellyfin-server-windows
383 |
384 | - name: "Clone UX repository"
385 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
386 | with:
387 | repository: jellyfin/jellyfin-ux
388 | path: .\jellyfin-server-windows\jellyfin-ux
389 |
390 | - name: "Extract Jellyfin server archive"
391 | working-directory: ./jellyfin-server-windows
392 | run: |
393 | New-Item -ItemType Directory -Path jellyfin
394 | Expand-Archive -Path 'jellyfin_*-amd64.zip' -DestinationPath jellyfin -Force
395 | Copy-Item ".\Support Files\LICENSE" -Destination $(Resolve-Path .\jellyfin\jellyfin)
396 |
397 | - name: "Add NSSM"
398 | working-directory: ./jellyfin-server-windows
399 | run: |
400 | Invoke-WebRequest 'https://repo.jellyfin.org/files/other/nssm.zip' -OutFile 'nssm.zip'
401 | Expand-Archive 'nssm.zip'
402 | Copy-Item ".\nssm\nssm.exe" -Destination $(Resolve-Path .\jellyfin\jellyfin)
403 |
404 | - name: "Publish tray"
405 | working-directory: ./jellyfin-server-windows
406 | run: |
407 | New-Item -Path .\jellyfin\jellyfin\jellyfin-windows-tray -ItemType Directory
408 | dotnet publish -c Release -r win-x64 -f net472 --no-self-contained --output $(Resolve-Path .\jellyfin\jellyfin\jellyfin-windows-tray)
409 |
410 | - name: "Build installer"
411 | working-directory: ./jellyfin-server-windows
412 | run: |
413 | $env:InstallLocation = $(Resolve-Path .\jellyfin\jellyfin)
414 | makensis /Dx64 /DUXPATH=$(Resolve-Path .\jellyfin-ux) $(Join-Path -Path $(Resolve-Path .\nsis) -ChildPath jellyfin.nsi)
415 |
416 | - name: "Rename installer"
417 | working-directory: ./jellyfin-server-windows/nsis
418 | run: |
419 | Rename-Item -Path .\jellyfin_*_windows-x64.exe -NewName ("jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe")
420 |
421 | - name: "Store artifacts for next stage"
422 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
423 | with:
424 | name: windows-installer-artifacts
425 | retention-days: 1
426 | if-no-files-found: error
427 | path: ./jellyfin-server-windows/nsis/jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe
428 |
429 | WindowsInstallerUpload:
430 | needs:
431 | - Windows
432 | - WindowsInstaller
433 | runs-on: ubuntu-24.04
434 | strategy:
435 | fail-fast: false
436 | matrix:
437 | arch:
438 | - amd64
439 | continue-on-error: false # true in prod, false for testing
440 | steps:
441 | - name: "Set version from previous job"
442 | id: version
443 | run: |-
444 | echo "JELLYFIN_VERSION=${{ needs.Windows.outputs.JELLYFIN_VERSION }}" >> $GITHUB_ENV
445 | echo "JELLYFIN_RELEASE_TYPE=${{ needs.Windows.outputs.JELLYFIN_RELEASE_TYPE }}" >> $GITHUB_ENV
446 |
447 | - name: "Fetch artifact from previous stage"
448 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
449 | with:
450 | name: windows-installer-artifacts
451 |
452 | - name: "Upload artifacts to repository server"
453 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
454 | with:
455 | host: "${{ secrets.REPO_HOST }}"
456 | username: "${{ secrets.REPO_USER }}"
457 | key: "${{ secrets.REPO_KEY }}"
458 | source: "*.exe"
459 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
460 |
461 | - name: "Move artifacts into repository"
462 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
463 | with:
464 | host: "${{ secrets.REPO_HOST }}"
465 | username: "${{ secrets.REPO_USER }}"
466 | key: "${{ secrets.REPO_KEY }}"
467 | debug: false
468 | script: |
469 | set -e
470 | export BASEDIR="/srv/repository/main/server/windows"
471 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}/*.exe
472 |
473 | MacOS:
474 | runs-on: ubuntu-24.04
475 | strategy:
476 | fail-fast: false
477 | matrix:
478 | arch:
479 | - amd64
480 | - arm64
481 | continue-on-error: false # true in prod, false for testing
482 | steps:
483 | - name: "Set dated version for unstable builds"
484 | id: version
485 | run: |-
486 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
487 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
488 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
489 |
490 | - name: "Install dependencies"
491 | run: |-
492 | sudo apt-get update
493 | sudo apt-get install --yes python3-git python3-yaml
494 |
495 | - name: "Checkout repository"
496 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
497 |
498 | - name: "Prepare repository"
499 | run: |-
500 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
501 |
502 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
503 | run: |-
504 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} macos ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
505 |
506 | - name: "Upload artifacts to repository server"
507 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
508 | with:
509 | host: "${{ secrets.REPO_HOST }}"
510 | username: "${{ secrets.REPO_USER }}"
511 | key: "${{ secrets.REPO_KEY }}"
512 | source: "out/macos/*"
513 | strip_components: 2
514 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.arch }}"
515 |
516 | - name: "Move artifacts into repository"
517 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
518 | with:
519 | host: "${{ secrets.REPO_HOST }}"
520 | username: "${{ secrets.REPO_USER }}"
521 | key: "${{ secrets.REPO_KEY }}"
522 | debug: false
523 | script: |
524 | set -e
525 | export BASEDIR="/srv/repository/main/server/macos"
526 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
527 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
528 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
529 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
530 |
531 | Portable:
532 | runs-on: ubuntu-24.04
533 | continue-on-error: false # true in prod, false for testing
534 | steps:
535 | - name: "Set dated version for unstable builds"
536 | id: version
537 | run: |-
538 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
539 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
540 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
541 |
542 | - name: "Install dependencies"
543 | run: |-
544 | sudo apt-get update
545 | sudo apt-get install --yes python3-git python3-yaml
546 |
547 | - name: "Checkout repository"
548 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
549 |
550 | - name: "Prepare repository"
551 | run: |-
552 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
553 |
554 | - name: "Run builder for Portable"
555 | run: |-
556 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} portable ${{ env.DEBUG_FLAG }}
557 |
558 | - name: "Upload artifacts to repository server"
559 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
560 | with:
561 | host: "${{ secrets.REPO_HOST }}"
562 | username: "${{ secrets.REPO_USER }}"
563 | key: "${{ secrets.REPO_KEY }}"
564 | source: "out/portable/*"
565 | strip_components: 2
566 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable"
567 |
568 | - name: "Move artifacts into repository"
569 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
570 | with:
571 | host: "${{ secrets.REPO_HOST }}"
572 | username: "${{ secrets.REPO_USER }}"
573 | key: "${{ secrets.REPO_KEY }}"
574 | debug: false
575 | script: |
576 | set -e
577 | export BASEDIR="/srv/repository/main/server/portable"
578 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any || exit 1
579 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable/${{ matrix.release }}/* || exit 1
580 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
581 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
582 |
583 | Nuget:
584 | runs-on: ubuntu-24.04
585 | steps:
586 | - name: "Set dated version for unstable builds"
587 | id: version
588 | run: |-
589 | echo "JELLYFIN_VERSION=${{ inputs.version }}-rc${{ inputs.preview_id }}" >> $GITHUB_ENV
590 | echo "JELLYFIN_RELEASE_TYPE=preview" >> $GITHUB_ENV
591 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
592 |
593 | - name: "Install dependencies"
594 | run: |-
595 | sudo apt-get update
596 | sudo apt-get install --yes python3-git python3-yaml
597 |
598 | - name: "Setup .NET"
599 | uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
600 | with:
601 | dotnet-version: ${{ env.SDK_VERSION }}
602 |
603 | - name: "Checkout repository"
604 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
605 |
606 | - name: "Prepare repository"
607 | run: |-
608 | ./checkout.py ${{ inputs.version }}-rc${{ inputs.preview_id }}
609 |
610 | - name: "Run builder for Nuget"
611 | env:
612 | NUGET_STABLE_KEY: ${{ secrets.NUGET_STABLE_KEY }}
613 | NUGET_UNSTABLE_KEY: ${{ secrets.NUGET_UNSTABLE_KEY }}
614 | run: |-
615 | ./build.py ${{ env.JELLYFIN_VERSION }} nuget
616 |
--------------------------------------------------------------------------------
/.github/workflows/release-build.yaml:
--------------------------------------------------------------------------------
1 | name: "Release Build"
2 |
3 | on:
4 | workflow_dispatch:
5 | # Manual trigger (from bot)
6 | inputs:
7 | version:
8 | required: true
9 | type: string
10 | description: 'The server and web stable release tag (i.e. "vX.Y.Z")'
11 |
12 | env:
13 | SDK_VERSION: "9.0.x"
14 |
15 | permissions:
16 | contents: read
17 |
18 | jobs:
19 | Docker:
20 | runs-on: ubuntu-24.04
21 | continue-on-error: false # true in prod, false for testing
22 | steps:
23 | - name: "Set dated version for unstable builds"
24 | id: version
25 | run: |-
26 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
27 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
28 |
29 | - name: "Install dependencies"
30 | run: |-
31 | sudo apt-get update
32 | sudo apt-get install --yes python3-git python3-yaml
33 |
34 | - name: "Checkout repository"
35 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
36 |
37 | - name: "Prepare repository"
38 | run: |-
39 | ./checkout.py ${{ inputs.version || 'master' }}
40 |
41 | - name: "Run builder for Docker containers"
42 | env:
43 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
44 | DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
45 | GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
46 | GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
47 | run: |-
48 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} docker ${{ env.DEBUG_FLAG }}
49 |
50 | Debian:
51 | runs-on: ubuntu-24.04
52 | strategy:
53 | fail-fast: false
54 | matrix:
55 | release:
56 | - bullseye
57 | - bookworm
58 | - trixie
59 | arch:
60 | - amd64
61 | - arm64
62 | continue-on-error: false # true in prod, false for testing
63 | steps:
64 | - name: "Set dated version for unstable builds"
65 | id: version
66 | run: |-
67 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
68 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
69 |
70 | - name: "Install dependencies"
71 | run: |-
72 | sudo apt-get update
73 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
74 |
75 | - name: "Checkout repository"
76 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
77 |
78 | - name: "Prepare repository"
79 | run: |-
80 | ./checkout.py ${{ inputs.version || 'master' }}
81 |
82 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
83 | run: |-
84 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} debian ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
85 | sudo chown --recursive $USER out/debian
86 |
87 | - name: "Import repository signing GPG key"
88 | run: |
89 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
90 |
91 | - name: "Sign Debian package and source files"
92 | run: |
93 | for file in out/debian/*.deb; do
94 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
95 | done
96 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/debian/*.changes
97 |
98 | - name: "Remove repository signing GPG key"
99 | run: |
100 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
101 |
102 | - name: "Upload artifacts to repository server"
103 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
104 | with:
105 | host: "${{ secrets.REPO_HOST }}"
106 | username: "${{ secrets.REPO_USER }}"
107 | key: "${{ secrets.REPO_KEY }}"
108 | source: "out/debian/*"
109 | strip_components: 2
110 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}"
111 |
112 | - name: "Import artifacts into reprepro"
113 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
114 | with:
115 | host: "${{ secrets.REPO_HOST }}"
116 | username: "${{ secrets.REPO_USER }}"
117 | key: "${{ secrets.REPO_KEY }}"
118 | debug: false
119 | script: |
120 | set -o xtrace
121 | COMPONENT="main"
122 | # Only include the architecture-dependent deb here, as the others are done only for amd64
123 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*_${{ matrix.arch }}.deb || exit 1
124 | if [[ ${{ matrix.arch }} == "amd64" ]]; then
125 | # Only include the architecture-independent packages for amd64; the other architectures are the same and conflict
126 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*_all.deb || exit 1
127 | # Only include the source DSC for amd64; the other architectures are the same and conflict
128 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedsc ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*.dsc || exit 1
129 | fi
130 |
131 | # Only keep the latest point release for a given stable version; i.e. 10.10.14 obsoletes 10.10.13 and removes it, while leaving 10.9.10
132 | NEW_VERSION=${{ matrix.release }}
133 | NEW_MAJOR_VERSION="${NEW_VERSION#v}"
134 | NEW_MAJOR_VERSION="${NEW_MAJOR_VERSION%.*}"
135 | NEW_POINT_VERSION="${NEW_VERSION##*.}"
136 | if [[ ${NEW_POINT_VERSION} -gt 0 ]]; then
137 | LAST_POINT_VERSION=$(( NEW_POINT_VERSION - 1 ))
138 | LAST_VERSION="${NEW_MAJOR_VERSION}.${LAST_POINT_VERSION}"
139 | for PACKAGE in jellyfin jellyfin-server jellyfin-web; do
140 | for SCRAP_VERSION in $( sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} list ${{ matrix.release }} | grep "${PACKAGE} " | grep "${LAST_VERSION}" | sort | uniq ); do
141 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} remove ${{ matrix.release }} ${PACKAGE}=${SCRAP_VERSION}
142 | done
143 | done
144 | fi
145 |
146 | - name: "Move artifacts into repository"
147 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
148 | with:
149 | host: "${{ secrets.REPO_HOST }}"
150 | username: "${{ secrets.REPO_USER }}"
151 | key: "${{ secrets.REPO_KEY }}"
152 | debug: false
153 | script: |
154 | export BASEDIR="/srv/repository/main/server/debian"
155 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
156 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
157 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
158 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
159 | sudo rm ${BASEDIR}/latest || true
160 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
161 |
162 | Ubuntu:
163 | runs-on: ubuntu-24.04
164 | strategy:
165 | fail-fast: false
166 | matrix:
167 | release:
168 | - focal
169 | - jammy
170 | - noble
171 | arch:
172 | - amd64
173 | - arm64
174 | continue-on-error: false # true in prod, false for testing
175 | steps:
176 | - name: "Set dated version for unstable builds"
177 | id: version
178 | run: |-
179 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
180 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
181 |
182 | - name: "Install dependencies"
183 | run: |-
184 | sudo apt-get update
185 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
186 |
187 | - name: "Checkout repository"
188 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
189 |
190 | - name: "Prepare repository"
191 | run: |-
192 | ./checkout.py ${{ inputs.version || 'master' }}
193 |
194 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
195 | run: |-
196 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} ubuntu ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
197 | sudo chown --recursive $USER out/ubuntu
198 |
199 | - name: "Import repository signing GPG key"
200 | run: |
201 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
202 |
203 | - name: "Sign Ubuntu package and source files"
204 | run: |
205 | for file in out/ubuntu/*.deb; do
206 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
207 | done
208 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/ubuntu/*.changes
209 |
210 | - name: "Remove repository signing GPG key"
211 | run: |
212 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
213 |
214 | - name: "Upload artifacts to repository server"
215 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
216 | with:
217 | host: "${{ secrets.REPO_HOST }}"
218 | username: "${{ secrets.REPO_USER }}"
219 | key: "${{ secrets.REPO_KEY }}"
220 | source: "out/ubuntu/*"
221 | strip_components: 2
222 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}"
223 |
224 | - name: "Import artifacts into reprepro"
225 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
226 | with:
227 | host: "${{ secrets.REPO_HOST }}"
228 | username: "${{ secrets.REPO_USER }}"
229 | key: "${{ secrets.REPO_KEY }}"
230 | debug: false
231 | script: |
232 | set -o xtrace
233 | COMPONENT="main"
234 | # Only include the architecture-dependent deb here, as the others are done only for amd64
235 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*_${{ matrix.arch }}.deb || exit 1
236 | if [[ ${{ matrix.arch }} == "amd64" ]]; then
237 | # Only include the architecture-independent packages for amd64; the other architectures are the same and conflict
238 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*_all.deb || exit 1
239 | # Only include the source DSC for amd64; the other architectures are the same and conflict
240 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedsc ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*.dsc || exit 1
241 | fi
242 |
243 | # Only keep the latest point release for a given stable version; i.e. 10.10.14 obsoletes 10.10.13 and removes it, while leaving 10.9.10
244 | NEW_VERSION=${{ matrix.release }}
245 | NEW_MAJOR_VERSION="${NEW_VERSION#v}"
246 | NEW_MAJOR_VERSION="${NEW_MAJOR_VERSION%.*}"
247 | NEW_POINT_VERSION="${NEW_VERSION##*.}"
248 | if [[ ${NEW_POINT_VERSION} -gt 0 ]]; then
249 | LAST_POINT_VERSION=$(( NEW_POINT_VERSION - 1 ))
250 | LAST_VERSION="${NEW_MAJOR_VERSION}.${LAST_POINT_VERSION}"
251 | for PACKAGE in jellyfin jellyfin-server jellyfin-web; do
252 | for SCRAP_VERSION in $( sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} list ${{ matrix.release }} | grep "${PACKAGE} " | grep "${LAST_VERSION}" | sort | uniq ); do
253 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} remove ${{ matrix.release }} ${PACKAGE}=${SCRAP_VERSION}
254 | done
255 | done
256 | fi
257 |
258 | - name: "Move artifacts into repository"
259 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
260 | with:
261 | host: "${{ secrets.REPO_HOST }}"
262 | username: "${{ secrets.REPO_USER }}"
263 | key: "${{ secrets.REPO_KEY }}"
264 | debug: false
265 | script: |
266 | export BASEDIR="/srv/repository/main/server/ubuntu"
267 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
268 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
269 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
270 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
271 | sudo rm ${BASEDIR}/latest || true
272 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
273 |
274 | Linux:
275 | runs-on: ubuntu-24.04
276 | strategy:
277 | fail-fast: false
278 | matrix:
279 | arch:
280 | - amd64
281 | - amd64-musl
282 | - arm64
283 | - arm64-musl
284 | continue-on-error: false # true in prod, false for testing
285 | steps:
286 | - name: "Set dated version for unstable builds"
287 | id: version
288 | run: |-
289 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
290 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
291 |
292 | - name: "Install dependencies"
293 | run: |-
294 | sudo apt-get update
295 | sudo apt-get install --yes python3-git python3-yaml
296 |
297 | - name: "Checkout repository"
298 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
299 |
300 | - name: "Prepare repository"
301 | run: |-
302 | ./checkout.py ${{ inputs.version || 'master' }}
303 |
304 | - name: "Run builder for ${{ matrix.arch }}"
305 | run: |-
306 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} linux ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
307 |
308 | - name: "Upload artifacts to repository server"
309 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
310 | with:
311 | host: "${{ secrets.REPO_HOST }}"
312 | username: "${{ secrets.REPO_USER }}"
313 | key: "${{ secrets.REPO_KEY }}"
314 | source: "out/linux/*"
315 | strip_components: 2
316 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.arch }}"
317 |
318 | - name: "Move artifacts into repository"
319 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
320 | with:
321 | host: "${{ secrets.REPO_HOST }}"
322 | username: "${{ secrets.REPO_USER }}"
323 | key: "${{ secrets.REPO_KEY }}"
324 | debug: false
325 | script: |
326 | set -e
327 | export BASEDIR="/srv/repository/main/server/linux"
328 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
329 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
330 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
331 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
332 | sudo rm ${BASEDIR}/latest || true
333 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
334 |
335 | Windows:
336 | runs-on: ubuntu-24.04
337 | strategy:
338 | fail-fast: false
339 | matrix:
340 | arch:
341 | - amd64
342 | - arm64
343 | outputs:
344 | JELLYFIN_VERSION: ${{ steps.version.outputs.JELLYFIN_VERSION }}
345 | JELLYFIN_RELEASE_TYPE: ${{ steps.version.outputs.JELLYFIN_RELEASE_TYPE }}
346 | continue-on-error: false # true in prod, false for testing
347 | steps:
348 | - name: "Set dated version for unstable builds"
349 | id: version
350 | run: |-
351 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
352 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_OUTPUT
353 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
354 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_OUTPUT
355 |
356 | - name: "Install dependencies"
357 | run: |-
358 | sudo apt-get update
359 | sudo apt-get install --yes python3-git python3-yaml
360 |
361 | - name: "Checkout repository"
362 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
363 |
364 | - name: "Prepare repository"
365 | run: |-
366 | ./checkout.py ${{ inputs.version || 'master' }}
367 |
368 | - name: "Run builder for ${{ matrix.arch }}"
369 | run: |-
370 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} windows ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
371 |
372 | - name: "Upload artifacts to repository server"
373 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
374 | with:
375 | host: "${{ secrets.REPO_HOST }}"
376 | username: "${{ secrets.REPO_USER }}"
377 | key: "${{ secrets.REPO_KEY }}"
378 | source: "out/windows/*"
379 | strip_components: 2
380 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
381 |
382 | - name: "Move artifacts into repository"
383 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
384 | with:
385 | host: "${{ secrets.REPO_HOST }}"
386 | username: "${{ secrets.REPO_USER }}"
387 | key: "${{ secrets.REPO_KEY }}"
388 | debug: false
389 | script: |
390 | set -e
391 | export BASEDIR="/srv/repository/main/server/windows"
392 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
393 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
394 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
395 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
396 | sudo rm ${BASEDIR}/latest || true
397 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
398 |
399 | - name: "Store artifacts for next stage"
400 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
401 | with:
402 | name: windows-artifacts
403 | retention-days: 1
404 | if-no-files-found: ignore # Ignore for arm64 build, as no file will be found
405 | path: out/windows/jellyfin_*-amd64.zip
406 |
407 | WindowsInstaller:
408 | needs:
409 | - Windows
410 | runs-on: windows-latest
411 | strategy:
412 | fail-fast: false
413 | matrix:
414 | arch:
415 | - amd64
416 | continue-on-error: false # true in prod, false for testing
417 | env:
418 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
419 | steps:
420 | - name: "Set dated version for unstable builds"
421 | id: version
422 | shell: pwsh
423 | run: |
424 | $version = "${{ needs.Windows.outputs.JELLYFIN_VERSION }}"
425 | $cleanVersion = $version.Substring(1)
426 | echo "JELLYFIN_VERSION=$cleanVersion" | Out-File -FilePath $env:GITHUB_ENV -Append
427 |
428 | - name: "Checkout repository"
429 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
430 |
431 | - name: "Install dependencies"
432 | run: |-
433 | python -m pip install --upgrade gitpython
434 |
435 | - name: "Install NSIS"
436 | uses: negrutiu/nsis-install@f3339c88dba6fd08910d5275a943f8f746d94876 # v2
437 | with:
438 | distro: official
439 |
440 | - name: "Prepare repository"
441 | run: |-
442 | python checkout.py ${{ inputs.version || 'master' }}
443 |
444 | - name: "Fetch artifacts from previous stage"
445 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
446 | with:
447 | name: windows-artifacts
448 | path: ./jellyfin-server-windows
449 |
450 | - name: "Clone UX repository"
451 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
452 | with:
453 | repository: jellyfin/jellyfin-ux
454 | path: .\jellyfin-server-windows\jellyfin-ux
455 |
456 | - name: "Extract Jellyfin server archive"
457 | working-directory: ./jellyfin-server-windows
458 | run: |
459 | New-Item -ItemType Directory -Path jellyfin
460 | Expand-Archive -Path 'jellyfin_*-amd64.zip' -DestinationPath jellyfin -Force
461 | Copy-Item ".\Support Files\LICENSE" -Destination $(Resolve-Path .\jellyfin\jellyfin)
462 |
463 | - name: "Add NSSM"
464 | working-directory: ./jellyfin-server-windows
465 | run: |
466 | Invoke-WebRequest 'https://repo.jellyfin.org/files/other/nssm.zip' -OutFile 'nssm.zip'
467 | Expand-Archive 'nssm.zip'
468 | Copy-Item ".\nssm\nssm.exe" -Destination $(Resolve-Path .\jellyfin\jellyfin)
469 |
470 | - name: "Publish tray"
471 | working-directory: ./jellyfin-server-windows
472 | run: |
473 | New-Item -Path .\jellyfin\jellyfin\jellyfin-windows-tray -ItemType Directory
474 | dotnet publish -c Release -r win-x64 -f net472 --no-self-contained --output $(Resolve-Path .\jellyfin\jellyfin\jellyfin-windows-tray)
475 |
476 | - name: "Build installer"
477 | working-directory: ./jellyfin-server-windows
478 | run: |
479 | $env:InstallLocation = $(Resolve-Path .\jellyfin\jellyfin)
480 | makensis /Dx64 /DUXPATH=$(Resolve-Path .\jellyfin-ux) $(Join-Path -Path $(Resolve-Path .\nsis) -ChildPath jellyfin.nsi)
481 |
482 | - name: "Rename installer"
483 | working-directory: ./jellyfin-server-windows/nsis
484 | run: |
485 | Rename-Item -Path .\jellyfin_*_windows-x64.exe -NewName ("jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe")
486 |
487 | - name: "Store artifacts for next stage"
488 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
489 | with:
490 | name: windows-installer-artifacts
491 | retention-days: 1
492 | if-no-files-found: error
493 | path: ./jellyfin-server-windows/nsis/jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe
494 |
495 | WindowsInstallerUpload:
496 | needs:
497 | - Windows
498 | - WindowsInstaller
499 | runs-on: ubuntu-24.04
500 | strategy:
501 | fail-fast: false
502 | matrix:
503 | arch:
504 | - amd64
505 | continue-on-error: false # true in prod, false for testing
506 | steps:
507 | - name: "Set version from previous job"
508 | id: version
509 | run: |-
510 | echo "JELLYFIN_VERSION=${{ needs.Windows.outputs.JELLYFIN_VERSION }}" >> $GITHUB_ENV
511 | echo "JELLYFIN_RELEASE_TYPE=${{ needs.Windows.outputs.JELLYFIN_RELEASE_TYPE }}" >> $GITHUB_ENV
512 |
513 | - name: "Fetch artifact from previous stage"
514 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
515 | with:
516 | name: windows-installer-artifacts
517 |
518 | - name: "Upload artifacts to repository server"
519 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
520 | with:
521 | host: "${{ secrets.REPO_HOST }}"
522 | username: "${{ secrets.REPO_USER }}"
523 | key: "${{ secrets.REPO_KEY }}"
524 | source: "*.exe"
525 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
526 |
527 | - name: "Move artifacts into repository"
528 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
529 | with:
530 | host: "${{ secrets.REPO_HOST }}"
531 | username: "${{ secrets.REPO_USER }}"
532 | key: "${{ secrets.REPO_KEY }}"
533 | debug: false
534 | script: |
535 | set -e
536 | export BASEDIR="/srv/repository/main/server/windows"
537 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}/*.exe
538 |
539 | MacOS:
540 | runs-on: ubuntu-24.04
541 | strategy:
542 | fail-fast: false
543 | matrix:
544 | arch:
545 | - amd64
546 | - arm64
547 | continue-on-error: false # true in prod, false for testing
548 | steps:
549 | - name: "Set dated version for unstable builds"
550 | id: version
551 | run: |-
552 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
553 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
554 |
555 | - name: "Install dependencies"
556 | run: |-
557 | sudo apt-get update
558 | sudo apt-get install --yes python3-git python3-yaml
559 |
560 | - name: "Checkout repository"
561 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
562 |
563 | - name: "Prepare repository"
564 | run: |-
565 | ./checkout.py ${{ inputs.version || 'master' }}
566 |
567 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
568 | run: |-
569 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} macos ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
570 |
571 | - name: "Upload artifacts to repository server"
572 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
573 | with:
574 | host: "${{ secrets.REPO_HOST }}"
575 | username: "${{ secrets.REPO_USER }}"
576 | key: "${{ secrets.REPO_KEY }}"
577 | source: "out/macos/*"
578 | strip_components: 2
579 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.arch }}"
580 |
581 | - name: "Move artifacts into repository"
582 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
583 | with:
584 | host: "${{ secrets.REPO_HOST }}"
585 | username: "${{ secrets.REPO_USER }}"
586 | key: "${{ secrets.REPO_KEY }}"
587 | debug: false
588 | script: |
589 | set -e
590 | export BASEDIR="/srv/repository/main/server/macos"
591 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
592 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
593 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
594 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
595 | sudo rm ${BASEDIR}/latest || true
596 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
597 |
598 | Portable:
599 | runs-on: ubuntu-24.04
600 | continue-on-error: false # true in prod, false for testing
601 | steps:
602 | - name: "Set dated version for unstable builds"
603 | id: version
604 | run: |-
605 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
606 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
607 |
608 | - name: "Install dependencies"
609 | run: |-
610 | sudo apt-get update
611 | sudo apt-get install --yes python3-git python3-yaml
612 |
613 | - name: "Checkout repository"
614 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
615 |
616 | - name: "Prepare repository"
617 | run: |-
618 | ./checkout.py ${{ inputs.version || 'master' }}
619 |
620 | - name: "Run builder for Portable"
621 | run: |-
622 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} portable ${{ env.DEBUG_FLAG }}
623 |
624 | - name: "Upload artifacts to repository server"
625 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
626 | with:
627 | host: "${{ secrets.REPO_HOST }}"
628 | username: "${{ secrets.REPO_USER }}"
629 | key: "${{ secrets.REPO_KEY }}"
630 | source: "out/portable/*"
631 | strip_components: 2
632 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable"
633 |
634 | - name: "Move artifacts into repository"
635 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
636 | with:
637 | host: "${{ secrets.REPO_HOST }}"
638 | username: "${{ secrets.REPO_USER }}"
639 | key: "${{ secrets.REPO_KEY }}"
640 | debug: false
641 | script: |
642 | set -e
643 | export BASEDIR="/srv/repository/main/server/portable"
644 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any || exit 1
645 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable/${{ matrix.release }}/* || exit 1
646 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
647 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
648 | sudo rm ${BASEDIR}/latest || true
649 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
650 |
651 | Nuget:
652 | runs-on: ubuntu-24.04
653 | steps:
654 | - name: "Set dated version for unstable builds"
655 | id: version
656 | run: |-
657 | echo "JELLYFIN_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
658 | echo "JELLYFIN_RELEASE_TYPE=stable" >> $GITHUB_ENV
659 |
660 | - name: "Install dependencies"
661 | run: |-
662 | sudo apt-get update
663 | sudo apt-get install --yes python3-git python3-yaml
664 |
665 | - name: "Setup .NET"
666 | uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
667 | with:
668 | dotnet-version: ${{ env.SDK_VERSION }}
669 |
670 | - name: "Checkout repository"
671 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
672 |
673 | - name: "Prepare repository"
674 | run: |-
675 | ./checkout.py ${{ inputs.version || 'master' }}
676 |
677 | - name: "Run builder for Nuget"
678 | env:
679 | NUGET_STABLE_KEY: ${{ secrets.NUGET_STABLE_KEY }}
680 | NUGET_UNSTABLE_KEY: ${{ secrets.NUGET_UNSTABLE_KEY }}
681 | run: |-
682 | ./build.py ${{ env.JELLYFIN_VERSION }} nuget
683 |
--------------------------------------------------------------------------------
/.github/workflows/unstable-build.yaml:
--------------------------------------------------------------------------------
1 | name: "Unstable Build"
2 |
3 | on:
4 | schedule:
5 | # Weekly unstable trigger on Monday at 05:00 GMT
6 | - cron: '0 5 * * 1'
7 | workflow_dispatch:
8 | # Manual trigger
9 |
10 | env:
11 | SDK_VERSION: "9.0.x"
12 |
13 | permissions:
14 | contents: read
15 |
16 | jobs:
17 | Docker:
18 | runs-on: ubuntu-24.04
19 | continue-on-error: false # true in prod, false for testing
20 | steps:
21 | - name: "Set dated version for unstable builds"
22 | id: version
23 | run: |-
24 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
25 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
26 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
27 |
28 | - name: "Install dependencies"
29 | run: |-
30 | sudo apt-get update
31 | sudo apt-get install --yes python3-git python3-yaml
32 |
33 | - name: "Checkout repository"
34 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
35 |
36 | - name: "Prepare repository"
37 | run: |-
38 | ./checkout.py master
39 |
40 | - name: "Run builder for Docker containers"
41 | env:
42 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
43 | DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
44 | GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
45 | GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
46 | run: |-
47 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} docker ${{ env.DEBUG_FLAG }}
48 |
49 | Debian:
50 | runs-on: ubuntu-24.04
51 | strategy:
52 | fail-fast: false
53 | matrix:
54 | release:
55 | - bullseye
56 | - bookworm
57 | - trixie
58 | arch:
59 | - amd64
60 | - arm64
61 | continue-on-error: false # true in prod, false for testing
62 | steps:
63 | - name: "Set dated version for unstable builds"
64 | id: version
65 | run: |-
66 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
67 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
68 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
69 |
70 | - name: "Install dependencies"
71 | run: |-
72 | sudo apt-get update
73 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
74 |
75 | - name: "Checkout repository"
76 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
77 |
78 | - name: "Prepare repository"
79 | run: |-
80 | ./checkout.py master
81 |
82 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
83 | run: |-
84 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} debian ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
85 | sudo chown --recursive $USER out/debian
86 |
87 | - name: "Import repository signing GPG key"
88 | run: |
89 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
90 |
91 | - name: "Sign Debian package and source files"
92 | run: |
93 | for file in out/debian/*.deb; do
94 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
95 | done
96 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/debian/*.changes
97 |
98 | - name: "Remove repository signing GPG key"
99 | run: |
100 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
101 |
102 | - name: "Upload artifacts to repository server"
103 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
104 | with:
105 | host: "${{ secrets.REPO_HOST }}"
106 | username: "${{ secrets.REPO_USER }}"
107 | key: "${{ secrets.REPO_KEY }}"
108 | source: "out/debian/*"
109 | strip_components: 2
110 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}"
111 |
112 | - name: "Import artifacts into reprepro"
113 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
114 | with:
115 | host: "${{ secrets.REPO_HOST }}"
116 | username: "${{ secrets.REPO_USER }}"
117 | key: "${{ secrets.REPO_KEY }}"
118 | debug: false
119 | script: |
120 | set -o xtrace
121 | COMPONENT="unstable"
122 | # Only include the architecture-dependent deb here, as the others are done only for amd64
123 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*_${{ matrix.arch }}.deb || exit 1
124 | if [[ ${{ matrix.arch }} == "amd64" ]]; then
125 | # Only include the architecture-independent packages for amd64; the other architectures are the same and conflict
126 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*_all.deb || exit 1
127 | # Only include the source DSC for amd64; the other architectures are the same and conflict
128 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} includedsc ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/*.dsc || exit 1
129 | fi
130 |
131 | # Only keep ${NKEEP} (3) unstables; we have to do this manually since reprepro doesn't have granularity for this at the component level
132 | NKEEP=3
133 | # Get whatever the oldest version is; since these are dated, a simple numerical sort works
134 | LIST="$( sudo reprepro -b /srv/debian -C unstable list ${{ matrix.release }} | awk '{ print $NF }' | sort -rn | uniq )"
135 | COUNT=$( wc -l <<<"${LIST}" )
136 | if [[ ${COUNT} -gt ${NKEEP} ]]; then
137 | # Get anything that isn't in the latest ${NKEEP} entries
138 | OBSOLETE=( $( sed "1,${NKEEP}d" <<<"${LIST}" ) )
139 | for VERSION in ${OBSOLETE[@]}; do
140 | for PACKAGE in jellyfin jellyfin-server jellyfin-web; do
141 | for SCRAP_VERSION in $( sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} list ${{ matrix.release }} | grep "${PACKAGE} " | grep "${VERSION}" | sort | uniq ); do
142 | sudo reprepro --waitforlock 30 --basedir /srv/debian --component ${COMPONENT} remove ${{ matrix.release }} ${PACKAGE}=${SCRAP_VERSION}
143 | done
144 | done
145 | done
146 | fi
147 |
148 | - name: "Move artifacts into repository"
149 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
150 | with:
151 | host: "${{ secrets.REPO_HOST }}"
152 | username: "${{ secrets.REPO_USER }}"
153 | key: "${{ secrets.REPO_KEY }}"
154 | debug: false
155 | script: |
156 | export BASEDIR="/srv/repository/main/server/debian"
157 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
158 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/debian/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
159 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
160 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
161 |
162 | Ubuntu:
163 | runs-on: ubuntu-24.04
164 | strategy:
165 | fail-fast: false
166 | matrix:
167 | release:
168 | - focal
169 | - jammy
170 | - noble
171 | arch:
172 | - amd64
173 | - arm64
174 | continue-on-error: false # true in prod, false for testing
175 | steps:
176 | - name: "Set dated version for unstable builds"
177 | id: version
178 | run: |-
179 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
180 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
181 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
182 |
183 | - name: "Install dependencies"
184 | run: |-
185 | sudo apt-get update
186 | sudo apt-get install --yes python3-git python3-yaml debsigs devscripts
187 |
188 | - name: "Checkout repository"
189 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
190 |
191 | - name: "Prepare repository"
192 | run: |-
193 | ./checkout.py master
194 |
195 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
196 | run: |-
197 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} ubuntu ${{ matrix.arch }} ${{ matrix.release }} ${{ env.DEBUG_FLAG }}
198 | sudo chown --recursive $USER out/ubuntu
199 |
200 | - name: "Import repository signing GPG key"
201 | run: |
202 | echo -n "${{ secrets.DEBIAN_SIGNING_KEY }}" | base64 --decode | gpg --batch --yes --import
203 |
204 | - name: "Sign Ubuntu package and source files"
205 | run: |
206 | for file in out/ubuntu/*.deb; do
207 | debsigs --sign=origin --default-key=${{ secrets.DEBIAN_SIGNING_KEY_ID }} ${file}
208 | done
209 | debsign -k ${{ secrets.DEBIAN_SIGNING_KEY_ID }} out/ubuntu/*.changes
210 |
211 | - name: "Remove repository signing GPG key"
212 | run: |
213 | gpg --batch --yes --delete-secret-keys ${{ secrets.DEBIAN_SIGNING_KEY_ID }}
214 |
215 | - name: "Upload artifacts to repository server"
216 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
217 | with:
218 | host: "${{ secrets.REPO_HOST }}"
219 | username: "${{ secrets.REPO_USER }}"
220 | key: "${{ secrets.REPO_KEY }}"
221 | source: "out/ubuntu/*"
222 | strip_components: 2
223 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}"
224 |
225 | - name: "Import artifacts into reprepro"
226 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
227 | with:
228 | host: "${{ secrets.REPO_HOST }}"
229 | username: "${{ secrets.REPO_USER }}"
230 | key: "${{ secrets.REPO_KEY }}"
231 | debug: false
232 | script: |
233 | set -o xtrace
234 | COMPONENT="unstable"
235 | # Only include the architecture-dependent deb here, as the others are done only for amd64
236 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*_${{ matrix.arch }}.deb || exit 1
237 | if [[ ${{ matrix.arch }} == "amd64" ]]; then
238 | # Only include the architecture-independent packages for amd64; the other architectures are the same and conflict
239 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedeb ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*_all.deb || exit 1
240 | # Only include the source DSC for amd64; the other architectures are the same and conflict
241 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} includedsc ${{ matrix.release }} /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/*.dsc || exit 1
242 | fi
243 |
244 | # Only keep ${NKEEP} (4) unstables; we have to do this manually since reprepro doesn't have granularity for this at the component level
245 | NKEEP=3
246 | # Get whatever the oldest version is; since these are dated, a simple numerical sort works
247 | LIST="$( sudo reprepro -b /srv/ubuntu -C unstable list ${{ matrix.release }} | awk '{ print $NF }' | sort -rn | uniq )"
248 | COUNT=$( wc -l <<<"${LIST}" )
249 | if [[ ${COUNT} -gt ${NKEEP} ]]; then
250 | # Get anything that isn't in the latest ${NKEEP} entries
251 | OBSOLETE=( $( sed "1,${NKEEP}d" <<<"${LIST}" ) )
252 | for VERSION in ${OBSOLETE[@]}; do
253 | for PACKAGE in jellyfin jellyfin-server jellyfin-web; do
254 | for SCRAP_VERSION in $( sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} list ${{ matrix.release }} | grep "${PACKAGE} " | grep "${VERSION}" | sort | uniq ); do
255 | sudo reprepro --waitforlock 30 --basedir /srv/ubuntu --component ${COMPONENT} remove ${{ matrix.release }} ${PACKAGE}=${SCRAP_VERSION}
256 | done
257 | done
258 | done
259 | fi
260 |
261 | - name: "Move artifacts into repository"
262 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
263 | with:
264 | host: "${{ secrets.REPO_HOST }}"
265 | username: "${{ secrets.REPO_USER }}"
266 | key: "${{ secrets.REPO_KEY }}"
267 | debug: false
268 | script: |
269 | export BASEDIR="/srv/repository/main/server/ubuntu"
270 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
271 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/ubuntu/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
272 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
273 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
274 | if [[ ${{ env.JELLYFIN_RELEASE_TYPE }} == "stable" ]]; then
275 | sudo rm ${BASEDIR}/latest || true
276 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest || exit 1
277 | fi
278 |
279 | Linux:
280 | runs-on: ubuntu-24.04
281 | strategy:
282 | fail-fast: false
283 | matrix:
284 | arch:
285 | - amd64
286 | - amd64-musl
287 | - arm64
288 | - arm64-musl
289 | continue-on-error: false # true in prod, false for testing
290 | steps:
291 | - name: "Set dated version for unstable builds"
292 | id: version
293 | run: |-
294 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
295 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
296 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
297 |
298 | - name: "Install dependencies"
299 | run: |-
300 | sudo apt-get update
301 | sudo apt-get install --yes python3-git python3-yaml
302 |
303 | - name: "Checkout repository"
304 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
305 |
306 | - name: "Prepare repository"
307 | run: |-
308 | ./checkout.py ${{ inputs.version || 'master' }}
309 |
310 | - name: "Run builder for ${{ matrix.arch }}"
311 | run: |-
312 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} linux ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
313 |
314 | - name: "Upload artifacts to repository server"
315 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
316 | with:
317 | host: "${{ secrets.REPO_HOST }}"
318 | username: "${{ secrets.REPO_USER }}"
319 | key: "${{ secrets.REPO_KEY }}"
320 | source: "out/linux/*"
321 | strip_components: 2
322 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.arch }}"
323 |
324 | - name: "Move artifacts into repository"
325 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
326 | with:
327 | host: "${{ secrets.REPO_HOST }}"
328 | username: "${{ secrets.REPO_USER }}"
329 | key: "${{ secrets.REPO_KEY }}"
330 | debug: false
331 | script: |
332 | set -e
333 | export BASEDIR="/srv/repository/main/server/linux"
334 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
335 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/linux/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
336 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
337 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
338 |
339 | Windows:
340 | runs-on: ubuntu-24.04
341 | strategy:
342 | fail-fast: false
343 | matrix:
344 | arch:
345 | - amd64
346 | - arm64
347 | outputs:
348 | JELLYFIN_VERSION: ${{ steps.version.outputs.JELLYFIN_VERSION }}
349 | JELLYFIN_RELEASE_TYPE: ${{ steps.version.outputs.JELLYFIN_RELEASE_TYPE }}
350 | continue-on-error: false # true in prod, false for testing
351 | steps:
352 | - name: "Set dated version for unstable builds"
353 | id: version
354 | run: |-
355 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
356 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_OUTPUT
357 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
358 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_OUTPUT
359 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
360 |
361 | - name: "Install dependencies"
362 | run: |-
363 | sudo apt-get update
364 | sudo apt-get install --yes python3-git python3-yaml
365 |
366 | - name: "Checkout repository"
367 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
368 |
369 | - name: "Prepare repository"
370 | run: |-
371 | ./checkout.py ${{ inputs.version || 'master' }}
372 |
373 | - name: "Run builder for ${{ matrix.arch }}"
374 | run: |-
375 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} windows ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
376 |
377 | - name: "Upload artifacts to repository server"
378 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
379 | with:
380 | host: "${{ secrets.REPO_HOST }}"
381 | username: "${{ secrets.REPO_USER }}"
382 | key: "${{ secrets.REPO_KEY }}"
383 | source: "out/windows/*"
384 | strip_components: 2
385 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
386 |
387 | - name: "Move artifacts into repository"
388 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
389 | with:
390 | host: "${{ secrets.REPO_HOST }}"
391 | username: "${{ secrets.REPO_USER }}"
392 | key: "${{ secrets.REPO_KEY }}"
393 | debug: false
394 | script: |
395 | set -e
396 | export BASEDIR="/srv/repository/main/server/windows"
397 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
398 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
399 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
400 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
401 |
402 | - name: "Store artifacts for next stage"
403 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
404 | with:
405 | name: windows-artifacts
406 | retention-days: 1
407 | if-no-files-found: ignore # Ignore for arm64 build, as no file will be found
408 | path: out/windows/jellyfin_*-amd64.zip
409 |
410 | WindowsInstaller:
411 | needs:
412 | - Windows
413 | runs-on: windows-latest
414 | strategy:
415 | fail-fast: false
416 | matrix:
417 | arch:
418 | - amd64
419 | continue-on-error: false # true in prod, false for testing
420 | env:
421 | DOTNET_CLI_TELEMETRY_OPTOUT: 1
422 | steps:
423 | - name: "Set dated version for unstable builds"
424 | id: version
425 | shell: pwsh
426 | run: |
427 | $version = "${{ needs.Windows.outputs.JELLYFIN_VERSION }}"
428 | $cleanVersion = $version
429 | echo "JELLYFIN_VERSION=$cleanVersion" | Out-File -FilePath $env:GITHUB_ENV -Append
430 |
431 | - name: "Checkout repository"
432 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
433 |
434 | - name: "Install dependencies"
435 | run: |-
436 | python -m pip install --upgrade gitpython
437 |
438 | - name: "Install NSIS"
439 | uses: negrutiu/nsis-install@f3339c88dba6fd08910d5275a943f8f746d94876 # v2
440 | with:
441 | distro: official
442 |
443 | - name: "Prepare repository"
444 | run: |-
445 | python checkout.py master
446 |
447 | - name: "Fetch artifacts from previous stage"
448 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
449 | with:
450 | name: windows-artifacts
451 | path: ./jellyfin-server-windows
452 |
453 | - name: "Clone UX repository"
454 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
455 | with:
456 | repository: jellyfin/jellyfin-ux
457 | path: .\jellyfin-server-windows\jellyfin-ux
458 |
459 | - name: "Extract Jellyfin server archive"
460 | working-directory: ./jellyfin-server-windows
461 | run: |
462 | New-Item -ItemType Directory -Path jellyfin
463 | Expand-Archive -Path 'jellyfin_*-amd64.zip' -DestinationPath jellyfin -Force
464 | Copy-Item ".\Support Files\LICENSE" -Destination $(Resolve-Path .\jellyfin\jellyfin)
465 |
466 | - name: "Add NSSM"
467 | working-directory: ./jellyfin-server-windows
468 | run: |
469 | Invoke-WebRequest 'https://repo.jellyfin.org/files/other/nssm.zip' -OutFile 'nssm.zip'
470 | Expand-Archive 'nssm.zip'
471 | Copy-Item ".\nssm\nssm.exe" -Destination $(Resolve-Path .\jellyfin\jellyfin)
472 |
473 | - name: "Publish tray"
474 | working-directory: ./jellyfin-server-windows
475 | run: |
476 | New-Item -Path .\jellyfin\jellyfin\jellyfin-windows-tray -ItemType Directory
477 | dotnet publish -c Release -r win-x64 -f net472 --no-self-contained --output $(Resolve-Path .\jellyfin\jellyfin\jellyfin-windows-tray)
478 |
479 | - name: "Build installer"
480 | working-directory: ./jellyfin-server-windows
481 | run: |
482 | $env:InstallLocation = $(Resolve-Path .\jellyfin\jellyfin)
483 | makensis /Dx64 /DUXPATH=$(Resolve-Path .\jellyfin-ux) $(Join-Path -Path $(Resolve-Path .\nsis) -ChildPath jellyfin.nsi)
484 |
485 | - name: "Rename installer"
486 | working-directory: ./jellyfin-server-windows/nsis
487 | run: |
488 | Rename-Item -Path .\jellyfin_*_windows-x64.exe -NewName ("jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe")
489 |
490 | - name: "Store artifacts for next stage"
491 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
492 | with:
493 | name: windows-installer-artifacts
494 | retention-days: 1
495 | if-no-files-found: error
496 | path: ./jellyfin-server-windows/nsis/jellyfin_${{ env.JELLYFIN_VERSION }}_windows-x64.exe
497 |
498 | WindowsInstallerUpload:
499 | needs:
500 | - Windows
501 | - WindowsInstaller
502 | runs-on: ubuntu-24.04
503 | strategy:
504 | fail-fast: false
505 | matrix:
506 | arch:
507 | - amd64
508 | continue-on-error: false # true in prod, false for testing
509 | steps:
510 | - name: "Set version from previous job"
511 | id: version
512 | run: |-
513 | echo "JELLYFIN_VERSION=${{ needs.Windows.outputs.JELLYFIN_VERSION }}" >> $GITHUB_ENV
514 | echo "JELLYFIN_RELEASE_TYPE=${{ needs.Windows.outputs.JELLYFIN_RELEASE_TYPE }}" >> $GITHUB_ENV
515 |
516 | - name: "Fetch artifact from previous stage"
517 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
518 | with:
519 | name: windows-installer-artifacts
520 |
521 | - name: "Upload artifacts to repository server"
522 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
523 | with:
524 | host: "${{ secrets.REPO_HOST }}"
525 | username: "${{ secrets.REPO_USER }}"
526 | key: "${{ secrets.REPO_KEY }}"
527 | source: "*.exe"
528 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}"
529 |
530 | - name: "Move artifacts into repository"
531 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
532 | with:
533 | host: "${{ secrets.REPO_HOST }}"
534 | username: "${{ secrets.REPO_USER }}"
535 | key: "${{ secrets.REPO_KEY }}"
536 | debug: false
537 | script: |
538 | set -e
539 | export BASEDIR="/srv/repository/main/server/windows"
540 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/windows/${{ matrix.arch }}/*.exe
541 |
542 | MacOS:
543 | runs-on: ubuntu-24.04
544 | strategy:
545 | fail-fast: false
546 | matrix:
547 | arch:
548 | - amd64
549 | - arm64
550 | continue-on-error: false # true in prod, false for testing
551 | steps:
552 | - name: "Set dated version for unstable builds"
553 | id: version
554 | run: |-
555 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
556 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
557 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
558 |
559 | - name: "Install dependencies"
560 | run: |-
561 | sudo apt-get update
562 | sudo apt-get install --yes python3-git python3-yaml
563 |
564 | - name: "Checkout repository"
565 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
566 |
567 | - name: "Prepare repository"
568 | run: |-
569 | ./checkout.py master
570 |
571 | - name: "Run builder for ${{ matrix.version }} ${{ matrix.arch }}"
572 | run: |-
573 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} macos ${{ matrix.arch }} ${{ env.DEBUG_FLAG }}
574 |
575 | - name: "Upload artifacts to repository server"
576 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
577 | with:
578 | host: "${{ secrets.REPO_HOST }}"
579 | username: "${{ secrets.REPO_USER }}"
580 | key: "${{ secrets.REPO_KEY }}"
581 | source: "out/macos/*"
582 | strip_components: 2
583 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.arch }}"
584 |
585 | - name: "Move artifacts into repository"
586 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
587 | with:
588 | host: "${{ secrets.REPO_HOST }}"
589 | username: "${{ secrets.REPO_USER }}"
590 | key: "${{ secrets.REPO_KEY }}"
591 | debug: false
592 | script: |
593 | set -e
594 | export BASEDIR="/srv/repository/main/server/macos"
595 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }} || exit 1
596 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/${{ matrix.arch }}/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/macos/${{ matrix.release }}/${{ matrix.arch }}/* || exit 1
597 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
598 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
599 |
600 | Portable:
601 | runs-on: ubuntu-24.04
602 | continue-on-error: false # true in prod, false for testing
603 | steps:
604 | - name: "Set dated version for unstable builds"
605 | id: version
606 | run: |-
607 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
608 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
609 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
610 |
611 | - name: "Install dependencies"
612 | run: |-
613 | sudo apt-get update
614 | sudo apt-get install --yes python3-git python3-yaml
615 |
616 | - name: "Checkout repository"
617 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
618 |
619 | - name: "Prepare repository"
620 | run: |-
621 | ./checkout.py ${{ inputs.version || 'master' }}
622 |
623 | - name: "Run builder for Portable"
624 | run: |-
625 | sudo --preserve-env ./build.py ${{ env.JELLYFIN_VERSION }} portable ${{ env.DEBUG_FLAG }}
626 |
627 | - name: "Upload artifacts to repository server"
628 | uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
629 | with:
630 | host: "${{ secrets.REPO_HOST }}"
631 | username: "${{ secrets.REPO_USER }}"
632 | key: "${{ secrets.REPO_KEY }}"
633 | source: "out/portable/*"
634 | strip_components: 2
635 | target: "/srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable"
636 |
637 | - name: "Move artifacts into repository"
638 | uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
639 | with:
640 | host: "${{ secrets.REPO_HOST }}"
641 | username: "${{ secrets.REPO_USER }}"
642 | key: "${{ secrets.REPO_KEY }}"
643 | debug: false
644 | script: |
645 | set -e
646 | export BASEDIR="/srv/repository/main/server/portable"
647 | sudo mkdir -p ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any || exit 1
648 | sudo mv -t ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }}/any/ /srv/incoming/server/${{ env.JELLYFIN_VERSION }}/portable/${{ matrix.release }}/* || exit 1
649 | sudo rm ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || true
650 | sudo ln -sf ${BASEDIR}/${{ env.JELLYFIN_RELEASE_TYPE }}/${{ env.JELLYFIN_VERSION }} ${BASEDIR}/latest-${{ env.JELLYFIN_RELEASE_TYPE }} || exit 1
651 |
652 | Nuget:
653 | runs-on: ubuntu-24.04
654 | steps:
655 | - name: "Set dated version for unstable builds"
656 | id: version
657 | run: |-
658 | echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H')" >> $GITHUB_ENV
659 | echo "JELLYFIN_RELEASE_TYPE=unstable" >> $GITHUB_ENV
660 | echo "DEBUG_FLAG=--debug" >>$GITHUB_ENV
661 |
662 | - name: "Install dependencies"
663 | run: |-
664 | sudo apt-get update
665 | sudo apt-get install --yes python3-git python3-yaml
666 |
667 | - name: "Setup .NET"
668 | uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
669 | with:
670 | dotnet-version: ${{ env.SDK_VERSION }}
671 |
672 | - name: "Checkout repository"
673 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
674 |
675 | - name: "Prepare repository"
676 | run: |-
677 | ./checkout.py master
678 |
679 | - name: "Run builder for Nuget"
680 | env:
681 | NUGET_STABLE_KEY: ${{ secrets.NUGET_STABLE_KEY }}
682 | NUGET_UNSTABLE_KEY: ${{ secrets.NUGET_UNSTABLE_KEY }}
683 | run: |-
684 | ./build.py ${{ env.JELLYFIN_VERSION }} nuget
685 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore temporary build files
2 | build/
3 | # Ignore the output artifact directory
4 | out/
5 | # Ignore the generated Debian changelog
6 | debian/changelog
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "jellyfin-server"]
2 | path = jellyfin-server
3 | url = https://github.com/jellyfin/jellyfin
4 | [submodule "jellyfin-web"]
5 | path = jellyfin-web
6 | url = https://github.com/jellyfin/jellyfin-web
7 | [submodule "jellyfin-server-windows"]
8 | path = jellyfin-server-windows
9 | url = https://github.com/jellyfin/jellyfin-server-windows.git
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Jellyfin Packaging
2 | Part of Jellyfin: The Free Software Media System
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 | Jellyfin is the Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps.
46 |
47 | This repository contains operating system and Docker packaging for Jellyfin, for use by manual builders and our release CI system with GitHub workflows. All packaging has henceforth been removed from the main code repositories for the [Jellyfin Server](https://github.com/jellyfin/jellyfin) and [Primary WebUI](https://github.com/jellyfin/jellyfin-web) and moved here.
48 |
49 | ## Quickstart
50 |
51 | To build Jellyfin packages for yourself, follow this quickstart guide. You will need to be running on an amd64 Linux system, preferably Debian- or Ubuntu-based, with Docker, Python3 and the Python packages `PyYAML` and `git` (`python3-yaml` and `python3-git` in Debian). Other systems including WSL are untested.
52 |
53 | 1. Install Docker on your system. The build scripts leverage Docker containers to perform clean builds and avoid contaminating the host system with dependencies.
54 |
55 | 1. Clone this repository somewhere on your system and enter it.
56 |
57 | 1. Run `git submodule update --init` to check out the submodules (`jellyfin-server`, `jellyfin-web`).
58 |
59 | 1. Run `./checkout.py` to update the submodules to the correct `HEAD`s. This command takes one argument, the tag or branch (i.e. `master`) of the repositories to check out; if nothing is specified, `master` is assumed. For example, `./checkout.py master` checks out the current `master` branch of both `jellyfin-server` and `jellyfin-web`, `./checkout.py v10.8.13` checks out the `v10.8.13` tag of both, etc. If a tag is used and one (or more) of the repositories are missing the tag, this command will error out.
60 |
61 | ### Non-Docker Platforms
62 |
63 | If you want a non-Docker image output (`.deb`, `tar`/`zip` archive, etc.) follow this process:
64 |
65 | 1. Run `./build.py`. This command takes up to 4 arguments, depending on what you're trying to build:
66 |
67 | * The first argument is the version you want to tag your build as. For our official releases, we use either dates for unstable builds (`YYYYMMDDHH` numerical format or `auto` for autogeneration) or the tag without `v` for stable release builds (`10.8.13`, `10.9.0`, etc.), but you can use any version tag you wish here.
68 |
69 | * The second argument is the "platform" you want to build for. The available options are listed as top-level keys in the `build.yaml` configuration file or in the `-h` help output.
70 |
71 | * The third argument is, for all platforms except `portable` (DotNET portable), the architecture you want to build for. For each platform, the available architectures can be found as the keys under `archmaps` in the `build.yaml` configuration file. Cross-building of `arm64` on `amd64` is supported, but not the other way around.
72 |
73 | * The fourth argument is exclusive to `debian` and `ubuntu` `.deb` packages, and is the release codename of Debian or Ubuntu to build for. For each platform, the available releases can be found as the keys under `releases` in the `build.yaml` configuration file.
74 |
75 | **NOTE:** Your running user must have Docker privileges, or you should run `build.py` as root/with `sudo`.
76 |
77 | 1. The output binaries will be in the `out/` directory, ready for use. The exact format varies depending on the build type, and can be found, for each archive-based platform, as the values to the `archivetypes` key in the `build.yaml` configuration file.
78 |
79 | #### Examples
80 |
81 | Build `.deb` packages for Debian 13 "Trixie" `amd64`:
82 |
83 | ```
84 | ./build.py auto debian amd64 trixie
85 | ```
86 |
87 | Build Linux `.tar.xx` archives for `arm64-musl`:
88 |
89 | ```
90 | ./build.py auto linux arm64-musl
91 | ```
92 |
93 | Build Windows `.zip` for `amd64`:
94 |
95 | ```
96 | ./build.py auto windows amd64
97 | ```
98 |
99 | Build a .NET portable `.zip`:
100 |
101 | ```
102 | ./build.py auto portable
103 | ```
104 |
105 | ### Docker Image Platform
106 |
107 | If you want a Docker image output follow this process:
108 |
109 | 1. Install `docker` and `docker-buildx-plugin` for your OS.
110 |
111 | 1. Run `./build.py`. This command takes up to 4 arguments specific to Docker builds:
112 |
113 | * The first argument is the version you want to tag your build as. For our official releases, we use either dates for unstable builds (`YYYYMMDDHH` numerical format or `auto` for autogeneration) or the tag without `v` for stable release builds (`10.8.13`, `10.9.0`, etc.), but you can use any version tag you wish here.
114 |
115 | * The second argument is the "platform" you want to build for. For Docker images, this should be `docker`.
116 |
117 | * The third argument is the architecture you wish to build for. This argument is optional, and not providing it will build images for all supported architectures (sequentially). Cross-building of `arm64` on `amd64` is supported, but not the other way around.
118 |
119 | * The fourth argument is `--local`, which should be provided to prevent the script from trying to generate image manifests and push the resulting images to our repositories.
120 |
121 | 1. The output container image(s) will be present in your `docker image ls` as `jellyfin/jellyfin` with the tag(s) `-`.
122 |
123 | #### Examples
124 |
125 | Build an `amd64` Docker image:
126 |
127 | ```
128 | ./build.py auto docker amd64 --local
129 | ```
130 |
131 | ## Design
132 |
133 | Inside this repository are 7 major components:
134 |
135 | 1. Submodules for the `jellyfin` (as `jellyfin-server`) and `jellyfin-web` repositories. These are dynamic submodules; the `checkout.py` script will check them out to the required `HEAD` on each build, and thus their actual committed value is irrelevant. Nonetheless, they should be bumped occasionally just to avoid excessive checkout times later.
136 |
137 | 1. Debian/Ubuntu packaging configurations (under `debian`). These will build the 3 Jellyfin packages (`jellyfin` metapackage, `jellyfin-server` core server, and `jellyfin-web` web client) from a single Dockerfile and helper script (`build.sh`) under `debian/docker/`. Future packages (e.g. Vue) may be added here as well if and when they are promoted to a production build alongside the others, following one consistent versioning scheme.
138 |
139 | 1. Docker image builder (under `docker`). Like the above two as well, only building the combined Docker images with a single Dockerfile as well as preparing the various manifests needed to push these to the container repos.
140 |
141 | 1. Portable image builder (under `portable`), which covers all the "archive" builds (.NET portable, Linux, Windows, and MacOS) again from a single Dockerfile.
142 |
143 | 1. NuGet package builder, to prepare NuGet packages for consumption by plugins and 3rd party clients.
144 |
145 | 1. Script infrastructure to handle coordinating builds (`build.py`). This script takes basic arguments and, using its internal logic, fires the correct Dockerized builds for the given build type.
146 |
147 | 1. The GitHub Actions CI to build all the above for every supported version and architecture.
148 |
149 | ## Design Decisions
150 |
151 | ### General
152 |
153 | * Unified packaging: all packaging is in this repository (vs. within the `jellyfin-server` and `jellyfin-web` repositories)
154 |
155 | This helps ensure two things:
156 | 1. There is a single source of truth for packaging. Previously, there were at least 3 sources of truth, and this became very confusing to update.
157 | 2. Packaging can be run and managed independently of actual code, simplifying the release and build process.
158 |
159 | * GitHub Actions for CI: all builds use the GitHub Actions system instead of Azure DevOps
160 |
161 | This helps ensure that CI is visible in a "single pane of glass" (GitHub) and is easier to manage long-term.
162 |
163 | * Python script-based builds: Building actually happens via the `build.py` script
164 |
165 | This helps reduce the complexity of the builds by ensuring that the logic to actually generate specific builds is handled in one place in a consistent, well-known language, instead of the CI definitions.
166 |
167 | * Git Submodules to handle code (vs. cross-repo builds)
168 |
169 | This ensures that the code checked out is consistent to both repositories, and allows for the unified builds described below without extra steps to combine.
170 |
171 | * Remote manual-only triggers: CI workers are triggered by a remote bot
172 |
173 | This reduces the complexity of triggering builds; while it can be done manually in this repo, using an external bot allows for more release wrapper actions to occur before triggering builds.
174 |
175 | ### Debian/Ubuntu Packages
176 |
177 | * Unified package build: this entire repo is the "source" and the source package is named "jellyfin".
178 |
179 | This was chosen to simplify the source package system and simplify building. Now, there is only a single "jellyfin" source package rather than 2. There may be more in the future as other repos might be included (e.g. "jellyfin-ffmpeg", "jellyfin-vue", etc.)
180 |
181 | * Dockerized build (`debian/docker/`): the build is run inside a Docker container that matches the target OS release
182 |
183 | This was chosen to ensure a clean slate for every build, as well as enable release-specific builds due to the complexities of our shared dependencies (e.g. `libssl`).
184 |
185 | * Per-release/version builds: package versions contain the specific OS version (e.g. `-deb11`, `-ubu2204`)
186 |
187 | This enables support for different builds and packages for each OS release, simplifying shared dependency handling as mentioned above.
188 |
189 | * Ubuntu LTS-only support: non-LTS Ubuntu versions have been dropped
190 |
191 | This simplifies our builds as we do not need to then track many 9-month-only releases of Ubuntu, and also reduces the build burden. Users of non-LTS Ubuntu releases can use either the closest Ubuntu LTS version or use Docker containers instead.
192 |
193 | * Signing of Debian packages with `debsigs`.
194 |
195 | This was suggested in https://github.com/jellyfin/jellyfin-packaging/issues/14 and was not something we had ever done, but has become trivial with this CI. This alows for the end-user verification of the ownership and integrity of manually downloaded binary `.deb` files obtained from the repository with the `debsigs-verify` command and the policy detailed in that issue. Note that since Debian as a whole (i.e. `dpkg`, `apt`, etc.) does not enforce package signing at this time, enabling this for the *repository* is not possible; conventional repository signatures (using the same signing key) are considered sufficient.
196 |
197 | ### Docker
198 |
199 | * Single unified Docker build: the entirety of our Docker images are built as one container from one Dockerfile.
200 |
201 | This was chosen to keep our Docker builds as simple as possible without requiring 2 intervening images (as was the case with our previous CI).
202 |
203 | * Push to both DockerHub and GHCR (GitHub Packages)
204 |
205 | This ensures flexibility for container users to fetch the containers from whatever repository they choose.
206 |
207 | * Seamless rebuilds: The root images are appended with the build date to keep them unique
208 |
209 | This ensures we can trigger rebuilds of the Docker containers arbitrarily, in response to things like base OS updates or packaging changes (e.g. a new version of the Intel compute engine for instance).
210 |
211 | * Based on Debian 12 ("Bookworm"): the latest base Debian release
212 |
213 | While possibly not as "up-to-date" as Ubuntu, this release is quite current and should cover all major compatibility issues we had with the old images based on Debian 11.
214 |
215 | ### Portable Builds (Portable .NET, Linux, MacOS, Windows)
216 |
217 | * Single unified build: the entirety of the output package is built in one container from one Dockerfile
218 |
219 | This was chosen to keep the portable builds as simple as possible without requiring complex archive combining (as was the case with our previous CI).
220 |
221 | * Multiple archive type support (`.tar.gz` vs. `.zip`)
222 |
223 | The output archive type (`.tar.gz` or `.zip`) is chosen based on the build target, with Portable providing both for maximum compatibility, Windows providing `.zip`, and Linux and MacOS providing `.tar.gz`. This can be changed later, for example to add more formats (e.g. `.tar.xz`) or change what produces what, without major complications.
224 |
225 | * Full architecture support
226 |
227 | The portable builds support all major architectures now, specifically adding `arm64` Windows builds (I'm certain that _someone_ out there uses it), and making it quite trivial to add new architectures in the future if needed.
228 |
--------------------------------------------------------------------------------
/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # build.py - Build packages in a Docker wrapper
4 | #
5 | # Part of the Jellyfin CI system
6 | ###############################################################################
7 |
8 | from argparse import ArgumentParser
9 | from datetime import datetime
10 | from email.utils import format_datetime, localtime
11 | from git import Repo
12 | from os import getenv
13 | import os.path
14 | from packaging.version import Version
15 | from subprocess import run, PIPE
16 | import sys
17 | from yaml import load, SafeLoader
18 |
19 | # Determine top level directory of this repository ("jellyfin-packaging")
20 | revparse = run(["git", "rev-parse", "--show-toplevel"], stdout=PIPE)
21 | repo_root_dir = revparse.stdout.decode().strip()
22 |
23 | # Base Docker commands
24 | docker_build_cmd = "docker buildx build --progress=plain --no-cache"
25 | docker_run_cmd = "docker run --rm"
26 |
27 |
28 | def log(message):
29 | print(message, flush=True)
30 |
31 |
32 | # Configuration loader
33 | try:
34 | with open("build.yaml", encoding="utf-8") as fh:
35 | configurations = load(fh, Loader=SafeLoader)
36 | except Exception as e:
37 | log(f"Error: Failed to find 'build.yaml' configuration: {e}")
38 | exit(1)
39 |
40 |
41 | # Shared functions
42 | def _determine_arch(build_type, build_arch, build_version):
43 | PACKAGE_ARCH = (
44 | configurations[build_type]["archmaps"][build_arch]["PACKAGE_ARCH"]
45 | if build_arch in configurations[build_type]["archmaps"].keys()
46 | else None
47 | )
48 | if PACKAGE_ARCH is None:
49 | raise ValueError(
50 | f"{build_arch} is not a valid {build_type} {build_version} architecture in {configurations[build_type]['archmaps'].keys()}"
51 | )
52 | else:
53 | return PACKAGE_ARCH
54 |
55 | def _determine_framework_versions():
56 | # Prepare repo object for this repository
57 | this_repo = Repo(repo_root_dir)
58 |
59 | framework_args = dict()
60 |
61 | for submodule in this_repo.submodules:
62 | if submodule.name in configurations["frameworks"].keys():
63 | for framework_arg in configurations["frameworks"][submodule.name].keys():
64 | framework_args[framework_arg] = None
65 | def sort_versions(input_dict):
66 | return dict(sorted(input_dict.items(), key=lambda item: Version(str(item[1]))))
67 | for commit_hash in sort_versions(configurations["frameworks"][submodule.name][framework_arg]):
68 | try:
69 | commit = submodule.module().commit(commit_hash)
70 | if commit in submodule.module().iter_commits('HEAD'):
71 | framework_args[framework_arg] = configurations["frameworks"][submodule.name][framework_arg][commit_hash]
72 | except ValueError:
73 | continue
74 |
75 | log(f"Determined the following framework versions based on current HEAD values:")
76 | for k, v in framework_args.items():
77 | log(f" * {k}: {v}")
78 | log("")
79 |
80 | return framework_args
81 |
82 |
83 | def build_package_deb(
84 | jellyfin_version, build_type, build_arch, build_version, local=False, debug=False
85 | ):
86 | """
87 | Build a .deb package (Debian or Ubuntu) within a Docker container that matches the requested distribution version
88 | """
89 | log(f"> Building an {build_arch} {build_type} .deb package...")
90 | log("")
91 |
92 | try:
93 | os_type = build_type if build_type in configurations.keys() else None
94 | if os_type is None:
95 | raise ValueError(
96 | f"{build_type} is not a valid OS type in {configurations.keys()}"
97 | )
98 | os_version = (
99 | configurations[build_type]["releases"][build_version]
100 | if build_version in configurations[build_type]["releases"].keys()
101 | else None
102 | )
103 | if os_version is None:
104 | raise ValueError(
105 | f"{build_version} is not a valid {build_type} version in {configurations[build_type]['releases'].keys()}"
106 | )
107 | PACKAGE_ARCH = _determine_arch(build_type, build_arch, build_version)
108 | GCC_ARCH = configurations[build_type]["archmaps"][build_arch]["GCC_ARCH"]
109 | except Exception as e:
110 | log(f"Invalid/unsupported arguments: {e}")
111 | exit(1)
112 |
113 | # Set the dockerfile
114 | dockerfile = configurations[build_type]["dockerfile"]
115 |
116 | # Set the cross-gcc version
117 | crossgccvers = configurations[build_type]["cross-gcc"][build_version]
118 |
119 | # Prepare the debian changelog file
120 | changelog_src = f"{repo_root_dir}/debian/changelog.in"
121 | changelog_dst = f"{repo_root_dir}/debian/changelog"
122 |
123 | with open(changelog_src) as fh:
124 | changelog = fh.read()
125 |
126 | if "v" in jellyfin_version:
127 | comment = f"Jellyfin release {jellyfin_version}, see https://github.com/jellyfin/jellyfin/releases/{jellyfin_version} for details."
128 | else:
129 | comment = f"Jellyin unstable release {jellyfin_version}."
130 | jellyfin_version = jellyfin_version.replace("v", "")
131 |
132 | changelog = changelog.format(
133 | package_version=jellyfin_version,
134 | package_build=f"{build_type[:3]}{os_version.replace('.', '')}",
135 | release_comment=comment,
136 | release_date=format_datetime(localtime()),
137 | )
138 |
139 | with open(changelog_dst, "w") as fh:
140 | fh.write(changelog)
141 |
142 | # Use a unique docker image name for consistency
143 | imagename = f"{configurations[build_type]['imagename']}-{jellyfin_version}_{build_arch}-{build_type}-{build_version}"
144 |
145 | # Prepare the list of build-args
146 | build_args = list()
147 | build_args.append(f"--build-arg PACKAGE_TYPE={os_type}")
148 | build_args.append(f"--build-arg PACKAGE_VERSION={os_version}")
149 | build_args.append(f"--build-arg PACKAGE_ARCH={PACKAGE_ARCH}")
150 | build_args.append(f"--build-arg GCC_ARCH={GCC_ARCH}")
151 | build_args.append(f"--build-arg GCC_VERSION={crossgccvers}")
152 |
153 | # Determine framework versions
154 | framework_versions = _determine_framework_versions()
155 | for arg in framework_versions.keys():
156 | if framework_versions[arg] is not None:
157 | build_args.append(
158 | f"--build-arg {arg}={framework_versions[arg]}"
159 | )
160 |
161 | build_args = ' '.join(build_args)
162 |
163 | # Build the dockerfile and packages
164 | log(
165 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
166 | )
167 | os.system(
168 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
169 | )
170 |
171 | log(
172 | f">>> {docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
173 | )
174 | os.system(
175 | f"{docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
176 | )
177 |
178 |
179 | def build_linux(
180 | jellyfin_version, build_type, build_arch, _build_version, local=False, debug=False
181 | ):
182 | """
183 | Build a portable Linux archive
184 | """
185 | log(f"> Building a portable {build_arch} Linux archive...")
186 | log("")
187 |
188 | try:
189 | PACKAGE_ARCH = _determine_arch(build_type, build_arch, _build_version)
190 | DOTNET_ARCH = configurations[build_type]["archmaps"][build_arch]["DOTNET_ARCH"]
191 | except Exception as e:
192 | log(f"Invalid/unsupported arguments: {e}")
193 | exit(1)
194 |
195 | jellyfin_version = jellyfin_version.replace("v", "")
196 |
197 | # Set the dockerfile
198 | dockerfile = configurations[build_type]["dockerfile"]
199 |
200 | # Use a unique docker image name for consistency
201 | imagename = f"{configurations[build_type]['imagename']}-{jellyfin_version}_{build_arch}-{build_type}"
202 |
203 | # Set the archive type (tar-gz or zip)
204 | archivetypes = f"{configurations[build_type]['archivetypes']}"
205 |
206 | # Prepare the list of build-args
207 | build_args = list()
208 |
209 | # Determine framework versions
210 | framework_versions = _determine_framework_versions()
211 | for arg in framework_versions.keys():
212 | if framework_versions[arg] is not None:
213 | build_args.append(
214 | f"--build-arg {arg}={framework_versions[arg]}"
215 | )
216 |
217 | build_args = ' '.join(build_args)
218 |
219 | # Build the dockerfile and packages
220 | log(
221 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
222 | )
223 | os.system(
224 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
225 | )
226 |
227 | log(
228 | f">>> {docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=linux --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
229 | )
230 | os.system(
231 | f"{docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=linux --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
232 | )
233 |
234 |
235 | def build_windows(
236 | jellyfin_version, build_type, _build_arch, _build_version, local=False, debug=False
237 | ):
238 | """
239 | Build a portable Windows archive
240 | """
241 | log(f"> Building a portable {build_arch} Windows archive...")
242 | log("")
243 |
244 | try:
245 | PACKAGE_ARCH = _determine_arch(build_type, build_arch, _build_version)
246 | DOTNET_ARCH = configurations[build_type]["archmaps"][build_arch]["DOTNET_ARCH"]
247 | except Exception as e:
248 | log(f"Invalid/unsupported arguments: {e}")
249 | exit(1)
250 |
251 | jellyfin_version = jellyfin_version.replace("v", "")
252 |
253 | # Set the dockerfile
254 | dockerfile = configurations[build_type]["dockerfile"]
255 |
256 | # Use a unique docker image name for consistency
257 | imagename = f"{configurations[build_type]['imagename']}-{jellyfin_version}_{build_arch}-{build_type}"
258 |
259 | # Set the archive type (tar-gz or zip)
260 | archivetypes = f"{configurations[build_type]['archivetypes']}"
261 |
262 | # Prepare the list of build-args
263 | build_args = list()
264 |
265 | # Determine framework versions
266 | framework_versions = _determine_framework_versions()
267 | for arg in framework_versions.keys():
268 | if framework_versions[arg] is not None:
269 | build_args.append(
270 | f"--build-arg {arg}={framework_versions[arg]}"
271 | )
272 |
273 | build_args = ' '.join(build_args)
274 |
275 | # Build the dockerfile and packages
276 | log(
277 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
278 | )
279 | os.system(
280 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
281 | )
282 |
283 | log(
284 | f">>> {docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=win --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
285 | )
286 | os.system(
287 | f"{docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=win --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
288 | )
289 |
290 |
291 | def build_macos(
292 | jellyfin_version, build_type, build_arch, _build_version, local=False, debug=False
293 | ):
294 | """
295 | Build a portable MacOS archive
296 | """
297 | log(f"> Building a portable {build_arch} MacOS archive...")
298 | log("")
299 |
300 | try:
301 | PACKAGE_ARCH = _determine_arch(build_type, build_arch, _build_version)
302 | DOTNET_ARCH = configurations[build_type]["archmaps"][build_arch]["DOTNET_ARCH"]
303 | except Exception as e:
304 | log(f"Invalid/unsupported arguments: {e}")
305 | exit(1)
306 |
307 | jellyfin_version = jellyfin_version.replace("v", "")
308 |
309 | # Set the dockerfile
310 | dockerfile = configurations[build_type]["dockerfile"]
311 |
312 | # Use a unique docker image name for consistency
313 | imagename = f"{configurations[build_type]['imagename']}-{jellyfin_version}_{build_arch}-{build_type}"
314 |
315 | # Set the archive type (tar-gz or zip)
316 | archivetypes = f"{configurations[build_type]['archivetypes']}"
317 |
318 | # Prepare the list of build-args
319 | build_args = list()
320 |
321 | # Determine framework versions
322 | framework_versions = _determine_framework_versions()
323 | for arg in framework_versions.keys():
324 | if framework_versions[arg] is not None:
325 | build_args.append(
326 | f"--build-arg {arg}={framework_versions[arg]}"
327 | )
328 |
329 | build_args = ' '.join(build_args)
330 |
331 | # Build the dockerfile and packages
332 | log(
333 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
334 | )
335 | os.system(
336 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
337 | )
338 |
339 | log(
340 | f">>> {docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=osx --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
341 | )
342 | os.system(
343 | f"{docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env PACKAGE_ARCH={PACKAGE_ARCH} --env DOTNET_TYPE=osx --env DOTNET_ARCH={DOTNET_ARCH} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
344 | )
345 |
346 |
347 | def build_portable(
348 | jellyfin_version, build_type, _build_arch, _build_version, local=False, debug=False
349 | ):
350 | """
351 | Build a portable .NET archive
352 | """
353 | log("> Building a portable .NET archive...")
354 | log("")
355 |
356 | jellyfin_version = jellyfin_version.replace("v", "")
357 |
358 | # Set the dockerfile
359 | dockerfile = configurations[build_type]["dockerfile"]
360 |
361 | # Use a unique docker image name for consistency
362 | imagename = (
363 | f"{configurations[build_type]['imagename']}-{jellyfin_version}_{build_type}"
364 | )
365 |
366 | # Set the archive type (tar-gz or zip)
367 | archivetypes = f"{configurations[build_type]['archivetypes']}"
368 |
369 | # Prepare the list of build-args
370 | build_args = list()
371 |
372 | # Determine framework versions
373 | framework_versions = _determine_framework_versions()
374 | for arg in framework_versions.keys():
375 | if framework_versions[arg] is not None:
376 | build_args.append(
377 | f"--build-arg {arg}={framework_versions[arg]}"
378 | )
379 |
380 | build_args = ' '.join(build_args)
381 |
382 | # Build the dockerfile and packages
383 | log(
384 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
385 | )
386 | os.system(
387 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
388 | )
389 |
390 | log(
391 | f">>> {docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
392 | )
393 | os.system(
394 | f"{docker_run_cmd} --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{build_type}:/dist --env JELLYFIN_VERSION={jellyfin_version} --env BUILD_TYPE={build_type} --env ARCHIVE_TYPES={archivetypes} --env CONFIG={'Debug' if debug else 'Release'} --name {imagename} {imagename}"
395 | )
396 |
397 |
398 | def build_docker(
399 | jellyfin_version, build_type, build_arch, _build_version, local=False, debug=False
400 | ):
401 | """
402 | Build Docker images for one or all architectures and combining manifests
403 | """
404 | log("> Building Docker images...")
405 |
406 | if build_arch:
407 | log(f"NOTE: Building only for arch {build_arch}")
408 |
409 | # We build all architectures simultaneously to push a single tag, so no conditional checks
410 | architectures = configurations["docker"]["archmaps"].keys()
411 |
412 | if build_arch:
413 | if build_arch not in architectures:
414 | log(f"Error: Architecture {build_arch} is not valid.")
415 | exit(1)
416 | else:
417 | architectures = [build_arch]
418 |
419 | # Set the dockerfile
420 | dockerfile = configurations[build_type]["dockerfile"]
421 |
422 | # Determine if this is a "latest"-type image (v in jellyfin_version) or not
423 | if "v" in jellyfin_version and not "rc" in jellyfin_version:
424 | is_stable = True
425 | is_preview = False
426 | elif "rc" in jellyfin_version:
427 | is_stable = False
428 | is_preview = True
429 | else:
430 | is_stable = False
431 | is_preview = False
432 |
433 | jellyfin_version = jellyfin_version.replace("v", "")
434 |
435 | log(f"NOTE: Build type is {'stable' if is_stable else 'preview/unstable'} for version {jellyfin_version}")
436 | log("")
437 |
438 | # Set today's date in a convenient format for use as an image suffix
439 | date = datetime.now().strftime("%Y%m%d-%H%M%S")
440 |
441 | images_hub = list()
442 | images_ghcr = list()
443 | for _build_arch in architectures:
444 | log(f">> Building Docker image for {_build_arch}...")
445 | log("")
446 |
447 | # Get our ARCH variables from the archmaps
448 | PACKAGE_ARCH = configurations["docker"]["archmaps"][_build_arch]["PACKAGE_ARCH"]
449 | DOTNET_ARCH = configurations["docker"]["archmaps"][_build_arch]["DOTNET_ARCH"]
450 | IMAGE_ARCH = configurations["docker"]["archmaps"][_build_arch]["IMAGE_ARCH"]
451 | TARGET_ARCH = configurations["docker"]["archmaps"][_build_arch]["TARGET_ARCH"]
452 |
453 | # Use a unique docker image name for consistency
454 | if is_stable or is_preview:
455 | imagename = f"{configurations['docker']['imagename']}:{jellyfin_version}-{_build_arch}.{date}"
456 | else:
457 | imagename = f"{configurations['docker']['imagename']}:{jellyfin_version}-{_build_arch}"
458 |
459 | # Clean up any existing qemu static image
460 | log(
461 | f">>> {docker_run_cmd} --privileged linuxserver/qemu-static --reset -p yes"
462 | )
463 | os.system(
464 | f"{docker_run_cmd} --privileged linuxserver/qemu-static --reset -p yes"
465 | )
466 | log("")
467 |
468 | # Prepare the list of build-args
469 | build_args = list()
470 | build_args.append(f"--build-arg PACKAGE_ARCH={PACKAGE_ARCH}")
471 | build_args.append(f"--build-arg DOTNET_ARCH={DOTNET_ARCH}")
472 | build_args.append(f"--build-arg IMAGE_ARCH={IMAGE_ARCH}")
473 | build_args.append(f"--build-arg TARGET_ARCH={TARGET_ARCH}")
474 | build_args.append(f"--build-arg JELLYFIN_VERSION={jellyfin_version}")
475 | build_args.append(f"--build-arg CONFIG={'Debug' if debug else 'Release'}")
476 |
477 | # Determine framework versions
478 | framework_versions = _determine_framework_versions()
479 | for arg in framework_versions.keys():
480 | if framework_versions[arg] is not None:
481 | build_args.append(
482 | f"--build-arg {arg}={framework_versions[arg]}"
483 | )
484 |
485 | if local:
486 | build_args.append("--load")
487 |
488 | build_args = ' '.join(build_args)
489 |
490 | # Build the dockerfile
491 | log(
492 | f">>> {docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
493 | )
494 | ret = os.system(
495 | f"{docker_build_cmd} {build_args} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}"
496 | )
497 | if ret > 0:
498 | exit(1)
499 | images_hub.append(imagename)
500 |
501 | if not local:
502 | os.system(f"docker image tag {imagename} ghcr.io/{imagename}")
503 | images_ghcr.append(f"ghcr.io/{imagename}")
504 |
505 | log("")
506 |
507 | if local:
508 | return
509 |
510 | if not getenv('DOCKER_USERNAME') or not getenv('DOCKER_TOKEN'):
511 | log("Warning: No DOCKER_USERNAME or DOCKER_TOKEN in environment; skipping manifest build and push (DockerHub and GHCR).")
512 | return
513 |
514 | def build_manifests(server, images):
515 | # Build the manifests
516 | log(f">> Building Docker manifests for {server}...")
517 | manifests = list()
518 |
519 | if is_stable or is_preview:
520 | log(">>> Building X.Y.Z dated version manifest...")
521 | log(
522 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:{jellyfin_version}.{date} {' '.join(images)}"
523 | )
524 | os.system(
525 | f"docker manifest create {server}/{configurations['docker']['imagename']}:{jellyfin_version}.{date} {' '.join(images)}"
526 | )
527 | manifests.append(
528 | f"{server}/{configurations['docker']['imagename']}:{jellyfin_version}.{date}"
529 | )
530 |
531 | log(">>> Building X.Y.Z version manifest...")
532 | log(
533 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:{jellyfin_version} {' '.join(images)}"
534 | )
535 | os.system(
536 | f"docker manifest create {server}/{configurations['docker']['imagename']}:{jellyfin_version} {' '.join(images)}"
537 | )
538 | manifests.append(f"{server}/{configurations['docker']['imagename']}:{jellyfin_version}")
539 |
540 | if is_stable:
541 | # Build major-minor point version
542 | log(">>> Building X.Y version manifest...")
543 | manifest_version_xy = '.'.join(jellyfin_version.split('.')[0:2])
544 | log(
545 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:{manifest_version_xy} {' '.join(images)}"
546 | )
547 | os.system(
548 | f"docker manifest create {server}/{configurations['docker']['imagename']}:{manifest_version_xy} {' '.join(images)}"
549 | )
550 | manifests.append(f"{server}/{configurations['docker']['imagename']}:{manifest_version_xy}")
551 |
552 | # Build major-only point version
553 | log(">>> Building X version manifest...")
554 | manifest_version_x = '.'.join(jellyfin_version.split('.')[0:1])
555 | log(
556 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:{manifest_version_x} {' '.join(images)}"
557 | )
558 | os.system(
559 | f"docker manifest create {server}/{configurations['docker']['imagename']}:{manifest_version_x} {' '.join(images)}"
560 | )
561 | manifests.append(f"{server}/{configurations['docker']['imagename']}:{manifest_version_x}")
562 |
563 | log(">>> Building latest manifest...")
564 | log(
565 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:latest {' '.join(images)}"
566 | )
567 | os.system(
568 | f"docker manifest create {server}/{configurations['docker']['imagename']}:latest {' '.join(images)}"
569 | )
570 | manifests.append(f"{server}/{configurations['docker']['imagename']}:latest")
571 | elif is_preview:
572 | log(">>> Building preview manifest...")
573 | log(
574 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:preview {' '.join(images)}"
575 | )
576 | os.system(
577 | f"docker manifest create {server}/{configurations['docker']['imagename']}:preview {' '.join(images)}"
578 | )
579 | manifests.append(f"{server}/{configurations['docker']['imagename']}:preview")
580 | else:
581 | log(">>> Building unstable manifest...")
582 | log(
583 | f">>>> docker manifest create {server}/{configurations['docker']['imagename']}:unstable {' '.join(images)}"
584 | )
585 | os.system(
586 | f"docker manifest create {server}/{configurations['docker']['imagename']}:unstable {' '.join(images)}"
587 | )
588 | manifests.append(f"{server}/{configurations['docker']['imagename']}:unstable")
589 |
590 | return manifests
591 |
592 | # Log in to DockerHub
593 | os.system(
594 | f"docker login -u {getenv('DOCKER_USERNAME')} -p {getenv('DOCKER_TOKEN')} 2>&1"
595 | )
596 |
597 | # Push the images to DockerHub
598 | for image in images_hub:
599 | log(f">>> Pushing image {image} to DockerHub")
600 | log(f">>>> docker push {image} 2>&1")
601 | os.system(f"docker push {image} 2>&1")
602 |
603 | manifests_hub = build_manifests("docker.io", images_hub)
604 |
605 | # Push the images and manifests to DockerHub
606 | for manifest in manifests_hub:
607 | log(f">>> Pushing manifest {manifest} to DockerHub")
608 | log(f">>>> docker manifest push --purge {manifest} 2>&1")
609 | os.system(f"docker manifest push --purge {manifest} 2>&1")
610 |
611 | # Log out of DockerHub
612 | os.system("docker logout")
613 |
614 | # Log in to GHCR
615 | os.system(
616 | f"docker login -u {getenv('GHCR_USERNAME')} -p {getenv('GHCR_TOKEN')} ghcr.io 2>&1"
617 | )
618 |
619 | # Push the images to GHCR
620 | for image in images_ghcr:
621 | log(f">>> Pushing image {image} to GHCR")
622 | log(f">>>> docker push {image} 2>&1")
623 | os.system(f"docker push {image} 2>&1")
624 |
625 | manifests_ghcr = build_manifests("ghcr.io", images_ghcr)
626 |
627 | # Push the images and manifests to GHCR
628 | for manifest in manifests_ghcr:
629 | log(f">>> Pushing manifest {manifest} to GHCR")
630 | log(f">>>> docker manifest push --purge {manifest} 2>&1")
631 | os.system(f"docker manifest push --purge {manifest} 2>&1")
632 |
633 | # Log out of GHCR
634 | os.system("docker logout")
635 |
636 |
637 | def build_nuget(
638 | jellyfin_version, build_type, _build_arch, _build_version, local=False, debug=False
639 | ):
640 | """
641 | Pack and upload nuget packages
642 | """
643 | log("> Building Nuget packages...")
644 | log("")
645 |
646 | project_files = configurations["nuget"]["projects"]
647 | log(project_files)
648 |
649 | # Determine if this is a "latest"-type image (v in jellyfin_version) or not
650 | if "v" in jellyfin_version and not "rc" in jellyfin_version:
651 | is_stable = True
652 | is_preview = False
653 | elif "rc" in jellyfin_version:
654 | is_stable = False
655 | is_preview = True
656 | else:
657 | is_stable = False
658 | is_preview = False
659 |
660 | jellyfin_version = jellyfin_version.replace("v", "")
661 |
662 | # Set today's date in a convenient format for use as an image suffix
663 | date = datetime.now().strftime("%Y%m%d%H%M%S")
664 |
665 | pack_command_base = "dotnet pack -o out/nuget/"
666 | if is_stable or is_preview:
667 | pack_command = f"{pack_command_base} -p:Version={jellyfin_version}"
668 | else:
669 | pack_command = (
670 | f"{pack_command_base} --version-suffix {date} -p:Stability=Unstable"
671 | )
672 |
673 | for project in project_files:
674 | log(f">> Packing {project}...")
675 | log("")
676 |
677 | project_pack_command = f"{pack_command} jellyfin-server/{project}"
678 | log(f">>>> {project_pack_command}")
679 | os.system(project_pack_command)
680 |
681 | if local:
682 | return
683 |
684 | if is_stable or is_preview:
685 | nuget_repo = configurations["nuget"]["feed_urls"]["stable"]
686 | nuget_key = getenv("NUGET_STABLE_KEY")
687 | else:
688 | nuget_repo = configurations["nuget"]["feed_urls"]["unstable"]
689 | nuget_key = getenv("NUGET_UNSTABLE_KEY")
690 |
691 | if nuget_key is None:
692 | log(f"Error: Failed to get NUGET_*_KEY environment variable")
693 | exit(1)
694 |
695 | push_command = f"dotnet nuget push out/nuget/*.nupkg -s {nuget_repo} -k {nuget_key}"
696 | log(f">>>> {push_command}")
697 | os.system(push_command)
698 |
699 |
700 | def usage():
701 | """
702 | Print usage information on error
703 | """
704 | log(f"{sys.argv[0]} JELLYFIN_VERSION BUILD_TYPE [BUILD_ARCH] [BUILD_VERSION]")
705 | log(" JELLYFIN_VERSION: The Jellyfin version being built")
706 | log(" * Stable releases should be tag names with a 'v' e.g. v10.9.0")
707 | log(
708 | " * Unstable releases should be 'master' or a date-to-the-hour version e.g. 2024021600"
709 | )
710 | log(" BUILD_TYPE: The type of build to execute")
711 | log(f" * Valid options are: {', '.join(configurations.keys())}")
712 | log(" BUILD_ARCH: The CPU architecture of the build")
713 | log(" * Valid options are: [portable/docker only], amd64, arm64")
714 | log(" BUILD_VERSION: A valid OS distribution version (.deb build types only)")
715 |
716 |
717 | # Define a map of possible build functions from the YAML configuration
718 | function_definitions = {
719 | "build_package_deb": build_package_deb,
720 | "build_portable": build_portable,
721 | "build_linux": build_linux,
722 | "build_windows": build_windows,
723 | "build_macos": build_macos,
724 | "build_portable": build_portable,
725 | "build_docker": build_docker,
726 | "build_nuget": build_nuget,
727 | }
728 |
729 |
730 | parser = ArgumentParser(
731 | prog='build.py',
732 | description='Jellyfin build automator',
733 | epilog='See "README.md" and "build.yaml" for the full details of what this tool can build at this time.',
734 | )
735 |
736 | parser.add_argument('jellyfin_version', help='The output version')
737 | parser.add_argument('build_type', choices=configurations.keys(), help='The build platform')
738 | parser.add_argument('build_arch', default=None, nargs='?', help='The build architecture')
739 | parser.add_argument('build_version', default=None, nargs='?', help='The build release version [debian/ubuntu only]')
740 | parser.add_argument('--local', action='store_true', help='Local build, do not generate manifests or push them [docker only]')
741 | parser.add_argument('--debug', action='store_true', help='Debug build, set .NET to use Debug instead of Release')
742 |
743 | args = parser.parse_args()
744 |
745 | jellyfin_version = args.jellyfin_version
746 | build_type = args.build_type
747 | build_arch = args.build_arch
748 | build_version = args.build_version
749 |
750 | if build_type not in ["portable", "docker", "nuget"] and not build_arch:
751 | log(f"Error: You must specify an architecture for build platform {build_type}")
752 | exit(1)
753 |
754 | # Autocorrect "master" to a dated version string
755 | if jellyfin_version in ["auto", "master"]:
756 | jellyfin_version = datetime.now().strftime("%Y%m%d%H")
757 | log(f"NOTE: Autocorrecting 'master' version to {jellyfin_version}")
758 |
759 | # Launch the builder function
760 | function_definitions[configurations[build_type]["build_function"]](
761 | jellyfin_version, build_type, build_arch, build_version, local=args.local, debug=args.debug
762 | )
763 |
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # Build definitions for `build.py`
3 |
4 | # Upstream Framework versions
5 | # This section defines target commits after which a particular framework is required.
6 | # This is used by build.py to automatically determine which framework version to use
7 | # within the build containers based on whatever HEAD the project is at (from checkout.py).
8 | # Target commits should be a merge commit!
9 | # Target commits should be in order from oldest to newest!
10 | # HOW THIS WORKS:
11 | # For each submodule, and then for each upstream framework version ARG to Docker...
12 | # Provide a commit hash as a key, and a version as a value...
13 | # If the given commit is in the commit tree of the current HEAD of the repo...
14 | # Use the given version. Otherwise use the default.
15 | frameworks:
16 | jellyfin-web:
17 | NODEJS_VERSION:
18 | 6c0a64ef12b9eb40a7c4ee4b9d43d0a5f32f2287: 20 # Default
19 | jellyfin-server:
20 | DOTNET_VERSION:
21 | 6d1abf67c36379f0b061095619147a3691841e21: 8.0 # Default
22 | ceb850c77052c465af8422dcf152f1d1d1530457: 9.0
23 |
24 | # DEB packages
25 | debian:
26 | releases:
27 | bullseye: '11'
28 | bookworm: '12'
29 | trixie: '13'
30 | archmaps:
31 | amd64:
32 | PACKAGE_ARCH: amd64
33 | GCC_ARCH: x86-64
34 | arm64:
35 | PACKAGE_ARCH: arm64
36 | GCC_ARCH: aarch64
37 | cross-gcc:
38 | bullseye: '10'
39 | bookworm: '12'
40 | trixie: '14'
41 | build_function: build_package_deb
42 | dockerfile: debian/docker/Dockerfile
43 | imagename: jellyfin-builder-debian
44 | ubuntu:
45 | releases:
46 | focal: '20.04'
47 | jammy: '22.04'
48 | noble: '24.04'
49 | archmaps:
50 | amd64:
51 | PACKAGE_ARCH: amd64
52 | GCC_ARCH: x86-64
53 | arm64:
54 | PACKAGE_ARCH: arm64
55 | GCC_ARCH: aarch64
56 | cross-gcc:
57 | focal: '10'
58 | jammy: '12'
59 | noble: '13'
60 | build_function: build_package_deb
61 | dockerfile: debian/docker/Dockerfile
62 | imagename: jellyfin-builder-ubuntu
63 |
64 | # Portable archives
65 | linux:
66 | build_function: build_linux
67 | archivetypes: targz,tarxz
68 | archmaps:
69 | amd64:
70 | DOTNET_ARCH: x64
71 | PACKAGE_ARCH: amd64
72 | amd64-musl:
73 | DOTNET_ARCH: musl-x64
74 | PACKAGE_ARCH: amd64-musl
75 | arm64:
76 | DOTNET_ARCH: arm64
77 | PACKAGE_ARCH: arm64
78 | arm64-musl:
79 | DOTNET_ARCH: musl-arm64
80 | PACKAGE_ARCH: arm64-musl
81 | dockerfile: portable/Dockerfile
82 | imagename: jellyfin-builder-linux
83 | windows:
84 | build_function: build_windows
85 | archivetypes: zip
86 | archmaps:
87 | amd64:
88 | DOTNET_ARCH: x64
89 | PACKAGE_ARCH: amd64
90 | arm64:
91 | DOTNET_ARCH: arm64
92 | PACKAGE_ARCH: arm64
93 | dockerfile: portable/Dockerfile
94 | imagename: jellyfin-builder-windows
95 | macos:
96 | build_function: build_macos
97 | archivetypes: tarxz
98 | archmaps:
99 | amd64:
100 | DOTNET_ARCH: x64
101 | PACKAGE_ARCH: amd64
102 | arm64:
103 | DOTNET_ARCH: arm64
104 | PACKAGE_ARCH: arm64
105 | dockerfile: portable/Dockerfile
106 | imagename: jellyfin-builder-macos
107 | portable:
108 | build_function: build_portable
109 | archivetypes: targz,tarxz,zip
110 | dockerfile: portable/Dockerfile
111 | imagename: jellyfin-builder-portable
112 |
113 | # Docker images
114 | docker:
115 | build_function: build_docker
116 | archmaps:
117 | amd64:
118 | DOTNET_ARCH: x64
119 | IMAGE_ARCH: amd64
120 | PACKAGE_ARCH: amd64
121 | TARGET_ARCH: amd64
122 | arm64:
123 | DOTNET_ARCH: arm64
124 | IMAGE_ARCH: arm64v8
125 | PACKAGE_ARCH: arm64
126 | TARGET_ARCH: arm64/v8
127 | dockerfile: docker/Dockerfile
128 | imagename: jellyfin/jellyfin
129 |
130 | # Nuget packages
131 | nuget:
132 | build_function: build_nuget
133 | projects:
134 | - Jellyfin.Data/Jellyfin.Data.csproj
135 | - MediaBrowser.Common/MediaBrowser.Common.csproj
136 | - MediaBrowser.Controller/MediaBrowser.Controller.csproj
137 | - MediaBrowser.Model/MediaBrowser.Model.csproj
138 | - Emby.Naming/Emby.Naming.csproj
139 | - src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
140 | - src/Jellyfin.Database/Jellyfin.Database.Implementations/Jellyfin.Database.Implementations.csproj
141 | - src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj
142 | feed_urls:
143 | stable: https://api.nuget.org/v3/index.json
144 | unstable: https://nuget.pkg.github.com/jellyfin/index.json
145 |
--------------------------------------------------------------------------------
/checkout.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # checkout.py - Checkout submodules for a build (checkout a given tag or head)
4 | #
5 | # Part of the Jellyfin CI system
6 | ###############################################################################
7 |
8 | from datetime import datetime
9 | from subprocess import run, PIPE
10 | import sys
11 |
12 | from git import Repo
13 |
14 | try:
15 | target_release = sys.argv[1]
16 | except IndexError:
17 | target_release = "master"
18 |
19 | print(f"Preparing targets for {target_release}")
20 |
21 | # Determine top level directory of this repository ("jellyfin-packaging")
22 | revparse = run(["git", "rev-parse", "--show-toplevel"], stdout=PIPE)
23 | revparse_dir = revparse.stdout.decode().strip()
24 |
25 | # Prepare repo object for this repository
26 | this_repo = Repo(revparse_dir)
27 |
28 | # Update all the submodules
29 | while True:
30 | try:
31 | this_repo.submodule_update(init=True, recursive=True)
32 | break
33 | except Exception as e:
34 | print(e)
35 | pass
36 |
37 | # Prepare a dictionary form of the submodules so we can reference them by name
38 | submodules = dict()
39 | for submodule in this_repo.submodules:
40 | submodules[submodule.name] = submodule.module()
41 |
42 | # Validate that the provided tag is valid; if not, fall back to "master"
43 | if target_release != "master":
44 | if (
45 | target_release not in submodules["jellyfin-server"].tags
46 | or target_release not in submodules["jellyfin-web"].tags
47 | ):
48 | print(
49 | f"WARNING: Provided tag {target_release} is not a valid tag for both jellyfin-server and jellyfin-web; using master instead"
50 | )
51 | target_release = "master"
52 |
53 | for submodule in submodules.keys():
54 | if target_release == "master" or submodule == 'jellyfin-server-windows':
55 | target_head = "origin/master"
56 | else:
57 | target_head = f"refs/tags/{target_release}"
58 | # Checkout the given head and reset the working tree
59 | submodules[submodule].head.reference = target_head
60 | submodules[submodule].head.reset(index=True, working_tree=True)
61 | sha = submodules[submodule].head.object.hexsha
62 | author = submodules[submodule].head.object.author.name
63 | summary = submodules[submodule].head.object.summary
64 | date = datetime.fromtimestamp(submodules[submodule].head.object.committed_date)
65 | print(f"Submodule {submodule} now at {target_head} (\"{summary}\" commit {sha} by {author} @ {date})")
66 |
67 | print(f"Successfully checked out submodules to ref {target_release}")
68 |
--------------------------------------------------------------------------------
/debian/changelog.in:
--------------------------------------------------------------------------------
1 | jellyfin ({package_version}+{package_build}) unstable; urgency=medium
2 |
3 | * {release_comment}
4 |
5 | -- Jellyfin Packaging Team {release_date}
6 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 10
2 |
--------------------------------------------------------------------------------
/debian/conf/jellyfin:
--------------------------------------------------------------------------------
1 | # Jellyfin default configuration options
2 | # This is a POSIX shell fragment
3 |
4 | # Use this file to override the default configurations; add additional
5 | # options with JELLYFIN_ADD_OPTS.
6 |
7 | # Under systemd, use
8 | # /etc/systemd/system/jellyfin.service.d/jellyfin.service.conf
9 | # to override the user or this config file's location.
10 |
11 | #
12 | # General options
13 | #
14 |
15 | # Program directories
16 | JELLYFIN_DATA_DIR="/var/lib/jellyfin"
17 | JELLYFIN_CONFIG_DIR="/etc/jellyfin"
18 | JELLYFIN_LOG_DIR="/var/log/jellyfin"
19 | JELLYFIN_CACHE_DIR="/var/cache/jellyfin"
20 |
21 | # temporary directory path; used to construct paths for short-lived temporary files
22 | # the old default was $JELLYFIN_CACHE_DIR/temp; use that to restore previous behaviour,
23 | # though it will have an additional jellyfin/ subdir appended
24 | # otherwise, leave this commented
25 | #TMPDIR="/tmp"
26 |
27 | # web client path, installed by the jellyfin-web package
28 | JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin/web"
29 |
30 | # ffmpeg binary paths, overriding the system values
31 | JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg"
32 |
33 | # Use jemalloc2 for improved RAM usage (#11588)
34 | LD_PRELOAD=/usr/lib/jellyfin/libjemalloc.so
35 |
36 | # Disable glibc dynamic heap adjustment
37 | MALLOC_TRIM_THRESHOLD_=131072
38 |
39 | # [OPTIONAL] run Jellyfin as a headless service
40 | #JELLYFIN_SERVICE_OPT="--service"
41 |
42 | # [OPTIONAL] run Jellyfin without the web app
43 | #JELLYFIN_NOWEBAPP_OPT="--nowebclient"
44 |
45 | # Space to add additional command line options to jellyfin (for help see ~$ jellyfin --help)
46 | JELLYFIN_ADDITIONAL_OPTS=""
47 |
48 | # [OPTIONAL] run Jellyfin with ASP.NET Server Garbage Collection (uses more RAM and less CPU than Workstation GC)
49 | # 0 = Workstation
50 | # 1 = Server
51 | #COMPlus_gcServer=1
52 |
53 | # Uncomment this option to stop Jellyfin from auto-starting during package installation; for systemd, `mask` is preferred.
54 | #DISABLE_JELLYFIN_AUTOSTART="yes"
55 |
56 | #
57 | # SysV init/Upstart options
58 | #
59 | # Note: These options are ignored by systemd; use /etc/systemd/system/jellyfin.d overrides instead.
60 | #
61 |
62 | # Application username
63 | JELLYFIN_USER="jellyfin"
64 | # Full application command
65 | JELLYFIN_ARGS="$JELLYFIN_WEB_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLFIN_ADDITIONAL_OPTS --datadir $JELLYFIN_DATA_DIR --configdir $JELLYFIN_CONFIG_DIR --logdir $JELLYFIN_LOG_DIR --cachedir $JELLYFIN_CACHE_DIR"
66 |
--------------------------------------------------------------------------------
/debian/conf/jellyfin.init:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: Jellyfin Media Server
4 | # Required-Start: $local_fs $network
5 | # Required-Stop: $local_fs
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: Jellyfin Media Server
9 | # Description: Runs Jellyfin Server
10 | ### END INIT INFO
11 |
12 | set -e
13 |
14 | # Carry out specific functions when asked to by the system
15 |
16 | if test -f /etc/default/jellyfin; then
17 | . /etc/default/jellyfin
18 | fi
19 |
20 | . /lib/lsb/init-functions
21 |
22 | PIDFILE="/run/jellyfin.pid"
23 |
24 | case "$1" in
25 | start)
26 | log_daemon_msg "Starting Jellyfin Media Server" "jellyfin" || true
27 |
28 | if start-stop-daemon --start --quiet --oknodo --background --chdir $JELLYFIN_DATA_DIR --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then
29 | log_end_msg 0 || true
30 | else
31 | log_end_msg 1 || true
32 | fi
33 | ;;
34 |
35 | stop)
36 | log_daemon_msg "Stopping Jellyfin Media Server" "jellyfin" || true
37 | if start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile; then
38 | log_end_msg 0 || true
39 | else
40 | log_end_msg 1 || true
41 | fi
42 | ;;
43 |
44 | restart)
45 | log_daemon_msg "Restarting Jellyfin Media Server" "jellyfin" || true
46 | start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE --remove-pidfile
47 | if start-stop-daemon --start --quiet --oknodo --background --chdir $JELLYFIN_DATA_DIR --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then
48 | log_end_msg 0 || true
49 | else
50 | log_end_msg 1 || true
51 | fi
52 | ;;
53 |
54 | status)
55 | status_of_proc -p $PIDFILE /usr/bin/jellyfin jellyfin && exit 0 || exit $?
56 | ;;
57 |
58 | *)
59 | echo "Usage: $0 {start|stop|restart|status}"
60 | exit 1
61 | ;;
62 | esac
63 |
--------------------------------------------------------------------------------
/debian/conf/jellyfin.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description = Jellyfin Media Server
3 | After = network-online.target
4 |
5 | [Service]
6 | Type = simple
7 | EnvironmentFile = /etc/default/jellyfin
8 | User = jellyfin
9 | Group = jellyfin
10 | WorkingDirectory = /var/lib/jellyfin
11 | ExecStart = /usr/bin/jellyfin $JELLYFIN_WEB_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLYFIN_ADDITIONAL_OPTS
12 | Restart = on-failure
13 | TimeoutSec = 15
14 | SuccessExitStatus=0 143
15 |
16 | [Install]
17 | WantedBy = multi-user.target
18 |
--------------------------------------------------------------------------------
/debian/conf/jellyfin.service.conf:
--------------------------------------------------------------------------------
1 | # Jellyfin systemd configuration options
2 |
3 | # Use this file to override default systemd unit values
4 |
5 | [Service]
6 | # Alter the user/group that Jellyfin runs as
7 | #User = jellyfin
8 | #Group = jellyfin
9 |
10 | # Alter where environment variables are sourced from
11 | #EnvironmentFile = /etc/default/jellyfin
12 |
13 | # Alter the working directory (useful if changing the data path)
14 | #WorkingDirectory = /var/lib/jellyfin
15 |
16 | # Service hardening options
17 | # These optional options provide additional service hardening for Jellyfin
18 | # These are an ADVANCED FEATURE - if you enable these and encounter issues,
19 | # please disable them first and triage which if any are causing the trouble
20 | # before reporting any issues.
21 | #NoNewPrivileges=true
22 | #SystemCallArchitectures=native
23 | #RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
24 | #RestrictNamespaces=false
25 | #RestrictRealtime=true
26 | #RestrictSUIDSGID=true
27 | #ProtectControlGroups=false
28 | #ProtectHostname=true
29 | #ProtectKernelLogs=false
30 | #ProtectKernelModules=false
31 | #ProtectKernelTunables=false
32 | #LockPersonality=true
33 | #PrivateTmp=false
34 | #PrivateDevices=false
35 | #PrivateUsers=true
36 | #RemoveIPC=true
37 | #SystemCallFilter=~@clock
38 | #SystemCallFilter=~@aio
39 | #SystemCallFilter=~@chown
40 | #SystemCallFilter=~@cpu-emulation
41 | #SystemCallFilter=~@debug
42 | #SystemCallFilter=~@keyring
43 | #SystemCallFilter=~@memlock
44 | #SystemCallFilter=~@module
45 | #SystemCallFilter=~@mount
46 | #SystemCallFilter=~@obsolete
47 | #SystemCallFilter=~@privileged
48 | #SystemCallFilter=~@raw-io
49 | #SystemCallFilter=~@reboot
50 | #SystemCallFilter=~@setuid
51 | #SystemCallFilter=~@swap
52 | #SystemCallErrorNumber=EPERM
53 |
--------------------------------------------------------------------------------
/debian/conf/jellyfin.upstart:
--------------------------------------------------------------------------------
1 | description "jellyfin daemon"
2 |
3 | start on (local-filesystems and net-device-up IFACE!=lo)
4 | stop on runlevel [!2345]
5 |
6 | console log
7 | respawn
8 | respawn limit 10 5
9 |
10 | kill timeout 20
11 |
12 | script
13 | set -x
14 | echo "Starting $UPSTART_JOB"
15 |
16 | # Log file
17 | logger -t "$0" "DEBUG: `set`"
18 | . /etc/default/jellyfin
19 | exec su -u $JELLYFIN_USER -c /usr/bin/jellyfin $JELLYFIN_ARGS
20 | end script
21 |
--------------------------------------------------------------------------------
/debian/conf/logging.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "MinimumLevel": "Information",
4 | "WriteTo": [
5 | {
6 | "Name": "Console",
7 | "Args": {
8 | "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}"
9 | }
10 | },
11 | {
12 | "Name": "Async",
13 | "Args": {
14 | "configure": [
15 | {
16 | "Name": "File",
17 | "Args": {
18 | "path": "%JELLYFIN_LOG_DIR%//jellyfin.log",
19 | "fileSizeLimitBytes": 10485700,
20 | "rollOnFileSizeLimit": true,
21 | "retainedFileCountLimit": 10,
22 | "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}"
23 | }
24 | }
25 | ]
26 | }
27 | }
28 | ]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: jellyfin
2 | Section: misc
3 | Priority: optional
4 | Maintainer: Jellyfin Packaging Team
5 | Build-Depends: debhelper (>= 10),
6 | npm | nodejs,
7 | Standards-Version: 3.9.4
8 | Homepage: https://jellyfin.org
9 | Vcs-Browser: https://github.com/jellyfin
10 |
11 | Package: jellyfin
12 | Architecture: all
13 | Depends: jellyfin-server, jellyfin-web, jellyfin-ffmpeg7
14 | Description: Jellyfin is the Free Software Media System.
15 | This metapackage provides the core Jellyfin components from one name.
16 |
17 | Package: jellyfin-server
18 | Architecture: any
19 | Depends: libfontconfig1, libjemalloc2, adduser, ${icu:Depends}
20 | Recommends: jellyfin-web, sudo, jellyfin-ffmpeg7 | ffmpeg
21 | Description: Jellyfin is the Free Software Media System.
22 | This package provides the Jellyfin server backend and API.
23 |
24 | Package: jellyfin-web
25 | Architecture: all
26 | Recommends: jellyfin-server
27 | Description: Jellyfin is the Free Software Media System.
28 | This package provides the Jellyfin web client.
29 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: http://dep.debian.net/deps/dep5
2 | Upstream-Name: jellyfin
3 | Source: https://github.com/jellyfin
4 |
5 | Files: *
6 | Copyright: 2018-2024 Jellyfin Contributors
7 | License: GPL-U
8 |
9 | Files: debian/*
10 | Copyright: 2020-2024 Joshua M. Boniface
11 | License: GPL-3.0
12 |
13 | License: GPL-U
14 | NOTICE: The licensing situation of Jellyfin is complex. This code
15 | is considered "GPL unversioned" and resulting binaries are treated
16 | as GPL version 3 only for license compatibility reasons.
17 | .
18 | This package is free software; you can redistribute it and/or modify
19 | it under the terms of the GNU General Public License as published by
20 | the Free Software Foundation; version 3 of the License.
21 | .
22 | This package is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
26 | .
27 | You should have received a copy of the GNU General Public License
28 | along with this program. If not, see
29 | .
30 | On Debian systems, the complete text of the GNU General
31 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
32 |
33 | License: GPL-3.0
34 |
35 | This package is free software; you can redistribute it and/or modify
36 | it under the terms of the GNU General Public License as published by
37 | the Free Software Foundation; either version 3 of the License, or
38 | (at your option) any later version.
39 | .
40 | This package is distributed in the hope that it will be useful,
41 | but WITHOUT ANY WARRANTY; without even the implied warranty of
42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 | GNU General Public License for more details.
44 | .
45 | You should have received a copy of the GNU General Public License
46 | along with this program. If not, see
47 | .
48 | On Debian systems, the complete text of the GNU General
49 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
50 |
--------------------------------------------------------------------------------
/debian/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker build arguments
2 | ARG DOTNET_VERSION=8.0
3 | ARG NODEJS_VERSION=20
4 | # Default to 12, but set externally by the `build.py` script
5 | ARG GCC_VERSION=12
6 |
7 | ARG PACKAGE_TYPE
8 | ARG PACKAGE_VERSION
9 | ARG PACKAGE_ARCH
10 | ARG GCC_ARCH
11 |
12 | FROM ${PACKAGE_TYPE}:${PACKAGE_VERSION}
13 |
14 | ARG SOURCE_DIR=/jellyfin
15 | ARG ARTIFACT_DIR=/dist
16 | ARG DOTNET_VERSION
17 | ARG NODEJS_VERSION
18 | ARG PACKAGE_TYPE
19 | ARG PACKAGE_VERSION
20 | ARG PACKAGE_ARCH
21 | ARG GCC_ARCH
22 | ARG GCC_VERSION
23 |
24 | # Docker run environment
25 | ENV SOURCE_DIR=/jellyfin
26 | ENV ARTIFACT_DIR=/dist
27 | ENV DEB_BUILD_OPTIONS=noddebs
28 | ENV TYPE=${PACKAGE_TYPE}
29 | ENV VERSION=${PACKAGE_VERSION}
30 | ENV ARCH=${PACKAGE_ARCH}
31 | ENV GCC_ARCH=${GCC_ARCH}
32 |
33 | # Prepare Debian build environment
34 | RUN apt-get update -y \
35 | && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC \
36 | apt-get install --no-install-recommends -y \
37 | wget \
38 | debhelper \
39 | gnupg \
40 | devscripts \
41 | build-essential \
42 | git \
43 | mmv \
44 | lsb-release \
45 | libssl*.* \
46 | liblttng-ust* \
47 | libfontconfig*-dev \
48 | libcurl*openssl-dev \
49 | libfreetype*-dev \
50 | libssl-dev \
51 | libicu-dev \
52 | icu-devtools \
53 | && apt-get clean autoclean -y \
54 | && apt-get autoremove -y \
55 | && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* \
56 | && git config --global --add safe.directory /jellyfin/jellyfin-web
57 |
58 | # Prepare the cross-toolchain
59 | RUN if test "${PACKAGE_ARCH}" != "$( dpkg --print-architecture )"; then \
60 | if grep -q -i ubuntu /etc/os-release; then \
61 | rm /etc/apt/sources.list /etc/apt/sources.list.d/* || true \
62 | && export CODENAME="$( lsb_release -c -s )" \
63 | && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
64 | && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
65 | && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
66 | && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
67 | && echo "deb [arch=${PACKAGE_ARCH}] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/${PACKAGE_ARCH}.list \
68 | && echo "deb [arch=${PACKAGE_ARCH}] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/${PACKAGE_ARCH}.list \
69 | && echo "deb [arch=${PACKAGE_ARCH}] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/${PACKAGE_ARCH}.list \
70 | && echo "deb [arch=${PACKAGE_ARCH}] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/${PACKAGE_ARCH}.list \
71 | ; fi \
72 | && set -o xtrace \
73 | && dpkg --add-architecture ${PACKAGE_ARCH} \
74 | && apt-get update -y \
75 | && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC \
76 | apt-get -f install --no-install-recommends -o Dpkg::Options::="--force-overwrite" -y \
77 | bison \
78 | flex \
79 | libtool \
80 | gdb \
81 | sharutils \
82 | netbase \
83 | libmpc-dev \
84 | libmpfr-dev \
85 | libgmp-dev \
86 | systemtap-sdt-dev \
87 | autogen \
88 | expect \
89 | chrpath \
90 | zlib1g-dev \
91 | zip \
92 | binutils-aarch64-linux-gnu \
93 | binutils-arm-linux-gnueabihf \
94 | gcc-${GCC_VERSION}-source \
95 | gcc-${GCC_ARCH}-linux-gnu \
96 | binutils-${GCC_ARCH}-linux-gnu \
97 | libstdc++-${GCC_VERSION}-dev-${PACKAGE_ARCH}-cross \
98 | libc6-dev:${PACKAGE_ARCH} \
99 | linux-libc-dev:${PACKAGE_ARCH} \
100 | libgcc1:${PACKAGE_ARCH} \
101 | libstdc++-${GCC_VERSION}-dev:${PACKAGE_ARCH} \
102 | libfontconfig*-dev:${PACKAGE_ARCH} \
103 | libcurl*openssl-dev:${PACKAGE_ARCH} \
104 | libfreetype*-dev:${PACKAGE_ARCH} \
105 | libssl-dev:${PACKAGE_ARCH} \
106 | libssl[13].*:${PACKAGE_ARCH} \
107 | liblttng-ust*:${PACKAGE_ARCH} \
108 | && apt-get clean autoclean -y \
109 | && apt-get autoremove -y \
110 | && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* \
111 | ; fi
112 |
113 | # Prepare dotnet SDK
114 | RUN wget -O- https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel ${DOTNET_VERSION} --install-dir /usr/local/bin
115 |
116 | # Prepare nodejs
117 | RUN wget https://deb.nodesource.com/setup_${NODEJS_VERSION}.x -O nodejs-install.sh \
118 | && chmod +x ./nodejs-install.sh \
119 | && ./nodejs-install.sh \
120 | && apt-get install -y \
121 | nodejs
122 |
123 | # Clean up dependencies
124 | RUN apt-get clean autoclean -y \
125 | && apt-get autoremove -y \
126 | && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*
127 |
128 | # Link to build script
129 | RUN ln -sf ${SOURCE_DIR}/debian/docker/build.sh /build.sh
130 |
131 | VOLUME ${SOURCE_DIR}/
132 |
133 | VOLUME ${ARTIFACT_DIR}/
134 |
135 | ENTRYPOINT ["/build.sh"]
136 |
--------------------------------------------------------------------------------
/debian/docker/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #= Debian .deb builder
4 |
5 | set -o errexit
6 | set -o xtrace
7 |
8 | # Move to source directory
9 | pushd "${SOURCE_DIR}"
10 |
11 | # Build DEB
12 | if [[ ${ARCH} != $( dpkg --print-architecture ) ]]; then
13 | export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
14 | export CONFIG_CROSS="-a ${ARCH}"
15 | fi
16 | dpkg-buildpackage -us -uc ${CONFIG_CROSS} --pre-clean --post-clean
17 |
18 | mkdir -p "${ARTIFACT_DIR}/"
19 | mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
20 |
21 | popd
22 |
--------------------------------------------------------------------------------
/debian/gbp.conf:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | pristine-tar = False
3 | cleaner = fakeroot debian/rules clean
4 |
5 | [import-orig]
6 | filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ]
7 |
--------------------------------------------------------------------------------
/debian/jellyfin-server.conffiles:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jellyfin/jellyfin-packaging/0878e2583bc2893542c150feb9dc3adb9c1d774f/debian/jellyfin-server.conffiles
--------------------------------------------------------------------------------
/debian/jellyfin-server.install:
--------------------------------------------------------------------------------
1 | build/usr/lib/jellyfin usr/lib/
2 | debian/conf/jellyfin etc/default/
3 | debian/conf/logging.json etc/jellyfin/
4 | debian/conf/jellyfin.service lib/systemd/system/
5 | debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/
6 |
--------------------------------------------------------------------------------
/debian/jellyfin-server.postinst:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | NAME=jellyfin
5 | DEFAULT_FILE=/etc/default/${NAME}
6 |
7 | # Source Jellyfin default configuration
8 | if [[ -f $DEFAULT_FILE ]]; then
9 | . $DEFAULT_FILE
10 | fi
11 |
12 | JELLYFIN_USER=${JELLYFIN_USER:-jellyfin}
13 | RENDER_GROUP=${RENDER_GROUP:-render}
14 | VIDEO_GROUP=${VIDEO_GROUP:-video}
15 |
16 | # Data directories for program data (cache, db), configs, and logs
17 | PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
18 | CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
19 | LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
20 | CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
21 |
22 | case "$1" in
23 | configure)
24 | # create jellyfin group if it does not exist
25 | if [[ -z "$(getent group ${JELLYFIN_USER})" ]]; then
26 | addgroup --quiet --system ${JELLYFIN_USER} > /dev/null 2>&1
27 | fi
28 | # create jellyfin user if it does not exist
29 | if [[ -z "$(getent passwd ${JELLYFIN_USER})" ]]; then
30 | adduser --system --ingroup ${JELLYFIN_USER} --shell /bin/false ${JELLYFIN_USER} --no-create-home --home ${PROGRAMDATA} \
31 | --gecos "Jellyfin default user" > /dev/null 2>&1
32 | fi
33 | # add jellyfin to the render group for hwa
34 | if [[ ! -z "$(getent group ${RENDER_GROUP})" ]]; then
35 | usermod -aG ${RENDER_GROUP} ${JELLYFIN_USER} > /dev/null 2>&1
36 | fi
37 | # add jellyfin to the video group for hwa
38 | if [[ ! -z "$(getent group ${VIDEO_GROUP})" ]]; then
39 | usermod -aG ${VIDEO_GROUP} ${JELLYFIN_USER} > /dev/null 2>&1
40 | fi
41 |
42 | # Ensure directories exist and, on initial creation, are chowned to the correct user(s)
43 | for DIRECTORY in $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA; do
44 | if [[ ! -d $DIRECTORY ]]; then
45 | mkdir -p $DIRECTORY
46 | fi
47 | if [[ $(stat -c '%u' $DIRECTORY) -eq 0 ]]; then
48 | chown -R ${JELLYFIN_USER}:adm $DIRECTORY
49 | chmod 0750 $DIRECTORY
50 | fi
51 | done
52 |
53 | # Refresh ldconfig cache and get libjemalloc.so path
54 | ldconfig &>/dev/null
55 | LIBJEMALLOC_PATH="$( ldconfig -p | grep libjemalloc | awk '{print $NF}' | head -n1 )"
56 | if [[ -z ${LIBJEMALLOC_PATH} ]]; then
57 | # Try to find the path with `find` instead of `ldconfig`; hackier, but a good fallback
58 | LIBJEMALLOC_PATH="$( find /usr/lib -type f -name "libjemalloc.so*" | head -n1 )"
59 | fi
60 | # Install libjemalloc.so symlink into /usr/lib/jellyfin
61 | if [[ -n ${LIBJEMALLOC_PATH} && -f ${LIBJEMALLOC_PATH} ]]; then
62 | ln -sf ${LIBJEMALLOC_PATH} /usr/lib/jellyfin/libjemalloc.so
63 | else
64 | echo "failed to find libjemalloc on system; ensure the 'libjemalloc2' package is [being] installed"
65 | exit 1
66 | fi
67 |
68 | # Install jellyfin symlink into /usr/bin
69 | ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin
70 |
71 | ;;
72 | abort-upgrade|abort-remove|abort-deconfigure)
73 | ;;
74 | *)
75 | echo "postinst called with unknown argument \`$1'" >&2
76 | exit 1
77 | ;;
78 | esac
79 |
80 | #DEBHELPER
81 |
82 | if [[ -x "/usr/bin/deb-systemd-helper" ]]; then
83 | # was-enabled defaults to true, so new installations run enable.
84 | if deb-systemd-helper --quiet was-enabled jellyfin.service; then
85 | # Enables the unit on first installation, creates new
86 | # symlinks on upgrades if the unit file has changed.
87 | deb-systemd-helper enable jellyfin.service >/dev/null || true
88 | else
89 | # Update the statefile to add new symlinks (if any), which need to be
90 | # cleaned up on purge. Also remove old symlinks.
91 | deb-systemd-helper update-state jellyfin.service >/dev/null || true
92 | fi
93 | fi
94 |
95 | if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then
96 | if [[ -d "/run/systemd/system" ]]; then
97 | systemctl --system daemon-reload >/dev/null || true
98 | if [[ -z ${DISABLE_JELLYFIN_AUTOSTART} ]]; then
99 | deb-systemd-invoke start jellyfin >/dev/null || true
100 | fi
101 | elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then
102 | if [[ -z ${DISABLE_JELLYFIN_AUTOSTART} ]]; then
103 | update-rc.d jellyfin defaults >/dev/null
104 | invoke-rc.d jellyfin start || exit $?
105 | fi
106 | fi
107 | fi
108 | exit 0
109 |
--------------------------------------------------------------------------------
/debian/jellyfin-server.postrm:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | NAME=jellyfin
5 | DEFAULT_FILE=/etc/default/${NAME}
6 |
7 | # Source Jellyfin default configuration
8 | if [[ -f $DEFAULT_FILE ]]; then
9 | . $DEFAULT_FILE
10 | fi
11 |
12 | # Data directories for program data (cache, db), configs, and logs
13 | PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
14 | CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
15 | LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
16 | CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
17 |
18 | # In case this system is running systemd, we make systemd reload the unit files
19 | # to pick up changes.
20 | if [[ -d /run/systemd/system ]] ; then
21 | systemctl --system daemon-reload >/dev/null || true
22 | fi
23 |
24 | case "$1" in
25 | purge)
26 | echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true
27 |
28 | if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then
29 | update-rc.d jellyfin remove >/dev/null 2>&1 || true
30 | fi
31 |
32 | if [[ -x "/usr/bin/deb-systemd-helper" ]]; then
33 | deb-systemd-helper purge jellyfin.service >/dev/null
34 | fi
35 |
36 | # Remove user and group
37 | userdel jellyfin > /dev/null 2>&1 || true
38 | delgroup --quiet jellyfin > /dev/null 2>&1 || true
39 | # Remove config dir
40 | if [[ -d $CONFIGDATA ]]; then
41 | rm -rf $CONFIGDATA || true
42 | fi
43 | # Remove log dir
44 | if [[ -d $LOGDATA ]]; then
45 | rm -rf $LOGDATA || true
46 | fi
47 | # Remove cache dir
48 | if [[ -d $CACHEDATA ]]; then
49 | rm -rf $CACHEDATA || true
50 | fi
51 | # Remove program data dir
52 | if [[ -d $PROGRAMDATA ]]; then
53 | rm -rf $PROGRAMDATA || true
54 | fi
55 | # Remove binary symlink
56 | rm -f /usr/bin/jellyfin
57 | # Remove sudoers config
58 | [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers
59 | # Remove anything at the default locations; catches situations where the user moved the defaults
60 | [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin || true
61 | [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin || true
62 | [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin || true
63 | [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin || true
64 | ;;
65 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
66 | ;;
67 | *)
68 | echo "postrm called with unknown argument \`$1'" >&2
69 | exit 1
70 | ;;
71 | esac
72 |
73 | #DEBHELPER#
74 |
75 | exit 0
76 |
--------------------------------------------------------------------------------
/debian/jellyfin-server.preinst:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | NAME=jellyfin
5 | DEFAULT_FILE=/etc/default/${NAME}
6 |
7 | # Source Jellyfin default configuration
8 | if [[ -f $DEFAULT_FILE ]]; then
9 | . $DEFAULT_FILE
10 | fi
11 |
12 | # Data directories for program data (cache, db), configs, and logs
13 | PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
14 | CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
15 | LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
16 | CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
17 |
18 | # In case this system is running systemd, we make systemd reload the unit files
19 | # to pick up changes.
20 | if [[ -d /run/systemd/system ]] ; then
21 | systemctl --system daemon-reload >/dev/null || true
22 | fi
23 |
24 | case "$1" in
25 | install|upgrade)
26 | # try graceful termination;
27 | if [[ -d /run/systemd/system ]]; then
28 | deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true
29 | elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then
30 | invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true
31 | fi
32 | # try and figure out if jellyfin is running
33 | PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit)
34 | [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE})
35 | # if its running, let's stop it
36 | if [[ -n "$JELLYFIN_PID" ]]; then
37 | echo "Stopping Jellyfin!"
38 | # if jellyfin is still running, kill it
39 | if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then
40 | CPIDS=$(pgrep -P $JELLYFIN_PID)
41 | sleep 2 && kill -KILL $CPIDS
42 | kill -TERM $CPIDS > /dev/null 2>&1
43 | fi
44 | sleep 1
45 | # if it's still running, show error
46 | if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then
47 | echo "Could not successfully stop Jellyfin, please do so before installing."
48 | exit 1
49 | else
50 | [[ -f $PIDFILE ]] && rm $PIDFILE
51 | fi
52 | fi
53 | ;;
54 | abort-upgrade)
55 | ;;
56 | *)
57 | echo "preinst called with unknown argument \`$1'" >&2
58 | exit 1
59 | ;;
60 | esac
61 | #DEBHELPER#
62 |
63 | exit 0
64 |
--------------------------------------------------------------------------------
/debian/jellyfin-server.prerm:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | NAME=jellyfin
5 | DEFAULT_FILE=/etc/default/${NAME}
6 |
7 | # Source Jellyfin default configuration
8 | if [[ -f $DEFAULT_FILE ]]; then
9 | . $DEFAULT_FILE
10 | fi
11 |
12 | # Data directories for program data (cache, db), configs, and logs
13 | PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME}
14 | CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME}
15 | LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME}
16 | CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME}
17 |
18 | case "$1" in
19 | remove|upgrade|deconfigure)
20 | echo "Stopping Jellyfin!"
21 | # try graceful termination;
22 | if [[ -d /run/systemd/system ]]; then
23 | deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true
24 | elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then
25 | invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true
26 | fi
27 | # Ensure that it is shutdown
28 | PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit)
29 | [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE})
30 | # if its running, let's stop it
31 | if [[ -n "$JELLYFIN_PID" ]]; then
32 | # if jellyfin is still running, kill it
33 | if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then
34 | CPIDS=$(pgrep -P $JELLYFIN_PID)
35 | sleep 2 && kill -KILL $CPIDS
36 | kill -TERM $CPIDS > /dev/null 2>&1
37 | fi
38 | sleep 1
39 | # if it's still running, show error
40 | if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then
41 | echo "Could not successfully stop Jellyfin, please do so before uninstalling."
42 | exit 1
43 | else
44 | [[ -f $PIDFILE ]] && rm $PIDFILE
45 | fi
46 | fi
47 | if [[ -f /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so ]]; then
48 | rm /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so
49 | fi
50 | ;;
51 | failed-upgrade)
52 | ;;
53 | *)
54 | echo "prerm called with unknown argument \`$1'" >&2
55 | exit 1
56 | ;;
57 | esac
58 |
59 | #DEBHELPER#
60 |
61 | exit 0
62 |
--------------------------------------------------------------------------------
/debian/jellyfin-web.conffiles:
--------------------------------------------------------------------------------
1 | /usr/share/jellyfin/web/config.json
2 |
--------------------------------------------------------------------------------
/debian/jellyfin-web.install:
--------------------------------------------------------------------------------
1 | build/web usr/share/jellyfin/
2 |
--------------------------------------------------------------------------------
/debian/po/POTFILES.in:
--------------------------------------------------------------------------------
1 | [type: gettext/rfc822deb] templates
2 |
--------------------------------------------------------------------------------
/debian/po/templates.pot:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: jellyfin-server\n"
10 | "Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n"
11 | "POT-Creation-Date: 2015-06-12 20:51-0600\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=CHARSET\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #. Type: note
21 | #. Description
22 | #: ../templates:1001
23 | msgid "Jellyfin permission info:"
24 | msgstr ""
25 |
26 | #. Type: note
27 | #. Description
28 | #: ../templates:1001
29 | msgid ""
30 | "Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the "
31 | "user jellyfin has read and write access to any folders you wish to add to your "
32 | "library. Otherwise please run jellyfin under a different user."
33 | msgstr ""
34 |
35 | #. Type: string
36 | #. Description
37 | #: ../templates:2001
38 | msgid "Username to run Jellyfin as:"
39 | msgstr ""
40 |
41 | #. Type: string
42 | #. Description
43 | #: ../templates:2001
44 | msgid "The user that jellyfin will run as."
45 | msgstr ""
46 |
47 | #. Type: note
48 | #. Description
49 | #: ../templates:3001
50 | msgid "Jellyfin still running"
51 | msgstr ""
52 |
53 | #. Type: note
54 | #. Description
55 | #: ../templates:3001
56 | msgid "Jellyfin is currently running. Please close it and try again."
57 | msgstr ""
58 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #! /usr/bin/make -f
2 | CONFIG ?= Release
3 | TERM := xterm
4 | SHELL := /bin/bash
5 |
6 | HOST_ARCH := $(shell arch)
7 | BUILD_ARCH := ${DEB_HOST_MULTIARCH}
8 | ifeq ($(HOST_ARCH),x86_64)
9 | # Building AMD64
10 | DOTNETRUNTIME := linux-x64
11 | ifeq ($(BUILD_ARCH),arm-linux-gnueabihf)
12 | # Cross-building ARM on AMD64
13 | DOTNETRUNTIME := linux-arm
14 | endif
15 | ifeq ($(BUILD_ARCH),aarch64-linux-gnu)
16 | # Cross-building ARM on AMD64
17 | DOTNETRUNTIME := linux-arm64
18 | endif
19 | endif
20 | ifeq ($(HOST_ARCH),armv7l)
21 | # Building ARM
22 | DOTNETRUNTIME := linux-arm
23 | endif
24 | ifeq ($(HOST_ARCH),arm64)
25 | # Building ARM
26 | DOTNETRUNTIME := linux-arm64
27 | endif
28 |
29 | export DH_VERBOSE=1
30 | export DOTNET_CLI_TELEMETRY_OPTOUT=1
31 | export COMPlus_LTTng=0
32 | export COMPlus_EnableDiagnostics=0
33 |
34 | %:
35 | dh $@
36 |
37 | override_dh_gencontrol:
38 | ICU_MAJOR="$$(apt-cache policy libicu-dev 2>/dev/null \
39 | | sed -n 's/ Candidate: \([0-9]\+\)\..*/\1/p' | head -n1)" && \
40 | dh_gencontrol -- -Vicu:Depends="libicu$$ICU_MAJOR"
41 |
42 | override_dh_auto_test:
43 |
44 | override_dh_clistrip:
45 |
46 | override_dh_auto_build:
47 | cd jellyfin-server && dotnet publish -maxcpucount:1 --configuration $(CONFIG) \
48 | --output='$(CURDIR)/build/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
49 | -p:DebugSymbols=false -p:DebugType=none Jellyfin.Server
50 | rm -f $(CURDIR)/build/usr/lib/jellyfin/bin/libcoreclrtraceptprovider.so
51 | cd jellyfin-web && npm ci --no-audit --unsafe-perm && \
52 | npm run build:production && mv $(CURDIR)/jellyfin-web/dist $(CURDIR)/build/web
53 |
54 | override_dh_auto_clean:
55 | cd jellyfin-server && dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true
56 | cd jellyfin-server && find . -type d \( -name bin -o -name obj \) -exec rm -r {} \; || true
57 | test -d $(CURDIR)/jellyfin-web/dist && \
58 | rm -rf '$(CURDIR)/jellyfin-web/dist' || true
59 | test -d $(CURDIR)/jellyfin-web/web && \
60 | rm -rf '$(CURDIR)/jellyfin-web/web' || true
61 | test -d $(CURDIR)/jellyfin-web/node_modules && \
62 | rm -rf '$(CURDIR)/jellyfin-web/node_modules' || true
63 | rm -rf '$(CURDIR)/build'
64 |
65 | override_dh_installinit:
66 | dh_installinit --name=jellyfin
67 |
68 | override_dh_shdeplibs:
69 | dh_shlibdeps -- --ignore-missing-info
70 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 1.0
2 |
--------------------------------------------------------------------------------
/debian/source/options:
--------------------------------------------------------------------------------
1 | tar-ignore='.git*'
2 | tar-ignore='**/.git'
3 | tar-ignore='**/.hg'
4 | tar-ignore='**/.vs'
5 | tar-ignore='**/.vscode'
6 | tar-ignore='**/obj'
7 | tar-ignore='**/bin'
8 | tar-ignore = "out/"
9 | tar-ignore = "fedora/"
10 | tar-ignore = "docker/"
11 | tar-ignore = "*.py"
12 | tar-ignore = "*.md"
13 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker build arguments
2 | ARG DOTNET_VERSION=8.0
3 | ARG NODEJS_VERSION=20
4 |
5 | # Combined image version (Debian)
6 | ARG OS_VERSION=trixie
7 |
8 | # Jellyfin FFMPEG package
9 | ARG FFMPEG_PACKAGE=jellyfin-ffmpeg7
10 |
11 | # https://github.com/intel/compute-runtime/releases
12 | ARG GMMLIB_VER=22.8.1
13 | # >= Gen12 graphics (current)
14 | ARG IGC2_VER=2.18.5
15 | ARG IGC2_BUILD=19820
16 | ARG NEO_VER=25.35.35096.9
17 | # <= Gen11 graphics (legacy)
18 | ARG IGC1_LEGACY_VER=1.0.17537.24
19 | ARG NEO_LEGACY_VER=24.35.30872.36
20 |
21 | # https://github.com/tsukumijima/libmali-rockchip
22 | ARG MALI_PKG_VER=1.9-1_arm64
23 | ARG MALI_PKG_TAG=v1.9-1-2131373
24 | ARG MALI_PKG_CFG=valhall-g610-g24p0-gbm
25 |
26 | # Debian architecture (amd64, arm64, armhf), set by build script
27 | ARG PACKAGE_ARCH
28 | # Dotnet architeture (x64, arm64, arm), set by build script
29 | ARG DOTNET_ARCH
30 | # Base Image architecture (amd64, arm64v8, arm32v7), set by build script
31 | ARG IMAGE_ARCH
32 | # Target platform architecture (amd64, arm64/v8, arm/v7), set by build script
33 | ARG TARGET_ARCH
34 |
35 | # Jellyfin version
36 | ARG JELLYFIN_VERSION
37 |
38 | #
39 | # Build the web artifacts
40 | #
41 | FROM node:${NODEJS_VERSION}-alpine AS web
42 |
43 | ARG SOURCE_DIR=/src
44 | ARG ARTIFACT_DIR=/web
45 |
46 | ARG JELLYFIN_VERSION
47 | ENV JELLYFIN_VERSION=${JELLYFIN_VERSION}
48 |
49 | RUN apk add \
50 | autoconf \
51 | g++ \
52 | make \
53 | libpng-dev \
54 | gifsicle \
55 | alpine-sdk \
56 | automake \
57 | libtool \
58 | gcc \
59 | musl-dev \
60 | nasm \
61 | python3 \
62 | git \
63 | && git config --global --add safe.directory /jellyfin/jellyfin-web
64 |
65 | WORKDIR ${SOURCE_DIR}
66 | COPY jellyfin-web .
67 |
68 | RUN npm ci --no-audit --unsafe-perm \
69 | && npm run build:production \
70 | && mv dist ${ARTIFACT_DIR}
71 |
72 | #
73 | # Build the server artifacts
74 | #
75 | FROM debian:${OS_VERSION}-slim AS server
76 |
77 | ARG DOTNET_ARCH
78 | ARG DOTNET_VERSION
79 |
80 | ARG SOURCE_DIR=/src
81 | ARG ARTIFACT_DIR=/server
82 |
83 | ARG CONFIG=Release
84 | ENV CONFIG=${CONFIG}
85 |
86 | WORKDIR ${SOURCE_DIR}
87 | COPY jellyfin-server .
88 | ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
89 |
90 | RUN apt-get update \
91 | && apt-get install --no-install-recommends --no-install-suggests --yes \
92 | curl \
93 | ca-certificates \
94 | libicu76 \
95 | && curl -fsSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel ${DOTNET_VERSION} --install-dir /usr/local/bin
96 |
97 | RUN dotnet publish Jellyfin.Server --arch ${DOTNET_ARCH} --configuration ${CONFIG} \
98 | --output="${ARTIFACT_DIR}" --self-contained \
99 | -p:DebugSymbols=false -p:DebugType=none
100 |
101 | #
102 | # Build the final combined image
103 | #
104 | FROM --platform=linux/${TARGET_ARCH} ${IMAGE_ARCH}/debian:${OS_VERSION}-slim AS combined
105 |
106 | ARG OS_VERSION
107 | ARG FFMPEG_PACKAGE
108 |
109 | ARG GMMLIB_VER
110 | ARG IGC2_VER
111 | ARG IGC2_BUILD
112 | ARG NEO_VER
113 | ARG IGC1_LEGACY_VER
114 | ARG NEO_LEGACY_VER
115 |
116 | ARG MALI_PKG_VER
117 | ARG MALI_PKG_TAG
118 | ARG MALI_PKG_CFG
119 |
120 | ARG PACKAGE_ARCH
121 |
122 | # Set the health URL
123 | ENV HEALTHCHECK_URL=http://localhost:8096/health
124 |
125 | # Default environment variables for the Jellyfin invocation
126 | ENV DEBIAN_FRONTEND="noninteractive" \
127 | LC_ALL="en_US.UTF-8" \
128 | LANG="en_US.UTF-8" \
129 | LANGUAGE="en_US:en" \
130 | JELLYFIN_DATA_DIR="/config" \
131 | JELLYFIN_CACHE_DIR="/cache" \
132 | JELLYFIN_CONFIG_DIR="/config/config" \
133 | JELLYFIN_LOG_DIR="/config/log" \
134 | JELLYFIN_WEB_DIR="/jellyfin/jellyfin-web" \
135 | JELLYFIN_FFMPEG="/usr/lib/jellyfin-ffmpeg/ffmpeg"
136 |
137 | # required for fontconfig cache
138 | ENV XDG_CACHE_HOME=${JELLYFIN_CACHE_DIR}
139 |
140 | # https://github.com/dlemstra/Magick.NET/issues/707#issuecomment-785351620
141 | ENV MALLOC_TRIM_THRESHOLD_=131072
142 |
143 | # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
144 | ENV NVIDIA_VISIBLE_DEVICES="all"
145 | ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
146 |
147 | # Install dependencies:
148 | RUN apt-get update \
149 | && apt-get install --no-install-recommends --no-install-suggests --yes \
150 | ca-certificates \
151 | gnupg \
152 | curl \
153 | && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key \
154 | | gpg --dearmor -o /etc/apt/keyrings/jellyfin.gpg \
155 | && cat < /etc/apt/sources.list.d/jellyfin.sources
156 | Types: deb
157 | URIs: https://repo.jellyfin.org/debian
158 | Suites: ${OS_VERSION}
159 | Components: main
160 | Architectures: ${PACKAGE_ARCH}
161 | Signed-By: /etc/apt/keyrings/jellyfin.gpg
162 | EOF
163 |
164 | RUN apt-get update \
165 | && apt-get install --no-install-recommends --no-install-suggests --yes \
166 | ${FFMPEG_PACKAGE} \
167 | openssl \
168 | locales \
169 | libicu76 \
170 | libfontconfig1 \
171 | libfreetype6 \
172 | libjemalloc2 \
173 | && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen \
174 | && apt-get remove gnupg apt-transport-https --yes \
175 | && apt-get clean autoclean --yes \
176 | && apt-get autoremove --yes \
177 | && rm -rf /var/cache/apt/archives* /var/lib/apt/lists/*
178 |
179 | # Intel OpenCL Tone mapping dependencies:
180 | RUN if test "${PACKAGE_ARCH}" = "amd64"; then \
181 | mkdir intel-compute-runtime \
182 | && cd intel-compute-runtime \
183 | && curl -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VER}/libigdgmm12_${GMMLIB_VER}_amd64.deb \
184 | -LO https://github.com/intel/intel-graphics-compiler/releases/download/v${IGC2_VER}/intel-igc-core-2_${IGC2_VER}+${IGC2_BUILD}_amd64.deb \
185 | -LO https://github.com/intel/intel-graphics-compiler/releases/download/v${IGC2_VER}/intel-igc-opencl-2_${IGC2_VER}+${IGC2_BUILD}_amd64.deb \
186 | -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VER}/intel-opencl-icd_${NEO_VER}-0_amd64.deb \
187 | -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC1_LEGACY_VER}/intel-igc-core_${IGC1_LEGACY_VER}_amd64.deb \
188 | -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC1_LEGACY_VER}/intel-igc-opencl_${IGC1_LEGACY_VER}_amd64.deb \
189 | -LO https://github.com/intel/compute-runtime/releases/download/${NEO_LEGACY_VER}/intel-opencl-icd-legacy1_${NEO_LEGACY_VER}_amd64.deb \
190 | && apt-get install --no-install-recommends --no-install-suggests -f -y ./*.deb \
191 | && cd .. \
192 | && rm -rf intel-compute-runtime \
193 | ; fi \
194 | && apt-get clean autoclean --yes \
195 | && apt-get autoremove --yes \
196 | && rm -rf /var/cache/apt/archives* /var/lib/apt/lists/*
197 |
198 | # Rockchip RK3588 libmali OpenCL dependencies:
199 | RUN if test "${PACKAGE_ARCH}" = "arm64"; then \
200 | mkdir libmali-rockchip \
201 | && cd libmali-rockchip \
202 | && curl -LO https://github.com/tsukumijima/libmali-rockchip/releases/download/${MALI_PKG_TAG}/libmali-${MALI_PKG_CFG}_${MALI_PKG_VER}.deb \
203 | && apt-get install --no-install-recommends --no-install-suggests -f -y ./*.deb \
204 | && cd .. \
205 | && rm -rf libmali-rockchip \
206 | ; fi \
207 | && apt-get clean autoclean --yes \
208 | && apt-get autoremove --yes \
209 | && rm -rf /var/cache/apt/archives* /var/lib/apt/lists/*
210 |
211 | # Add fonts for east asian languages rendering
212 | RUN apt update --yes \
213 | && apt install --no-install-recommends --no-install-suggests --yes \
214 | fonts-wqy-zenhei \
215 | fonts-wqy-microhei \
216 | fonts-arphic-ukai \
217 | fonts-arphic-uming \
218 | fonts-noto-cjk \
219 | fonts-ipafont-mincho \
220 | fonts-ipafont-gothic \
221 | fonts-unfonts-core \
222 | && apt clean autoclean --yes \
223 | && apt autoremove --yes \
224 | && rm -rf /var/cache/apt/archives* /var/lib/apt/lists/*
225 |
226 | # Setup jemalloc: link the library to a path owned by us to handle arch specific library paths
227 | RUN mkdir -p /usr/lib/jellyfin \
228 | && JEMALLOC_LINKED=0 \
229 | && if [ "${PACKAGE_ARCH}" = "amd64" ]; then \
230 | if [ -f "/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" ]; then \
231 | ln -s /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 /usr/lib/jellyfin/libjemalloc.so.2 && JEMALLOC_LINKED=1; \
232 | fi; \
233 | elif [ "${PACKAGE_ARCH}" = "arm64" ]; then \
234 | if [ -f "/usr/lib/aarch64-linux-gnu/libjemalloc.so.2" ]; then \
235 | ln -s /usr/lib/aarch64-linux-gnu/libjemalloc.so.2 /usr/lib/jellyfin/libjemalloc.so.2 && JEMALLOC_LINKED=1; \
236 | fi; \
237 | fi \
238 | && if [ "$JEMALLOC_LINKED" -eq 1 ]; then \
239 | echo "jemalloc library linked successfully for ${PACKAGE_ARCH}." ; \
240 | else \
241 | echo "WARNING: jemalloc library .so file not found for PACKAGE_ARCH ${PACKAGE_ARCH}. Either the arch is not supported or copy failed. LD_PRELOAD might not work as expected." >&2; \
242 | fi
243 |
244 | # Set LD_PRELOAD to use the linked jemalloc library
245 | ENV LD_PRELOAD=/usr/lib/jellyfin/libjemalloc.so.2
246 |
247 | RUN mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
248 | && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR}
249 |
250 | COPY --from=server /server /jellyfin
251 | COPY --from=web /web /jellyfin/jellyfin-web
252 | ARG JELLYFIN_VERSION
253 | LABEL "org.opencontainers.image.source"="https://github.com/jellyfin/jellyfin-packaging"
254 | LABEL "org.opencontainers.image.title"="Jellyfin"
255 | LABEL "org.opencontainers.image.description"="The Free Software Media System"
256 | LABEL "org.opencontainers.image.documentation"="https://jellyfin.org/docs/"
257 | LABEL "org.opencontainers.image.version"="${JELLYFIN_VERSION}"
258 | LABEL "org.opencontainers.image.url"="https://jellyfin.org"
259 |
260 | EXPOSE 8096
261 | VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR}
262 | ENTRYPOINT ["/jellyfin/jellyfin"]
263 |
264 | HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
265 | CMD curl --noproxy 'localhost' -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
266 |
--------------------------------------------------------------------------------
/portable/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker build arguments
2 | ARG DOTNET_VERSION=8.0
3 | ARG NODEJS_VERSION=20
4 |
5 | ARG PACKAGE_TYPE=debian
6 | ARG PACKAGE_VERSION=12
7 |
8 | FROM ${PACKAGE_TYPE}:${PACKAGE_VERSION}
9 |
10 | ARG SOURCE_DIR=/jellyfin
11 | ARG ARTIFACT_DIR=/dist
12 | ARG DOTNET_VERSION
13 | ARG NODEJS_VERSION
14 | ARG PACKAGE_TYPE
15 | ARG PACKAGE_VERSION
16 |
17 | # Docker run environment
18 | ENV SOURCE_DIR=/jellyfin
19 | ENV ARTIFACT_DIR=/dist
20 | ENV TYPE=${PACKAGE_TYPE}
21 | ENV VERSION=${PACKAGE_VERSION}
22 | ENV ARCHIVE_TYPES=targz
23 |
24 | # Prepare Debian build environment
25 | RUN apt-get update -y \
26 | && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC \
27 | apt-get install --no-install-recommends -y \
28 | wget \
29 | unzip \
30 | git \
31 | debhelper \
32 | gnupg \
33 | devscripts \
34 | build-essential \
35 | git \
36 | mmv \
37 | lsb-release\
38 | zip \
39 | libssl*.* \
40 | liblttng-ust*\
41 | libssl-dev \
42 | libicu-dev \
43 | icu-devtools \
44 | libfontconfig*-dev \
45 | libcurl*openssl-dev \
46 | libfreetype*-dev \
47 | && apt-get clean autoclean -y \
48 | && apt-get autoremove -y \
49 | && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* \
50 | && git config --global --add safe.directory /jellyfin/jellyfin-web
51 |
52 | # Prepare dotnet SDK
53 | RUN wget https://packages.microsoft.com/config/${PACKAGE_TYPE}/${PACKAGE_VERSION}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
54 | && dpkg -i packages-microsoft-prod.deb \
55 | && apt-get -f install \
56 | && apt-get update \
57 | && apt-get install -y dotnet-sdk-${DOTNET_VERSION}
58 |
59 | # Prepare nodejs
60 | RUN wget https://deb.nodesource.com/setup_${NODEJS_VERSION}.x -O nodejs-install.sh \
61 | && chmod +x ./nodejs-install.sh \
62 | && ./nodejs-install.sh \
63 | && apt-get install -y \
64 | nodejs
65 |
66 | # Clean up dependencies
67 | RUN apt-get clean autoclean -y \
68 | && apt-get autoremove -y \
69 | && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*
70 |
71 | # Link to build script
72 | RUN ln -sf ${SOURCE_DIR}/portable/build.sh /build.sh
73 |
74 | VOLUME ${SOURCE_DIR}/
75 |
76 | VOLUME ${ARTIFACT_DIR}/
77 |
78 | ENTRYPOINT ["/build.sh"]
79 |
--------------------------------------------------------------------------------
/portable/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #= Generic portable builder (portable, linux, macos, windows)
4 |
5 | set -o errexit
6 | set -o xtrace
7 |
8 | # Set global variables
9 | REPOSITORY_URI="https://repo.jellyfin.org"
10 | FFMPEG_VERSION="7.x"
11 |
12 | # Create the intermediate build dir
13 | BUILD_DIR="/build"
14 | mkdir -p ${BUILD_DIR}/jellyfin
15 |
16 | # Move to source directory
17 | pushd "${SOURCE_DIR}"
18 |
19 | # Build server
20 | pushd jellyfin-server
21 | case ${BUILD_TYPE} in
22 | portable)
23 | RUNTIME=""
24 | APPHOST="-p:UseAppHost=false"
25 | ;;
26 | *)
27 | RUNTIME="--self-contained --runtime ${DOTNET_TYPE}-${DOTNET_ARCH}"
28 | APPHOST="-p:UseAppHost=true"
29 | ;;
30 | esac
31 | export DOTNET_CLI_TELEMETRY_OPTOUT=1
32 | if [[ -z ${CONFIG} ]]; then
33 | CONFIG="Release"
34 | fi
35 | dotnet publish Jellyfin.Server --configuration ${CONFIG} ${RUNTIME} --output ${BUILD_DIR}/jellyfin/ -p:DebugSymbols=false -p:DebugType=none ${APPHOST}
36 | popd
37 |
38 | # Build web
39 | pushd jellyfin-web
40 | npm ci --no-audit --unsafe-perm
41 | npm run build:production
42 | mv dist ${BUILD_DIR}/jellyfin/jellyfin-web
43 | popd
44 |
45 | mkdir -p "${ARTIFACT_DIR}/"
46 |
47 | if [[ -n ${PACKAGE_ARCH} ]]; then
48 | VERSION_SUFFIX="${JELLYFIN_VERSION}-${PACKAGE_ARCH}"
49 | else
50 | VERSION_SUFFIX="${JELLYFIN_VERSION}"
51 | fi
52 |
53 | pushd ${BUILD_DIR}
54 |
55 | pushd jellyfin
56 | # Fetch any additional package(s) needed here (i.e. FFmpeg)
57 | # This is a hack because the ffmpeg naming is very inconsistent and we need to find the right URL(s) from the repo browser
58 | case ${BUILD_TYPE}-${PACKAGE_ARCH} in
59 | # linux-amd64*)
60 | # FFMPEG_PATH=$( curl ${REPOSITORY_URI}/?path=/ffmpeg/linux/latest-${FFMPEG_VERSION}/amd64 | grep -o "/files/.*_portable_linux64-gpl.tar.xz'" | sed "s/'$//" )
61 | # curl --location --output ffmpeg.tar.xz ${REPOSITORY_URI}${FFMPEG_PATH}
62 | # tar -xvJf ffmpeg.tar.xz
63 | # rm ffmpeg.tar.xz
64 | # ;;
65 | # linux-arm64*)
66 | # FFMPEG_PATH=$( curl ${REPOSITORY_URI}/?path=/ffmpeg/linux/latest-${FFMPEG_VERSION}/arm64 | grep -o "/files/.*_portable_linuxarm64-gpl.tar.xz'" | sed "s/'$//" )
67 | # curl --location --output ffmpeg.tar.xz ${REPOSITORY_URI}${FFMPEG_PATH}
68 | # tar -xvJf ffmpeg.tar.xz
69 | # rm ffmpeg.tar.xz
70 | # ;;
71 | # macos-amd64)
72 | # FFMPEG_PATH=$( curl ${REPOSITORY_URI}/?path=/ffmpeg/macos/latest-${FFMPEG_VERSION}/x86_64 | grep -o "/files/.*_portable_mac64-gpl.tar.xz'" | sed "s/'$//" )
73 | # curl --location --output ffmpeg.tar.xz ${REPOSITORY_URI}${FFMPEG_PATH}
74 | # tar -xvJf ffmpeg.tar.xz
75 | # rm ffmpeg.tar.xz
76 | # ;;
77 | # macos-arm64)
78 | # FFMPEG_PATH=$( curl ${REPOSITORY_URI}/?path=/ffmpeg/macos/latest-${FFMPEG_VERSION}/arm64 | grep -o "/files/.*_portable_macarm64-gpl.tar.xz'" | sed "s/'$//" )
79 | # curl --location --output ffmpeg.tar.xz ${REPOSITORY_URI}${FFMPEG_PATH}
80 | # tar -xvJf ffmpeg.tar.xz
81 | # rm ffmpeg.tar.xz
82 | # ;;
83 | windows-amd64)
84 | FFMPEG_PATH=$( curl ${REPOSITORY_URI}/?path=/ffmpeg/windows/latest-${FFMPEG_VERSION}/win64 | grep -o "/files/.*_portable_win64-clang-gpl.zip'" | sed "s/'$//" )
85 | curl --location --output ffmpeg.zip ${REPOSITORY_URI}${FFMPEG_PATH}
86 | unzip ffmpeg.zip
87 | rm ffmpeg.zip
88 | ;;
89 | *)
90 | true
91 | ;;
92 | esac
93 | popd
94 |
95 | for ARCHIVE_TYPE in $( tr ',' '\n' <<<"${ARCHIVE_TYPES}" ); do
96 | case ${ARCHIVE_TYPE} in
97 | targz)
98 | tar -czf "${ARTIFACT_DIR}"/jellyfin_${VERSION_SUFFIX}.tar.gz jellyfin/
99 | ;;
100 | tarxz)
101 | tar -cJf "${ARTIFACT_DIR}"/jellyfin_${VERSION_SUFFIX}.tar.xz jellyfin/
102 | ;;
103 | zip)
104 | zip -qr "${ARTIFACT_DIR}"/jellyfin_${VERSION_SUFFIX}.zip jellyfin/
105 | ;;
106 | esac
107 | done
108 | popd
109 |
110 | # Clean up any lingering artifacts
111 | make -f debian/rules clean
112 | rm -rf ${BUILD_DIR}
113 |
114 | popd
115 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | GitPython
2 | pyyaml
3 | packaging
4 |
--------------------------------------------------------------------------------