about summary refs log tree commit diff
path: root/bundles/jellyfin
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2025-09-07 17:32:13 +0200
committerRobert Günzler <r@gnzler.io>2025-09-07 17:42:42 +0200
commit38be6c13f76e893cf47636257071b440dec0c5b2 (patch)
treef7ded3235a7e3362cbd1ed3d91523968fe0faf90 /bundles/jellyfin
initial commit
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bundles/jellyfin')
-rw-r--r--bundles/jellyfin/bcast.py20
-rw-r--r--bundles/jellyfin/files/SSO-Auth.xml71
-rw-r--r--bundles/jellyfin/files/branding.xml17
-rw-r--r--bundles/jellyfin/files/kube.yaml72
-rw-r--r--bundles/jellyfin/files/logging.json11
-rw-r--r--bundles/jellyfin/files/system.xml202
-rw-r--r--bundles/jellyfin/items.py33
-rw-r--r--bundles/jellyfin/metadata.py16
8 files changed, 442 insertions, 0 deletions
diff --git a/bundles/jellyfin/bcast.py b/bundles/jellyfin/bcast.py
new file mode 100644
index 0000000..2318441
--- /dev/null
+++ b/bundles/jellyfin/bcast.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import socket
+from time import sleep
+
+def main():
+    msg = b'Who is JellyfinServer?'
+
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)  # UDP
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+    sock.bind(('0.0.0.0',0))
+    sock.sendto(msg, ("255.255.255.255", 7359))
+    sock.settimeout(10)
+    recvd, (ip, port) = sock.recvfrom(4096)
+    sock.close()
+    print(f"{ip}:{port} says:")
+    print(recvd.decode('utf-8'))
+
+main()
+
diff --git a/bundles/jellyfin/files/SSO-Auth.xml b/bundles/jellyfin/files/SSO-Auth.xml
new file mode 100644
index 0000000..50dfdc8
--- /dev/null
+++ b/bundles/jellyfin/files/SSO-Auth.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PluginConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <SamlConfigs />
+  <OidConfigs>
+    <item>
+      <key>
+        <string>authelia</string>
+      </key>
+      <value>
+        <PluginConfiguration>
+          <OidEndpoint>https://login.gzr.im</OidEndpoint>
+          <OidClientId>jellyfin</OidClientId>
+          <OidSecret>${repo.vault.cmd(f"rbw get authelia/client/jellyfin")}</OidSecret>
+          <Enabled>true</Enabled>
+          <EnableAuthorization>true</EnableAuthorization>
+          <EnableAllFolders>false</EnableAllFolders>
+          <EnabledFolders>
+            <string>f137a2dd21bbc1b99aa5c0f6bf02a805</string>
+            <string>a656b907eb3a73532e40e44b968d0225</string>
+          </EnabledFolders>
+          <AdminRoles>
+            <string>admins</string>
+          </AdminRoles>
+          <Roles>
+            <string>jellyfin</string>
+          </Roles>
+          <EnableFolderRoles>true</EnableFolderRoles>
+          <EnableLiveTvRoles>false</EnableLiveTvRoles>
+          <EnableLiveTv>false</EnableLiveTv>
+          <EnableLiveTvManagement>false</EnableLiveTvManagement>
+          <LiveTvRoles />
+          <LiveTvManagementRoles />
+          <FolderRoleMappings>
+            <FolderRoleMappings>
+              <Role>jellyfin</Role>
+              <Folders>
+                <string>f137a2dd21bbc1b99aa5c0f6bf02a805</string>
+                <string>a656b907eb3a73532e40e44b968d0225</string>
+              </Folders>
+            </FolderRoleMappings>
+            <FolderRoleMappings>
+              <Role>jellyfin-anime</Role>
+              <Folders>
+                <string>abebc196cc1b8bbf6f8bb5ca7b5ad6f1</string>
+                <string>29391378c4118b35b77f84980d25f0a6</string>
+              </Folders>
+            </FolderRoleMappings>
+            <FolderRoleMappings>
+              <Role>jellyfin-misc</Role>
+              <Folders>
+                <string>a1bc8a1aac164cd3d91bf3f4252f92f4</string>
+              </Folders>
+            </FolderRoleMappings>
+          </FolderRoleMappings>
+          <RoleClaim>groups</RoleClaim>
+          <OidScopes>
+            <string>groups</string>
+          </OidScopes>
+          <DefaultProvider>authelia</DefaultProvider>
+          <SchemeOverride>https</SchemeOverride>
+          <NewPath>true</NewPath>
+          <CanonicalLinks />
+          <DisableHttps>false</DisableHttps>
+          <DisablePushedAuthorization>false</DisablePushedAuthorization>
+          <DoNotValidateEndpoints>false</DoNotValidateEndpoints>
+          <DoNotValidateIssuerName>false</DoNotValidateIssuerName>
+        </PluginConfiguration>
+      </value>
+    </item>
+  </OidConfigs>
+</PluginConfiguration>
diff --git a/bundles/jellyfin/files/branding.xml b/bundles/jellyfin/files/branding.xml
new file mode 100644
index 0000000..487db6e
--- /dev/null
+++ b/bundles/jellyfin/files/branding.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <LoginDisclaimer>&lt;form action="https://tv.gzr.im/sso/OID/start/authelia"&gt;
+  &lt;button class="raised block emby-button button-submit"&gt;
+    Sign in with SSO
+  &lt;/button&gt;
+&lt;/form&gt;</LoginDisclaimer>
+  <CustomCss>a.raised.emby-button {
+  padding: 0.9em 1em;
+  color: inherit !important;
+}
+
+.disclaimerContainer {
+  display: block;
+}</CustomCss>
+  <SplashscreenEnabled>false</SplashscreenEnabled>
+</BrandingOptions>
diff --git a/bundles/jellyfin/files/kube.yaml b/bundles/jellyfin/files/kube.yaml
new file mode 100644
index 0000000..fb6897c
--- /dev/null
+++ b/bundles/jellyfin/files/kube.yaml
@@ -0,0 +1,72 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: jellyfin
+data:
+  JELLYFIN_PublishedServerUrl: http://shimakaze:8096
+---
+apiVersion: v1
+kind: Pod
+metadata:
+  name: jellyfin
+  labels:
+    traefik.enable: true
+    traefik.http.routers.jellyfin.tls: true
+    traefik.http.routers.jellyfin.tls.certresolver: le
+    traefik.http.routers.jellyfin.entrypoints: http,https
+    traefik.http.routers.jellyfin.rule: Host(`tv.gzr.im`)
+    traefik.http.routers.jellyfin.service: jellyfin
+    traefik.http.services.jellyfin.loadbalancer.server.port: 8096
+    # metrics
+    traefik.http.routers.jellyfin-metrics.entrypoints: metrics
+    traefik.http.routers.jellyfin-metrics.rule: Path(`/metrics/jellyfin`)
+    traefik.http.routers.jellyfin-metrics.middlewares: replacepath-metrics
+spec:
+  # NOTE: required to make discovery work
+  #       jellyfin clients use udp broadcast messages on port 7359
+  #       sending "Who is JellyfinServer?". To receive those requests the
+  #       jellyfin server can't be behind container networking
+  hostNetwork: true
+
+  restartPolicy: Never
+  dnsPolicy: Default
+  containers:
+    - name: jellyfin
+      image: ghcr.io/jellyfin/jellyfin:10.10.7
+      envFrom:
+      - configMapRef:
+          name: jellyfin
+      ports:
+        - containerPort: 8096
+          protocol: TCP
+        - containerPort: 1900
+          protocol: UDP
+        - containerPort: 7359
+          protocol: UDP
+      volumeMounts:
+        - name: data
+          mountPath: /config
+        - name: cache
+          mountPath: /cache
+        - name: media
+          mountPath: /media
+          readOnly: true
+      resources:
+        requests:
+          podman.io/device=/dev/dri/card0: 1
+          podman.io/device=/dev/dri/renderD128: 1
+        limits:
+          #cpu: 300m
+          #memory: 512Mi
+
+  volumes:
+    - name: data
+      hostPath:
+        path: /var/lib/jellyfin
+        type: DirectoryOrCreate
+    - name: cache
+      emptyDir: {}
+    - name: media
+      hostPath:
+        path: /media/hayasui/media
+        type: Directory
diff --git a/bundles/jellyfin/files/logging.json b/bundles/jellyfin/files/logging.json
new file mode 100644
index 0000000..b658515
--- /dev/null
+++ b/bundles/jellyfin/files/logging.json
@@ -0,0 +1,11 @@
+{
+    "Serilog": {
+        "MinimumLevel": {
+            "Default": "Information",
+            "Override": {
+                "Microsoft": "Warning",
+                "System": "Warning"
+            }
+        }
+    }
+}
diff --git a/bundles/jellyfin/files/system.xml b/bundles/jellyfin/files/system.xml
new file mode 100644
index 0000000..0696f49
--- /dev/null
+++ b/bundles/jellyfin/files/system.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ServerConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <LogFileRetentionDays>3</LogFileRetentionDays>
+  <IsStartupWizardCompleted>true</IsStartupWizardCompleted>
+  <CachePath>/cache</CachePath>
+  <EnableMetrics>true</EnableMetrics>
+  <EnableNormalizedItemByNameIds>true</EnableNormalizedItemByNameIds>
+  <IsPortAuthorized>true</IsPortAuthorized>
+  <QuickConnectAvailable>false</QuickConnectAvailable>
+  <EnableCaseSensitiveItemIds>true</EnableCaseSensitiveItemIds>
+  <DisableLiveTvChannelUserDataName>true</DisableLiveTvChannelUserDataName>
+  <MetadataPath>/config/metadata</MetadataPath>
+  <PreferredMetadataLanguage>en</PreferredMetadataLanguage>
+  <MetadataCountryCode>DE</MetadataCountryCode>
+  <SortReplaceCharacters>
+    <string>.</string>
+    <string>+</string>
+    <string>%</string>
+  </SortReplaceCharacters>
+  <SortRemoveCharacters>
+    <string>,</string>
+    <string>&amp;</string>
+    <string>-</string>
+    <string>{</string>
+    <string>}</string>
+    <string>'</string>
+  </SortRemoveCharacters>
+  <SortRemoveWords>
+    <string>the</string>
+    <string>a</string>
+    <string>an</string>
+  </SortRemoveWords>
+  <MinResumePct>5</MinResumePct>
+  <MaxResumePct>90</MaxResumePct>
+  <MinResumeDurationSeconds>300</MinResumeDurationSeconds>
+  <MinAudiobookResume>5</MinAudiobookResume>
+  <MaxAudiobookResume>5</MaxAudiobookResume>
+  <InactiveSessionThreshold>0</InactiveSessionThreshold>
+  <LibraryMonitorDelay>60</LibraryMonitorDelay>
+  <LibraryUpdateDuration>30</LibraryUpdateDuration>
+  <ImageSavingConvention>Legacy</ImageSavingConvention>
+  <MetadataOptions>
+    <MetadataOptions>
+      <ItemType>Book</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>Movie</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>MusicVideo</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers>
+        <string>The Open Movie Database</string>
+      </DisabledMetadataFetchers>
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers>
+        <string>The Open Movie Database</string>
+      </DisabledImageFetchers>
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>Series</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>MusicAlbum</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers>
+        <string>TheAudioDB</string>
+      </DisabledMetadataFetchers>
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>MusicArtist</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers>
+        <string>TheAudioDB</string>
+      </DisabledMetadataFetchers>
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>BoxSet</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>Season</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+    <MetadataOptions>
+      <ItemType>Episode</ItemType>
+      <DisabledMetadataSavers />
+      <LocalMetadataReaderOrder />
+      <DisabledMetadataFetchers />
+      <MetadataFetcherOrder />
+      <DisabledImageFetchers />
+      <ImageFetcherOrder />
+    </MetadataOptions>
+  </MetadataOptions>
+  <SkipDeserializationForBasicTypes>true</SkipDeserializationForBasicTypes>
+  <ServerName>jellyfin</ServerName>
+  <UICulture>en-US</UICulture>
+  <SaveMetadataHidden>false</SaveMetadataHidden>
+  <ContentTypes />
+  <RemoteClientBitrateLimit>0</RemoteClientBitrateLimit>
+  <EnableFolderView>false</EnableFolderView>
+  <EnableGroupingIntoCollections>false</EnableGroupingIntoCollections>
+  <DisplaySpecialsWithinSeasons>true</DisplaySpecialsWithinSeasons>
+  <CodecsUsed />
+  <PluginRepositories>
+    <RepositoryInfo>
+      <Name>Jellyfin Stable</Name>
+      <Url>https://repo.jellyfin.org/files/plugin/manifest.json</Url>
+      <Enabled>true</Enabled>
+    </RepositoryInfo>
+    <RepositoryInfo>
+      <Name>Jellyfin SSO</Name>
+      <Url>https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json</Url>
+      <Enabled>true</Enabled>
+    </RepositoryInfo>
+    <RepositoryInfo>
+      <Name>Intro Skipper</Name>
+      <Url>https://manifest.intro-skipper.org/manifest.json</Url>
+      <Enabled>true</Enabled>
+    </RepositoryInfo>
+  </PluginRepositories>
+  <EnableExternalContentInSuggestions>true</EnableExternalContentInSuggestions>
+  <ImageExtractionTimeoutMs>0</ImageExtractionTimeoutMs>
+  <PathSubstitutions />
+  <EnableSlowResponseWarning>true</EnableSlowResponseWarning>
+  <SlowResponseThresholdMs>500</SlowResponseThresholdMs>
+  <CorsHosts>
+    <string>*</string>
+  </CorsHosts>
+  <ActivityLogRetentionDays>30</ActivityLogRetentionDays>
+  <LibraryScanFanoutConcurrency>0</LibraryScanFanoutConcurrency>
+  <LibraryMetadataRefreshConcurrency>0</LibraryMetadataRefreshConcurrency>
+  <RemoveOldPlugins>false</RemoveOldPlugins>
+  <AllowClientLogUpload>true</AllowClientLogUpload>
+  <DummyChapterDuration>0</DummyChapterDuration>
+  <ChapterImageResolution>MatchSource</ChapterImageResolution>
+  <ParallelImageEncodingLimit>0</ParallelImageEncodingLimit>
+  <CastReceiverApplications>
+    <CastReceiverApplication>
+      <Id>F007D354</Id>
+      <Name>Stable</Name>
+    </CastReceiverApplication>
+    <CastReceiverApplication>
+      <Id>6F511C87</Id>
+      <Name>Unstable</Name>
+    </CastReceiverApplication>
+  </CastReceiverApplications>
+  <TrickplayOptions>
+    <EnableHwAcceleration>false</EnableHwAcceleration>
+    <EnableHwEncoding>false</EnableHwEncoding>
+    <EnableKeyFrameOnlyExtraction>false</EnableKeyFrameOnlyExtraction>
+    <ScanBehavior>NonBlocking</ScanBehavior>
+    <ProcessPriority>BelowNormal</ProcessPriority>
+    <Interval>10000</Interval>
+    <WidthResolutions>
+      <int>320</int>
+    </WidthResolutions>
+    <TileWidth>10</TileWidth>
+    <TileHeight>10</TileHeight>
+    <Qscale>4</Qscale>
+    <JpegQuality>90</JpegQuality>
+    <ProcessThreads>1</ProcessThreads>
+  </TrickplayOptions>
+</ServerConfiguration>
diff --git a/bundles/jellyfin/items.py b/bundles/jellyfin/items.py
new file mode 100644
index 0000000..5ed07ef
--- /dev/null
+++ b/bundles/jellyfin/items.py
@@ -0,0 +1,33 @@
+files = {
+    "/var/lib/jellyfin/plugins/SSO-Auth.zip": {
+        "content_type": "download",
+        "source": "https://github.com/9p4/jellyfin-plugin-sso/releases/download/v3.5.2.4/sso-authentication_3.5.2.4.zip",
+        "content_hash": "a2f00e5acc7adf785202e27fffc969e019cbd9e8",
+        "triggers": {"action:extract-jellyfin-plugin-sso"},
+    },
+    "/var/lib/jellyfin/config/branding.xml": {
+        "content_type": "text",
+        "triggers": {"svc_s6rc:jellyfin:restart"},
+    },
+    "/var/lib/jellyfin/config/logging.json": {
+        "content_type": "mako",
+        "triggers": {"svc_s6rc:jellyfin:restart"},
+    },
+    "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml": {
+        "content_type": "mako",
+        "triggers": {"svc_s6rc:jellyfin:restart"},
+    },
+
+    "/etc/deployments/jellyfin/kube.yaml": {
+        "content_type": "text",
+        "source": "kube.yaml",
+        "triggers": {"svc_s6rc:jellyfin:restart"},
+    },
+}
+
+actions = {
+    "extract-jellyfin-plugin-sso": {
+        "command": "unzip -d /var/lib/jellyfin/plugins/SSO-Auth /var/lib/jellyfin/plugins/SSO-Auth.zip",
+        "triggered": True,
+    },
+}
diff --git a/bundles/jellyfin/metadata.py b/bundles/jellyfin/metadata.py
new file mode 100644
index 0000000..a614afd
--- /dev/null
+++ b/bundles/jellyfin/metadata.py
@@ -0,0 +1,16 @@
+defaults = {
+    "backup": {
+        "includes": {
+            "/var/lib/jellyfin",
+            # "/media/hayasui/media/movies",
+            # "/media/hayasui/media/tvshows",
+            # "/media/hayasui/media/anime",
+        }
+    },
+    "containers": {
+        "jellyfin": {},
+    },
+    "metrics": {
+        "jellyfin": {},
+    },
+}