diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml
index 4b2673e82d..d8c550e704 100644
--- a/.github/workflows/ci-codeql-analysis.yml
+++ b/.github/workflows/ci-codeql-analysis.yml
@@ -27,11 +27,11 @@ jobs:
dotnet-version: '8.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0
+ uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0
+ uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0
+ uses: github/codeql-action/analyze@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 3752ba1b06..76e2b4ebc1 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -172,10 +172,11 @@
- [tallbl0nde](https://github.com/tallbl0nde)
- [sleepycatcoding](https://github.com/sleepycatcoding)
- [scampower3](https://github.com/scampower3)
- - [Chris-Codes-It] (https://github.com/Chris-Codes-It)
+ - [Chris-Codes-It](https://github.com/Chris-Codes-It)
- [Pithaya](https://github.com/Pithaya)
- [Çağrı Sakaoğlu](https://github.com/ilovepilav)
_ [Barasingha](https://github.com/MaVdbussche)
+ - [Gauvino](https://github.com/Gauvino)
# Emby Contributors
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 294414ee12..bc87f9fc96 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -12,7 +12,7 @@
-
+
@@ -71,7 +71,7 @@
-
+
@@ -85,6 +85,6 @@
-
+
diff --git a/Dockerfile b/Dockerfile
index d3f10cd12e..550c3203d7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,72 +8,68 @@ FROM node:20-alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && apk del curl \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
&& npm run build:production \
&& mv dist /dist
-FROM debian:stable-slim as app
+FROM debian:bookworm-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_VISIBLE_DEVICES="all"
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+ENV JELLYFIN_DATA_DIR=/config
+ENV JELLYFIN_CACHE_DIR=/cache
+
# https://github.com/intel/compute-runtime/releases
-ARG GMMLIB_VERSION=22.0.2
-ARG IGC_VERSION=1.0.10395
-ARG NEO_VERSION=22.08.22549
-ARG LEVEL_ZERO_VERSION=1.3.22549
+ARG GMMLIB_VERSION=22.3.11.ci17757293
+ARG IGC_VERSION=1.0.15136.22
+ARG NEO_VERSION=23.39.27427.23
+ARG LEVEL_ZERO_VERSION=1.3.27427.23
-# Install dependencies:
-# mesa-va-drivers: needed for AMD VAAPI. Mesa >= 20.1 is required for HEVC transcoding.
-# curl: healthcheck
RUN apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget curl \
- && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \
+ && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \
&& echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
&& apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y \
- mesa-va-drivers \
- jellyfin-ffmpeg5 \
- openssl \
- locales \
+ && apt-get install --no-install-recommends --no-install-suggests -y mesa-va-drivers jellyfin-ffmpeg6 openssl locales \
# Intel VAAPI Tone mapping dependencies:
# Prefer NEO to Beignet since the latter one doesn't support Comet Lake or newer for now.
# Do not use the intel-opencl-icd package from repo since they will not build with RELEASE_WITH_REGKEYS enabled.
&& mkdir intel-compute-runtime \
&& cd intel-compute-runtime \
- && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-gmmlib_${GMMLIB_VERSION}_amd64.deb \
- && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \
- && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \
- && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}_amd64.deb \
- && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \
+ && curl -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \
+ -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \
+ -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \
+ -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}_amd64.deb \
+ -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libigdgmm12_${GMMLIB_VERSION}_amd64.deb \
&& dpkg -i *.deb \
&& cd .. \
&& rm -rf intel-compute-runtime \
- && apt-get remove gnupg wget -y \
+ && apt-get remove gnupg -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
- && mkdir -p /cache /config /media \
- && chmod 777 /cache /config /media \
+ && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
+ && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
-# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
-ENV LC_ALL en_US.UTF-8
-ENV LANG en_US.UTF-8
-ENV LANGUAGE en_US:en
+ENV LC_ALL=en_US.UTF-8
+ENV LANG=en_US.UTF-8
+ENV LANGUAGE=en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# because of changes in docker and systemd we need to not build in parallel at the moment
-# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none
+
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none
FROM app
@@ -83,11 +79,9 @@ COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
-VOLUME /cache /config
-ENTRYPOINT ["./jellyfin/jellyfin", \
- "--datadir", "/config", \
- "--cachedir", "/cache", \
- "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
+VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR}
+ENTRYPOINT [ "./jellyfin/jellyfin", \
+ "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Dockerfile.arm b/Dockerfile.arm
index db1acc838a..07039e43b5 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -4,64 +4,58 @@
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=8.0
-
FROM node:20-alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && apk del curl \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
&& npm run build:production \
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-arm as qemu
-FROM arm32v7/debian:stable-slim as app
+FROM arm32v7/debian:bookworm-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_VISIBLE_DEVICES="all"
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+ENV JELLYFIN_DATA_DIR=/config
+ENV JELLYFIN_CACHE_DIR=/cache
+
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
-# curl: setup & healthcheck
RUN apt-get update \
- && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
- curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
- curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
- echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
- echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
- apt-get update && \
- apt-get install --no-install-recommends --no-install-suggests -y \
- jellyfin-ffmpeg \
- libssl-dev \
- libfontconfig1 \
- libfreetype6 \
- vainfo \
- libva2 \
- locales \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \
+ && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \
+ && curl -fsSL https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | gpg --dearmor -o /etc/apt/trusted.gpg.d/ubuntu-jellyfin.gpg \
+ && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y \
+ jellyfin-ffmpeg6 libssl-dev libfontconfig1 \
+ libfreetype6 vainfo libva2 locales \
&& apt-get remove gnupg -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
- && mkdir -p /cache /config /media \
- && chmod 777 /cache /config /media \
+ && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
+ && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
-# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
-ENV LC_ALL en_US.UTF-8
-ENV LANG en_US.UTF-8
-ENV LANGUAGE en_US:en
+ENV LC_ALL=en_US.UTF-8
+ENV LANG=en_US.UTF-8
+ENV LANGUAGE=en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# Discard objs - may cause failures if exists
-RUN find . -type d -name obj | xargs -r rm -r
-# Build
+
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none
FROM app
@@ -72,11 +66,9 @@ COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
-VOLUME /cache /config
-ENTRYPOINT ["./jellyfin/jellyfin", \
- "--datadir", "/config", \
- "--cachedir", "/cache", \
- "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
+VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR}
+ENTRYPOINT [ "/jellyfin/jellyfin", \
+ "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 3eb5f45fc4..54023794fc 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -4,58 +4,58 @@
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=8.0
-
FROM node:20-alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
+ && apk del curl \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
&& npm run build:production \
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
-FROM arm64v8/debian:stable-slim as app
+FROM arm64v8/debian:bookworm-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
+ENV NVIDIA_VISIBLE_DEVICES="all"
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
+ENV JELLYFIN_DATA_DIR=/config
+ENV JELLYFIN_CACHE_DIR=/cache
+
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
-# curl: healcheck
-RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
- ffmpeg \
- libssl-dev \
- ca-certificates \
- libfontconfig1 \
- libfreetype6 \
- libomxil-bellagio0 \
- libomxil-bellagio-bin \
- locales \
- curl \
+RUN apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \
+ && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \
+ && curl -fsSL https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | gpg --dearmor -o /etc/apt/trusted.gpg.d/ubuntu-jellyfin.gpg \
+ && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
+ && apt-get update \
+ && apt-get install --no-install-recommends --no-install-suggests -y \
+ jellyfin-ffmpeg6 locales libssl-dev libfontconfig1 \
+ libfreetype6 libomxil-bellagio0 libomxil-bellagio-bin \
+ && apt-get remove gnupg -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
- && mkdir -p /cache /config /media \
- && chmod 777 /cache /config /media \
+ && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
+ && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
-# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
-ENV LC_ALL en_US.UTF-8
-ENV LANG en_US.UTF-8
-ENV LANGUAGE en_US:en
+ENV LC_ALL=en_US.UTF-8
+ENV LANG=en_US.UTF-8
+ENV LANGUAGE=en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# Discard objs - may cause failures if exists
-RUN find . -type d -name obj | xargs -r rm -r
-# Build
+
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none
FROM app
@@ -66,11 +66,9 @@ COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
-VOLUME /cache /config
-ENTRYPOINT ["./jellyfin/jellyfin", \
- "--datadir", "/config", \
- "--cachedir", "/cache", \
- "--ffmpeg", "/usr/bin/ffmpeg"]
+VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR}
+ENTRYPOINT [ "/jellyfin/jellyfin", \
+ "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 48d5d8c6a9..5870fed761 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -695,7 +695,7 @@ namespace Emby.Server.Implementations
GetExports(),
GetExports());
- Resolve().AddParts(GetExports(), GetExports(), GetExports());
+ Resolve().AddParts(GetExports(), GetExports());
Resolve().AddParts(GetExports());
}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index d0772654ce..a6336f1451 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -699,7 +699,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@EndDate");
}
- saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(default) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture));
+ saveItemStatement.TryBind("@ChannelId", item.ChannelId.IsEmpty() ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture));
if (item is IHasProgramAttributes hasProgramAttributes)
{
@@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ProductionYear", item.ProductionYear);
var parentId = item.ParentId;
- if (parentId.Equals(default))
+ if (parentId.IsEmpty())
{
saveItemStatement.TryBindNull("@ParentId");
}
@@ -925,7 +925,7 @@ namespace Emby.Server.Implementations.Data
{
saveItemStatement.TryBind("@SeasonName", episode.SeasonName);
- var nullableSeasonId = episode.SeasonId.Equals(default) ? (Guid?)null : episode.SeasonId;
+ var nullableSeasonId = episode.SeasonId.IsEmpty() ? (Guid?)null : episode.SeasonId;
saveItemStatement.TryBind("@SeasonId", nullableSeasonId);
}
@@ -937,7 +937,7 @@ namespace Emby.Server.Implementations.Data
if (item is IHasSeries hasSeries)
{
- var nullableSeriesId = hasSeries.SeriesId.Equals(default) ? (Guid?)null : hasSeries.SeriesId;
+ var nullableSeriesId = hasSeries.SeriesId.IsEmpty() ? (Guid?)null : hasSeries.SeriesId;
saveItemStatement.TryBind("@SeriesId", nullableSeriesId);
saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey);
@@ -1010,7 +1010,7 @@ namespace Emby.Server.Implementations.Data
}
Guid ownerId = item.OwnerId;
- if (ownerId.Equals(default))
+ if (ownerId.IsEmpty())
{
saveItemStatement.TryBindNull("@OwnerId");
}
@@ -1266,7 +1266,7 @@ namespace Emby.Server.Implementations.Data
/// is .
public BaseItem RetrieveItem(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
@@ -1970,7 +1970,7 @@ namespace Emby.Server.Implementations.Data
{
CheckDisposed();
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
@@ -3230,7 +3230,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add($"ChannelId in ({inClause})");
}
- if (!query.ParentId.Equals(default))
+ if (!query.ParentId.IsEmpty())
{
whereClauses.Add("ParentId=@ParentId");
statement?.TryBind("@ParentId", query.ParentId);
@@ -4452,7 +4452,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
public void DeleteItem(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
@@ -4583,13 +4583,13 @@ AND Type = @InternalPersonType)");
statement?.TryBind("@UserId", query.User.InternalId);
}
- if (!query.ItemId.Equals(default))
+ if (!query.ItemId.IsEmpty())
{
whereClauses.Add("ItemId=@ItemId");
statement?.TryBind("@ItemId", query.ItemId);
}
- if (!query.AppearsInItemId.Equals(default))
+ if (!query.AppearsInItemId.IsEmpty())
{
whereClauses.Add("p.Name in (Select Name from People where ItemId=@AppearsInItemId)");
statement?.TryBind("@AppearsInItemId", query.AppearsInItemId);
@@ -4640,7 +4640,7 @@ AND Type = @InternalPersonType)");
private void UpdateAncestors(Guid itemId, List ancestorIds, SqliteConnection db, SqliteCommand deleteAncestorsStatement)
{
- if (itemId.Equals(default))
+ if (itemId.IsEmpty())
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5156,7 +5156,7 @@ AND Type = @InternalPersonType)");
private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, SqliteConnection db)
{
- if (itemId.Equals(default))
+ if (itemId.IsEmpty())
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5228,7 +5228,7 @@ AND Type = @InternalPersonType)");
public void UpdatePeople(Guid itemId, List people)
{
- if (itemId.Equals(default))
+ if (itemId.IsEmpty())
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5378,7 +5378,7 @@ AND Type = @InternalPersonType)");
{
CheckDisposed();
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
@@ -5758,7 +5758,7 @@ AND Type = @InternalPersonType)");
CancellationToken cancellationToken)
{
CheckDisposed();
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentException("Guid can't be empty.", nameof(id));
}
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index a83d7a4105..83e7b230df 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -241,7 +242,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint
{
var userIds = _sessionManager.Sessions
.Select(i => i.UserId)
- .Where(i => !i.Equals(default))
+ .Where(i => !i.IsEmpty())
.Distinct()
.ToArray();
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index a79ffd9cb1..8ae913dad8 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -732,7 +732,7 @@ namespace Emby.Server.Implementations.Library
Path = path
};
- if (folder.Id.Equals(default))
+ if (folder.Id.IsEmpty())
{
if (string.IsNullOrEmpty(folder.Path))
{
@@ -1219,7 +1219,7 @@ namespace Emby.Server.Implementations.Library
/// is null.
public BaseItem GetItemById(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
@@ -1241,7 +1241,7 @@ namespace Emby.Server.Implementations.Library
public List GetItemList(InternalItemsQuery query, bool allowExternalContent)
{
- if (query.Recursive && !query.ParentId.Equals(default))
+ if (query.Recursive && !query.ParentId.IsEmpty())
{
var parent = GetItemById(query.ParentId);
if (parent is not null)
@@ -1272,7 +1272,7 @@ namespace Emby.Server.Implementations.Library
public int GetCount(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(default))
+ if (query.Recursive && !query.ParentId.IsEmpty())
{
var parent = GetItemById(query.ParentId);
if (parent is not null)
@@ -1430,7 +1430,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult GetItemsResult(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(default))
+ if (query.Recursive && !query.ParentId.IsEmpty())
{
var parent = GetItemById(query.ParentId);
if (parent is not null)
@@ -1486,7 +1486,7 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
- query.ParentId.Equals(default) &&
+ query.ParentId.IsEmpty() &&
query.ChannelIds.Count == 0 &&
query.TopParentIds.Length == 0 &&
string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) &&
@@ -1520,7 +1520,7 @@ namespace Emby.Server.Implementations.Library
}
// Translate view into folders
- if (!view.DisplayParentId.Equals(default))
+ if (!view.DisplayParentId.IsEmpty())
{
var displayParent = GetItemById(view.DisplayParentId);
if (displayParent is not null)
@@ -1531,7 +1531,7 @@ namespace Emby.Server.Implementations.Library
return Array.Empty();
}
- if (!view.ParentId.Equals(default))
+ if (!view.ParentId.IsEmpty())
{
var displayParent = GetItemById(view.ParentId);
if (displayParent is not null)
@@ -2137,7 +2137,7 @@ namespace Emby.Server.Implementations.Library
return null;
}
- while (!item.ParentId.Equals(default))
+ while (!item.ParentId.IsEmpty())
{
var parent = item.GetParent();
if (parent is null || parent is AggregateFolder)
@@ -2215,7 +2215,7 @@ namespace Emby.Server.Implementations.Library
CollectionType? viewType,
string sortName)
{
- var parentIdString = parentId.Equals(default)
+ var parentIdString = parentId.IsEmpty()
? null
: parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty);
@@ -2251,7 +2251,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(default))
+ if (!refresh && !item.DisplayParentId.IsEmpty())
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2315,7 +2315,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(default))
+ if (!refresh && !item.DisplayParentId.IsEmpty())
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2345,7 +2345,7 @@ namespace Emby.Server.Implementations.Library
{
ArgumentException.ThrowIfNullOrEmpty(name);
- var parentIdString = parentId.Equals(default)
+ var parentIdString = parentId.IsEmpty()
? null
: parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty);
@@ -2391,7 +2391,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(default))
+ if (!refresh && !item.DisplayParentId.IsEmpty())
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2419,7 +2419,7 @@ namespace Emby.Server.Implementations.Library
return GetItemById(parentId.Value);
}
- if (userId.HasValue && !userId.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
return GetUserRootFolder();
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 68eccf311d..c38f1af912 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
@@ -524,10 +525,10 @@ namespace Emby.Server.Implementations.Library
_logger.LogInformation("Live stream opened: {@MediaSource}", mediaSource);
var clone = JsonSerializer.Deserialize(json, _jsonOptions);
- if (!request.UserId.Equals(default))
+ if (!request.UserId.IsEmpty())
{
var user = _userManager.GetUserById(request.UserId);
- var item = request.ItemId.Equals(default)
+ var item = request.ItemId.IsEmpty()
? null
: _libraryManager.GetItemById(request.ItemId);
SetDefaultAudioAndSubtitleStreamIndexes(item, clone, user);
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index b2439a87e1..078f4ad219 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Library
{
return Guid.Empty;
}
- }).Where(i => !i.Equals(default)).ToArray();
+ }).Where(i => !i.IsEmpty()).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index b916b91708..020cb517d1 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult GetSearchHints(SearchQuery query)
{
User user = null;
- if (!query.UserId.Equals(default))
+ if (!query.UserId.IsEmpty())
{
user = _userManager.GetUserById(query.UserId);
}
@@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.Library
if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
{
- if (!searchQuery.ParentId.Equals(default))
+ if (!searchQuery.ParentId.IsEmpty())
{
searchQuery.AncestorIds = new[] { searchQuery.ParentId };
searchQuery.ParentId = Guid.Empty;
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 1d662ed8dd..83a66c8e47 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Threading;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@@ -151,7 +152,7 @@ namespace Emby.Server.Implementations.Library
var index = Array.IndexOf(orders, i.Id);
if (index == -1
&& i is UserView view
- && !view.DisplayParentId.Equals(default))
+ && !view.DisplayParentId.IsEmpty())
{
index = Array.IndexOf(orders, view.DisplayParentId);
}
@@ -253,7 +254,7 @@ namespace Emby.Server.Implementations.Library
var parents = new List();
- if (!parentId.Equals(default))
+ if (!parentId.IsEmpty())
{
var parentItem = _libraryManager.GetItemById(parentId);
if (parentItem is Channel)
diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json
index 5368b8eb6e..2d02522fea 100644
--- a/Emby.Server.Implementations/Localization/Core/ka.json
+++ b/Emby.Server.Implementations/Localization/Core/ka.json
@@ -4,9 +4,9 @@
"HeaderFavoriteAlbums": "რჩეული ალბომები",
"TasksApplicationCategory": "აპლიკაცია",
"Albums": "ალბომები",
- "AppDeviceValues": "აპი: {0}, მოწყობილობა: {1}",
+ "AppDeviceValues": "აპლიკაცია: {0}, მოწყობილობა: {1}",
"Application": "აპლიკაცია",
- "Artists": "შემსრულებლები",
+ "Artists": "არტისტი",
"AuthenticationSucceededWithUserName": "{0} -ის ავთენტიკაცია წარმატებულია",
"Books": "წიგნები",
"Forced": "ძალით",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 66cbee3948..3ab9774c27 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -83,13 +83,13 @@
"UserDeletedWithName": "用戶 {0} 已被移除",
"UserDownloadingItemWithValues": "{0} 正在下載 {1}",
"UserLockedOutWithName": "用戶 {0} 已被封鎖",
- "UserOfflineFromDevice": "{0} 從 {1} 斷開連接",
+ "UserOfflineFromDevice": "{0} 終止了 {1} 的連接",
"UserOnlineFromDevice": "{0} 從 {1} 連線",
- "UserPasswordChangedWithName": "{0} 的密碼已被變改",
+ "UserPasswordChangedWithName": "{0} 的密碼已被更改",
"UserPolicyUpdatedWithName": "使用條款已更新為 {0}",
"UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}",
- "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}",
- "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫",
+ "UserStoppedPlayingItemWithValues": "{0} 停止在 {2} 上播放 {1}",
+ "ValueHasBeenAddedToLibrary": "{0} 已被加入至你的媒體庫",
"ValueSpecialEpisodeName": "特典 - {0}",
"VersionNumber": "版本 {0}",
"TaskDownloadMissingSubtitles": "下載欠缺字幕",
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index d2e2fd7d56..aea8d65322 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -11,6 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -178,7 +179,7 @@ namespace Emby.Server.Implementations.Playlists
public Task AddToPlaylistAsync(Guid playlistId, IReadOnlyCollection itemIds, Guid userId)
{
- var user = userId.Equals(default) ? null : _userManager.GetUserById(userId);
+ var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId);
return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
index acd4bf9056..812df81922 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
@@ -115,9 +115,10 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
List? itemsToRemove = null;
foreach (var linkedChild in folder.LinkedChildren)
{
- if (!File.Exists(folder.Path))
+ var path = linkedChild.Path;
+ if (!File.Exists(path))
{
- _logger.LogInformation("Item in {FolderName} cannot be found at {ItemPath}", folder.Name, linkedChild.Path);
+ _logger.LogInformation("Item in {FolderName} cannot be found at {ItemPath}", folder.Name, path);
(itemsToRemove ??= new List()).Add(linkedChild);
}
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index f457a78b3b..bbb3938dcf 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -337,7 +337,7 @@ namespace Emby.Server.Implementations.Session
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
- if (!info.ItemId.Equals(default) && info.Item is null && libraryItem is not null)
+ if (!info.ItemId.IsEmpty() && info.Item is null && libraryItem is not null)
{
var current = session.NowPlayingItem;
@@ -529,7 +529,7 @@ namespace Emby.Server.Implementations.Session
{
var users = new List();
- if (session.UserId.Equals(default))
+ if (session.UserId.IsEmpty())
{
return users;
}
@@ -690,7 +690,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId.Equals(default)
+ var libraryItem = info.ItemId.IsEmpty()
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -784,7 +784,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId.Equals(default)
+ var libraryItem = info.ItemId.IsEmpty()
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -923,7 +923,7 @@ namespace Emby.Server.Implementations.Session
session.StopAutomaticProgress();
- var libraryItem = info.ItemId.Equals(default)
+ var libraryItem = info.ItemId.IsEmpty()
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -933,7 +933,7 @@ namespace Emby.Server.Implementations.Session
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
- if (!info.ItemId.Equals(default) && info.Item is null && libraryItem is not null)
+ if (!info.ItemId.IsEmpty() && info.Item is null && libraryItem is not null)
{
var current = session.NowPlayingItem;
@@ -1154,7 +1154,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSessionToRemoteControl(sessionId);
- var user = session.UserId.Equals(default) ? null : _userManager.GetUserById(session.UserId);
+ var user = session.UserId.IsEmpty() ? null : _userManager.GetUserById(session.UserId);
List items;
@@ -1223,7 +1223,7 @@ namespace Emby.Server.Implementations.Session
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (!controllingSession.UserId.Equals(default))
+ if (!controllingSession.UserId.IsEmpty())
{
command.ControllingUserId = controllingSession.UserId;
}
@@ -1342,7 +1342,7 @@ namespace Emby.Server.Implementations.Session
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (!controllingSession.UserId.Equals(default))
+ if (!controllingSession.UserId.IsEmpty())
{
command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture);
}
@@ -1463,7 +1463,7 @@ namespace Emby.Server.Implementations.Session
ArgumentException.ThrowIfNullOrEmpty(request.AppVersion);
User user = null;
- if (!request.UserId.Equals(default))
+ if (!request.UserId.IsEmpty())
{
user = _userManager.GetUserById(request.UserId);
}
@@ -1766,7 +1766,7 @@ namespace Emby.Server.Implementations.Session
{
ArgumentNullException.ThrowIfNull(info);
- var user = info.UserId.Equals(default)
+ var user = info.UserId.IsEmpty()
? null
: _userManager.GetUserById(info.UserId);
diff --git a/Emby.Server.Implementations/SyncPlay/Group.cs b/Emby.Server.Implementations/SyncPlay/Group.cs
index da8f949326..a7821c0e0e 100644
--- a/Emby.Server.Implementations/SyncPlay/Group.cs
+++ b/Emby.Server.Implementations/SyncPlay/Group.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.SyncPlay;
@@ -553,7 +554,7 @@ namespace Emby.Server.Implementations.SyncPlay
if (playingItemRemoved)
{
var itemId = PlayQueue.GetPlayingItemId();
- if (!itemId.Equals(default))
+ if (!itemId.IsEmpty())
{
var item = _libraryManager.GetItemById(itemId);
RunTimeTicks = item.RunTimeTicks ?? 0;
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index ef890aeb4f..34c9e86f26 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -41,7 +42,7 @@ namespace Emby.Server.Implementations.TV
}
string? presentationUniqueKey = null;
- if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default))
+ if (!query.SeriesId.IsNullOrEmpty())
{
if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series)
{
@@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.TV
string? presentationUniqueKey = null;
int? limit = null;
- if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default))
+ if (!request.SeriesId.IsNullOrEmpty())
{
if (_libraryManager.GetItemById(request.SeriesId.Value) is Series series)
{
@@ -146,7 +147,7 @@ namespace Emby.Server.Implementations.TV
// If viewing all next up for all series, remove first episodes
// But if that returns empty, keep those first episodes (avoid completely empty view)
- var alwaysEnableFirstEpisode = request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default);
+ var alwaysEnableFirstEpisode = !request.SeriesId.IsNullOrEmpty();
var anyFound = false;
return allNextUp
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 15c4cfdf02..ce3d6cab88 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -11,6 +11,7 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
+using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
@@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.Updates
availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}
- if (!id.Equals(default))
+ if (!id.IsEmpty())
{
availablePackages = availablePackages.Where(x => x.Id.Equals(id));
}
diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
index cf3cb69052..7d0fe55898 100644
--- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
+++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs
@@ -2,6 +2,7 @@
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
@@ -41,7 +42,7 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy
var isApiKey = context.User.GetIsApiKey();
var userId = context.User.GetUserId();
// This likely only happens during the wizard, so skip the default checks and let any other handlers do it
- if (!isApiKey && userId.Equals(default))
+ if (!isApiKey && userId.IsEmpty())
{
return Task.CompletedTask;
}
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
index 688a13bc0b..965b7e7e60 100644
--- a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
+++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
@@ -46,7 +47,7 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy
}
var userId = contextUser.GetUserId();
- if (userId.Equals(default))
+ if (userId.IsEmpty())
{
context.Fail();
return Task.CompletedTask;
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index e7d3e694ab..8b931f1621 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -126,7 +127,7 @@ public class ArtistsController : BaseJellyfinApiController
User? user = null;
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
user = _userManager.GetUserById(userId.Value);
}
@@ -330,7 +331,7 @@ public class ArtistsController : BaseJellyfinApiController
User? user = null;
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
user = _userManager.GetUserById(userId.Value);
}
@@ -469,7 +470,7 @@ public class ArtistsController : BaseJellyfinApiController
var item = _libraryManager.GetArtist(name, dtoOptions);
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index fdc16ee23f..f83c71b578 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -126,7 +127,7 @@ public class ChannelsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -201,7 +202,7 @@ public class ChannelsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index baeb8b81a0..d6e043e6a1 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -3,6 +3,7 @@ using System.Linq;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -53,7 +54,7 @@ public class FilterController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -146,7 +147,7 @@ public class FilterController : BaseJellyfinApiController
[FromQuery] bool? recursive)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index 6cb1993e46..54d48aec21 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -95,7 +96,7 @@ public class GenresController : BaseJellyfinApiController
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
- User? user = userId.Value.Equals(default)
+ User? user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -172,7 +173,7 @@ public class GenresController : BaseJellyfinApiController
item ??= new Genre();
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs
index 4dc2a4253d..e7ff1f9868 100644
--- a/Jellyfin.Api/Controllers/InstantMixController.cs
+++ b/Jellyfin.Api/Controllers/InstantMixController.cs
@@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -76,7 +77,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var item = _libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -113,7 +114,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var album = _libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -150,7 +151,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var playlist = (Playlist)_libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -186,7 +187,7 @@ public class InstantMixController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -223,7 +224,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var item = _libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -260,7 +261,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var item = _libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
@@ -334,7 +335,7 @@ public class InstantMixController : BaseJellyfinApiController
{
var item = _libraryManager.GetItemById(id);
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index a1fc8e11b2..d10fba920c 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -245,7 +246,7 @@ public class ItemsController : BaseJellyfinApiController
var isApiKey = User.GetIsApiKey();
// if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
userId = RequestHelpers.GetUserId(User, userId);
- var user = !isApiKey && !userId.Value.Equals(default)
+ var user = !isApiKey && !userId.IsNullOrEmpty()
? _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException()
: null;
@@ -840,7 +841,7 @@ public class ItemsController : BaseJellyfinApiController
var ancestorIds = Array.Empty();
var excludeFolderIds = user.GetPreferenceValues(PreferenceKind.LatestItemExcludes);
- if (parentIdGuid.Equals(default) && excludeFolderIds.Length > 0)
+ if (parentIdGuid.IsEmpty() && excludeFolderIds.Length > 0)
{
ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index de057bbab9..a0bbc961f0 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -146,12 +146,12 @@ public class LibraryController : BaseJellyfinApiController
[FromQuery] bool inheritFromParent = false)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
- var item = itemId.Equals(default)
- ? (userId.Value.Equals(default)
+ var item = itemId.IsEmpty()
+ ? (userId.IsNullOrEmpty()
? _libraryManager.RootFolder
: _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
@@ -213,12 +213,12 @@ public class LibraryController : BaseJellyfinApiController
[FromQuery] bool inheritFromParent = false)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
- var item = itemId.Equals(default)
- ? (userId.Value.Equals(default)
+ var item = itemId.IsEmpty()
+ ? (userId.IsNullOrEmpty()
? _libraryManager.RootFolder
: _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
@@ -339,7 +339,7 @@ public class LibraryController : BaseJellyfinApiController
{
var isApiKey = User.GetIsApiKey();
var userId = User.GetUserId();
- var user = !isApiKey && !userId.Equals(default)
+ var user = !isApiKey && !userId.IsEmpty()
? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException()
: null;
if (!isApiKey && user is null)
@@ -382,7 +382,7 @@ public class LibraryController : BaseJellyfinApiController
{
var isApiKey = User.GetIsApiKey();
var userId = User.GetUserId();
- var user = !isApiKey && !userId.Equals(default)
+ var user = !isApiKey && !userId.IsEmpty()
? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException()
: null;
@@ -428,7 +428,7 @@ public class LibraryController : BaseJellyfinApiController
[FromQuery] bool? isFavorite)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -471,7 +471,7 @@ public class LibraryController : BaseJellyfinApiController
var baseItemDtos = new List();
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -702,8 +702,8 @@ public class LibraryController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
userId = RequestHelpers.GetUserId(User, userId);
- var item = itemId.Equals(default)
- ? (userId.Value.Equals(default)
+ var item = itemId.IsEmpty()
+ ? (userId.IsNullOrEmpty()
? _libraryManager.RootFolder
: _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
@@ -718,7 +718,7 @@ public class LibraryController : BaseJellyfinApiController
return new QueryResult();
}
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 5502836239..1b2f5750f6 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -10,12 +10,12 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LiveTvDtos;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
@@ -43,6 +43,7 @@ namespace Jellyfin.Api.Controllers;
public class LiveTvController : BaseJellyfinApiController
{
private readonly ILiveTvManager _liveTvManager;
+ private readonly ITunerHostManager _tunerHostManager;
private readonly IUserManager _userManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
@@ -55,6 +56,7 @@ public class LiveTvController : BaseJellyfinApiController
/// Initializes a new instance of the class.
///
/// Instance of the interface.
+ /// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
@@ -64,6 +66,7 @@ public class LiveTvController : BaseJellyfinApiController
/// Instance of the interface.
public LiveTvController(
ILiveTvManager liveTvManager,
+ ITunerHostManager tunerHostManager,
IUserManager userManager,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
@@ -73,6 +76,7 @@ public class LiveTvController : BaseJellyfinApiController
ITranscodeManager transcodeManager)
{
_liveTvManager = liveTvManager;
+ _tunerHostManager = tunerHostManager;
_userManager = userManager;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
@@ -179,7 +183,7 @@ public class LiveTvController : BaseJellyfinApiController
dtoOptions,
CancellationToken.None);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -211,10 +215,10 @@ public class LiveTvController : BaseJellyfinApiController
public ActionResult GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
- var item = channelId.Equals(default)
+ var item = channelId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(channelId);
@@ -384,7 +388,7 @@ public class LiveTvController : BaseJellyfinApiController
public async Task>> GetRecordingFolders([FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var folders = await _liveTvManager.GetRecordingFoldersAsync(user).ConfigureAwait(false);
@@ -407,10 +411,10 @@ public class LiveTvController : BaseJellyfinApiController
public ActionResult GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
- var item = recordingId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId);
+ var item = recordingId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId);
var dtoOptions = new DtoOptions()
.AddClientFields(User);
@@ -564,7 +568,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery] bool enableTotalRecordCount = true)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -591,7 +595,7 @@ public class LiveTvController : BaseJellyfinApiController
GenreIds = genreIds
};
- if (librarySeriesId.HasValue && !librarySeriesId.Equals(default))
+ if (!librarySeriesId.IsNullOrEmpty())
{
query.IsSeries = true;
@@ -620,7 +624,7 @@ public class LiveTvController : BaseJellyfinApiController
[Authorize(Policy = Policies.LiveTvAccess)]
public async Task>> GetPrograms([FromBody] GetProgramsDto body)
{
- var user = body.UserId.Equals(default) ? null : _userManager.GetUserById(body.UserId);
+ var user = body.UserId.IsEmpty() ? null : _userManager.GetUserById(body.UserId);
var query = new InternalItemsQuery(user)
{
@@ -645,7 +649,7 @@ public class LiveTvController : BaseJellyfinApiController
GenreIds = body.GenreIds
};
- if (!body.LibrarySeriesId.Equals(default))
+ if (!body.LibrarySeriesId.IsEmpty())
{
query.IsSeries = true;
@@ -704,7 +708,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery] bool enableTotalRecordCount = true)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -743,7 +747,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -951,9 +955,7 @@ public class LiveTvController : BaseJellyfinApiController
[Authorize(Policy = Policies.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
- {
- return await _liveTvManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
- }
+ => await _tunerHostManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
///
/// Deletes a tuner host.
@@ -1130,10 +1132,8 @@ public class LiveTvController : BaseJellyfinApiController
[HttpGet("TunerHosts/Types")]
[Authorize(Policy = Policies.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult> GetTunerHostTypes()
- {
- return _liveTvManager.GetTunerHostTypes();
- }
+ public IEnumerable GetTunerHostTypes()
+ => _tunerHostManager.GetTunerHostTypes();
///
/// Discover tuners.
@@ -1145,10 +1145,8 @@ public class LiveTvController : BaseJellyfinApiController
[HttpGet("Tuners/Discover")]
[Authorize(Policy = Policies.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task>> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
- {
- return await _liveTvManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false);
- }
+ public IAsyncEnumerable DiscoverTuners([FromQuery] bool newDevicesOnly = false)
+ => _tunerHostManager.DiscoverTuners(newDevicesOnly);
///
/// Gets a live tv recording stream.
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index e1145481fa..471bcd096e 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -7,6 +7,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@@ -69,7 +70,7 @@ public class MoviesController : BaseJellyfinApiController
[FromQuery] int itemLimit = 8)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index 69b9042646..5411baa3e7 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -95,7 +96,7 @@ public class MusicGenresController : BaseJellyfinApiController
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
- User? user = userId.Value.Equals(default)
+ User? user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -164,7 +165,7 @@ public class MusicGenresController : BaseJellyfinApiController
return NotFound();
}
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index b4c6f490a0..6ca3086015 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -83,7 +84,7 @@ public class PersonsController : BaseJellyfinApiController
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = userId.Value.Equals(default)
+ User? user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -129,7 +130,7 @@ public class PersonsController : BaseJellyfinApiController
return NotFound();
}
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
var user = _userManager.GetUserById(userId.Value);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index c4c89ccde0..921cc6031f 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -9,6 +9,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.PlaylistDtos;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
@@ -188,7 +189,7 @@ public class PlaylistsController : BaseJellyfinApiController
return NotFound();
}
- var user = userId.Equals(default)
+ var user = userId.IsEmpty()
? null
: _userManager.GetUserById(userId);
diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs
index 5b45941657..413b7b8340 100644
--- a/Jellyfin.Api/Controllers/SearchController.cs
+++ b/Jellyfin.Api/Controllers/SearchController.cs
@@ -209,7 +209,7 @@ public class SearchController : BaseJellyfinApiController
break;
}
- if (!item.ChannelId.Equals(default))
+ if (!item.ChannelId.IsEmpty())
{
var channel = _libraryManager.GetItemById(item.ChannelId);
result.ChannelName = channel?.Name;
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 083515a949..52b58b8f10 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -10,6 +10,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.SessionDtos;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
@@ -71,7 +72,7 @@ public class SessionController : BaseJellyfinApiController
result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
}
- if (controllableByUserId.HasValue && !controllableByUserId.Equals(default))
+ if (!controllableByUserId.IsNullOrEmpty())
{
result = result.Where(i => i.SupportsRemoteControl);
@@ -83,12 +84,12 @@ public class SessionController : BaseJellyfinApiController
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
- result = result.Where(i => i.UserId.Equals(default) || i.ContainsUser(controllableByUserId.Value));
+ result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(controllableByUserId.Value));
}
if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
- result = result.Where(i => !i.UserId.Equals(default));
+ result = result.Where(i => !i.UserId.IsEmpty());
}
result = result.Where(i =>
diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs
index f434f60f51..708fc7436f 100644
--- a/Jellyfin.Api/Controllers/StudiosController.cs
+++ b/Jellyfin.Api/Controllers/StudiosController.cs
@@ -5,6 +5,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -91,7 +92,7 @@ public class StudiosController : BaseJellyfinApiController
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = userId.Value.Equals(default)
+ User? user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -144,7 +145,7 @@ public class StudiosController : BaseJellyfinApiController
var dtoOptions = new DtoOptions().AddClientFields(User);
var item = _libraryManager.GetStudio(name);
- if (!userId.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index 675757fc51..2aa6d25a79 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -62,7 +63,7 @@ public class SuggestionsController : BaseJellyfinApiController
[FromQuery] int? limit,
[FromQuery] bool enableTotalRecordCount = false)
{
- var user = userId.Equals(default)
+ var user = userId.IsEmpty()
? null
: _userManager.GetUserById(userId);
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index 55a30d4692..3d84b61bf4 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -111,7 +111,7 @@ public class TvShowsController : BaseJellyfinApiController
},
options);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -150,7 +150,7 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] bool? enableUserData)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -222,7 +222,7 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] ItemSortBy? sortBy)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
@@ -284,7 +284,7 @@ public class TvShowsController : BaseJellyfinApiController
}
// This must be the last filter
- if (adjacentTo.HasValue && !adjacentTo.Value.Equals(default))
+ if (!adjacentTo.IsNullOrEmpty())
{
episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList();
}
@@ -339,7 +339,7 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] bool? enableUserData)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index f9f27f1480..ea10ee24f9 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.UserDtos;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -532,7 +533,7 @@ public class UserController : BaseJellyfinApiController
public ActionResult GetCurrentUser()
{
var userId = User.GetUserId();
- if (userId.Equals(default))
+ if (userId.IsEmpty())
{
return BadRequest();
}
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index 2c4fe91862..264e0a3dbd 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -84,7 +85,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -145,7 +146,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -185,7 +186,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -221,7 +222,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -257,7 +258,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -294,7 +295,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -330,7 +331,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -375,7 +376,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -558,7 +559,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound();
}
- var item = itemId.Equals(default)
+ var item = itemId.IsEmpty()
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index c231c147fc..e6c3198698 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -11,6 +11,7 @@ using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
@@ -96,12 +97,12 @@ public class VideosController : BaseJellyfinApiController
public ActionResult> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- var user = userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
- var item = itemId.Equals(default)
- ? (userId.Value.Equals(default)
+ var item = itemId.IsEmpty()
+ ? (userId.IsNullOrEmpty()
? _libraryManager.RootFolder
: _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs
index ca46c38c59..e4aa0ea42d 100644
--- a/Jellyfin.Api/Controllers/YearsController.cs
+++ b/Jellyfin.Api/Controllers/YearsController.cs
@@ -90,7 +90,7 @@ public class YearsController : BaseJellyfinApiController
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = userId.Value.Equals(default)
+ User? user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
@@ -110,7 +110,7 @@ public class YearsController : BaseJellyfinApiController
{
var folder = (Folder)parentItem;
- if (userId.Equals(default))
+ if (userId.IsNullOrEmpty())
{
items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToList();
}
@@ -182,7 +182,7 @@ public class YearsController : BaseJellyfinApiController
var dtoOptions = new DtoOptions()
.AddClientFields(User);
- if (!userId.Value.Equals(default))
+ if (!userId.IsNullOrEmpty())
{
var user = _userManager.GetUserById(userId.Value);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index 321987ca74..6a24ad32ab 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -86,7 +87,7 @@ public class MediaInfoHelper
string? mediaSourceId = null,
string? liveStreamId = null)
{
- var user = userId is null || userId.Value.Equals(default)
+ var user = userId.IsNullOrEmpty()
? null
: _userManager.GetUserById(userId.Value);
var item = _libraryManager.GetItemById(id);
diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs
index be3d4dfb67..429e972132 100644
--- a/Jellyfin.Api/Helpers/RequestHelpers.cs
+++ b/Jellyfin.Api/Helpers/RequestHelpers.cs
@@ -7,6 +7,7 @@ using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -67,7 +68,7 @@ public static class RequestHelpers
var authenticatedUserId = claimsPrincipal.GetUserId();
// UserId not provided, fall back to authenticated user id.
- if (userId is null || userId.Value.Equals(default))
+ if (userId.IsNullOrEmpty())
{
return authenticatedUserId;
}
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 78943f7b58..7a3842a9f7 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -82,7 +82,7 @@ public static class StreamingHelpers
};
var userId = httpContext.User.GetUserId();
- if (!userId.Equals(default))
+ if (!userId.IsEmpty())
{
state.User = userManager.GetUserById(userId);
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index cc464a7a3d..c4a2bfdb87 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -11,6 +11,7 @@ using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Data.Events.Users;
+using Jellyfin.Extensions;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -117,7 +118,7 @@ namespace Jellyfin.Server.Implementations.Users
///
public User? GetUserById(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 5192b9e21b..d5b6e93b8e 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -7,7 +7,6 @@ using Jellyfin.Api.WebSocketListeners;
using Jellyfin.Drawing;
using Jellyfin.Drawing.Skia;
using Jellyfin.LiveTv;
-using Jellyfin.LiveTv.Channels;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
using Jellyfin.Server.Implementations.Devices;
@@ -18,18 +17,15 @@ using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.BaseItemManager;
-using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Lyric;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -101,11 +97,6 @@ namespace Jellyfin.Server
serviceCollection.AddScoped();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
-
foreach (var type in GetExportTypes())
{
serviceCollection.AddSingleton(typeof(ILyricProvider), type);
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 1030c6f5fc..7d5f22545d 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -5,6 +5,7 @@ using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using Jellyfin.Api.Middleware;
+using Jellyfin.LiveTv.Extensions;
using Jellyfin.MediaEncoding.Hls.Extensions;
using Jellyfin.Networking;
using Jellyfin.Networking.HappyEyeballs;
@@ -121,6 +122,7 @@ namespace Jellyfin.Server
.AddCheck>(nameof(JellyfinDbContext));
services.AddHlsPlaylistGenerator();
+ services.AddLiveTvServices();
services.AddHostedService();
}
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 8eb27888ab..c8b432ecb2 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -95,12 +95,5 @@ namespace MediaBrowser.Controller.Channels
/// The cancellation token.
/// The item media sources.
IEnumerable GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
-
- ///
- /// Whether the item supports media probe.
- ///
- /// The item.
- /// Whether media probe should be enabled.
- bool EnableMediaProbe(BaseItem item);
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index d789033f16..b225f22df0 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -184,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
/// The id is empty.
public BaseItem FindVirtualChild(Guid id)
{
- if (id.Equals(default))
+ if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 18d948a62f..11cdf84445 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo
{
[JsonIgnore]
- public bool IsAccessedByName => ParentId.Equals(default);
+ public bool IsAccessedByName => ParentId.IsEmpty();
[JsonIgnore]
public override bool IsFolder => !IsAccessedByName;
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index fdbceac961..ddcc994a0b 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -240,7 +240,7 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (!ChannelId.Equals(default))
+ if (!ChannelId.IsEmpty())
{
return SourceType.Channel;
}
@@ -530,7 +530,7 @@ namespace MediaBrowser.Controller.Entities
get
{
var id = DisplayParentId;
- if (id.Equals(default))
+ if (id.IsEmpty())
{
return null;
}
@@ -746,7 +746,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool StopRefreshIfLocalMetadataFound => true;
[JsonIgnore]
- protected virtual bool SupportsOwnedItems => !ParentId.Equals(default) && IsFileProtocol;
+ protected virtual bool SupportsOwnedItems => !ParentId.IsEmpty() && IsFileProtocol;
[JsonIgnore]
public virtual bool SupportsPeople => false;
@@ -823,7 +823,7 @@ namespace MediaBrowser.Controller.Entities
public BaseItem GetOwner()
{
var ownerId = OwnerId;
- return ownerId.Equals(default) ? null : LibraryManager.GetItemById(ownerId);
+ return ownerId.IsEmpty() ? null : LibraryManager.GetItemById(ownerId);
}
public bool CanDelete(User user, List allCollectionFolders)
@@ -968,7 +968,7 @@ namespace MediaBrowser.Controller.Entities
public BaseItem GetParent()
{
var parentId = ParentId;
- if (parentId.Equals(default))
+ if (parentId.IsEmpty())
{
return null;
}
@@ -1361,7 +1361,7 @@ namespace MediaBrowser.Controller.Entities
var tasks = extras.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
- if (!i.OwnerId.Equals(ownerId) || !i.ParentId.Equals(default))
+ if (!i.OwnerId.Equals(ownerId) || !i.ParentId.IsEmpty())
{
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
@@ -1673,7 +1673,7 @@ namespace MediaBrowser.Controller.Entities
// First get using the cached Id
if (info.ItemId.HasValue)
{
- if (info.ItemId.Value.Equals(default))
+ if (info.ItemId.Value.IsEmpty())
{
return null;
}
@@ -2439,7 +2439,7 @@ namespace MediaBrowser.Controller.Entities
return Task.FromResult(true);
}
- if (video.OwnerId.Equals(default))
+ if (video.OwnerId.IsEmpty())
{
video.OwnerId = this.Id;
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index e707eedbf6..74eb089de3 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
@@ -198,7 +199,7 @@ namespace MediaBrowser.Controller.Entities
{
item.SetParent(this);
- if (item.Id.Equals(default))
+ if (item.Id.IsEmpty())
{
item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
}
@@ -697,7 +698,7 @@ namespace MediaBrowser.Controller.Entities
if (this is not UserRootFolder
&& this is not AggregateFolder
- && query.ParentId.Equals(default))
+ && query.ParentId.IsEmpty())
{
query.Parent = this;
}
@@ -840,7 +841,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
Logger.LogDebug("Query requires post-filtering due to AdjacentTo");
return true;
@@ -987,7 +988,7 @@ namespace MediaBrowser.Controller.Entities
#pragma warning restore CA1309
// This must be the last filter
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index bf31508c1d..37e2414142 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -8,6 +8,7 @@ using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -74,12 +75,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seriesId = SeriesId;
- if (seriesId.Equals(default))
+ if (seriesId.IsEmpty())
{
seriesId = FindSeriesId();
}
- return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series);
+ return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series);
}
}
@@ -89,12 +90,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seasonId = SeasonId;
- if (seasonId.Equals(default))
+ if (seasonId.IsEmpty())
{
seasonId = FindSeasonId();
}
- return seasonId.Equals(default) ? null : (LibraryManager.GetItemById(seasonId) as Season);
+ return seasonId.IsEmpty() ? null : (LibraryManager.GetItemById(seasonId) as Season);
}
}
@@ -271,7 +272,7 @@ namespace MediaBrowser.Controller.Entities.TV
var seasonId = SeasonId;
- if (!seasonId.Equals(default) && !list.Contains(seasonId))
+ if (!seasonId.IsEmpty() && !list.Contains(seasonId))
{
list.Add(seasonId);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 0a040a3c28..c29cefc15e 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Querying;
@@ -48,12 +49,12 @@ namespace MediaBrowser.Controller.Entities.TV
get
{
var seriesId = SeriesId;
- if (seriesId.Equals(default))
+ if (seriesId.IsEmpty())
{
seriesId = FindSeriesId();
}
- return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series);
+ return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series);
}
}
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index eb026deb4f..c93488a85e 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -70,11 +70,11 @@ namespace MediaBrowser.Controller.Entities
///
public override IEnumerable GetIdsForAncestorQuery()
{
- if (!DisplayParentId.Equals(default))
+ if (!DisplayParentId.IsEmpty())
{
yield return DisplayParentId;
}
- else if (!ParentId.Equals(default))
+ else if (!ParentId.IsEmpty())
{
yield return ParentId;
}
@@ -95,11 +95,11 @@ namespace MediaBrowser.Controller.Entities
{
var parent = this as Folder;
- if (!DisplayParentId.Equals(default))
+ if (!DisplayParentId.IsEmpty())
{
parent = LibraryManager.GetItemById(DisplayParentId) as Folder ?? parent;
}
- else if (!ParentId.Equals(default))
+ else if (!ParentId.IsEmpty())
{
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index a3525c8623..4af000557e 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -433,7 +433,7 @@ namespace MediaBrowser.Controller.Entities
var user = query.User;
// This must be the last filter
- if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default))
+ if (!query.AdjacentTo.IsNullOrEmpty())
{
items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value);
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index be2eb4d288..5adadec390 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -456,7 +456,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var child in LinkedAlternateVersions)
{
// Reset the cached value
- if (child.ItemId.HasValue && child.ItemId.Value.Equals(default))
+ if (child.ItemId.IsNullOrEmpty())
{
child.ItemId = null;
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 4206159e7b..26f9fe42d3 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -71,9 +71,8 @@ namespace MediaBrowser.Controller.LiveTv
/// Adds the parts.
///
/// The services.
- /// The tuner hosts.
/// The listing providers.
- void AddParts(IEnumerable services, IEnumerable tunerHosts, IEnumerable listingProviders);
+ void AddParts(IEnumerable services, IEnumerable listingProviders);
///
/// Gets the timer.
@@ -253,14 +252,6 @@ namespace MediaBrowser.Controller.LiveTv
/// Task.
Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList fields, User user = null);
- ///
- /// Saves the tuner host.
- ///
- /// Turner host to save.
- /// Option to specify that data source has changed.
- /// Tuner host information wrapped in a task.
- Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
-
///
/// Saves the listing provider.
///
@@ -298,10 +289,6 @@ namespace MediaBrowser.Controller.LiveTv
Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
- List GetTunerHostTypes();
-
- Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
-
string GetEmbyTvActiveRecordingPath(string id);
ActiveRecordingInfo GetActiveRecordingInfo(string path);
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
index ce34954e3f..52fb156481 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs
@@ -140,14 +140,6 @@ namespace MediaBrowser.Controller.LiveTv
/// Task.
Task CloseLiveStream(string id, CancellationToken cancellationToken);
- ///
- /// Records the live stream.
- ///
- /// The identifier.
- /// The cancellation token.
- /// Task.
- Task RecordLiveStream(string id, CancellationToken cancellationToken);
-
///
/// Resets the tuner.
///
@@ -180,9 +172,4 @@ namespace MediaBrowser.Controller.LiveTv
{
Task GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List currentLiveStreams, CancellationToken cancellationToken);
}
-
- public interface ISupportsUpdatingDefaults
- {
- Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken);
- }
}
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index b983091588..3689a2adf6 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -35,13 +35,6 @@ namespace MediaBrowser.Controller.LiveTv
/// Task<IEnumerable<ChannelInfo>>.
Task> GetChannels(bool enableCache, CancellationToken cancellationToken);
- ///
- /// Gets the tuner infos.
- ///
- /// The cancellation token.
- /// Task<List<LiveTvTunerInfo>>.
- Task> GetTunerInfos(CancellationToken cancellationToken);
-
///
/// Gets the channel stream.
///
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
new file mode 100644
index 0000000000..3df6066f66
--- /dev/null
+++ b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+
+namespace MediaBrowser.Controller.LiveTv;
+
+///
+/// Service responsible for managing the s.
+///
+public interface ITunerHostManager
+{
+ ///
+ /// Gets the available s.
+ ///
+ IReadOnlyList TunerHosts { get; }
+
+ ///
+ /// Gets the s for the available s.
+ ///
+ /// The s.
+ IEnumerable GetTunerHostTypes();
+
+ ///
+ /// Saves the tuner host.
+ ///
+ /// Turner host to save.
+ /// Option to specify that data source has changed.
+ /// Tuner host information wrapped in a task.
+ Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
+
+ ///
+ /// Discovers the available tuners.
+ ///
+ /// A value indicating whether to only return new devices.
+ /// The s.
+ IAsyncEnumerable DiscoverTuners(bool newDevicesOnly);
+
+ ///
+ /// Scans for tuner devices that have changed URLs.
+ ///
+ /// The to use.
+ /// A task that represents the scanning operation.
+ Task ScanForTunerDeviceChanges(CancellationToken cancellationToken);
+}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
deleted file mode 100644
index eb3babc180..0000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvServiceStatusInfo
- {
- public LiveTvServiceStatusInfo()
- {
- Tuners = new List();
- IsVisible = true;
- }
-
- ///
- /// Gets or sets the status.
- ///
- /// The status.
- public LiveTvServiceStatus Status { get; set; }
-
- ///
- /// Gets or sets the status message.
- ///
- /// The status message.
- public string StatusMessage { get; set; }
-
- ///
- /// Gets or sets the version.
- ///
- /// The version.
- public string Version { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance has update available.
- ///
- /// true if this instance has update available; otherwise, false.
- public bool HasUpdateAvailable { get; set; }
-
- ///
- /// Gets or sets the tuners.
- ///
- /// The tuners.
- public List Tuners { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is visible.
- ///
- /// true if this instance is visible; otherwise, false.
- public bool IsVisible { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
deleted file mode 100644
index aa5eb59d16..0000000000
--- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class LiveTvTunerInfo
- {
- public LiveTvTunerInfo()
- {
- Clients = new List();
- }
-
- ///
- /// Gets or sets the type of the source.
- ///
- /// The type of the source.
- public string SourceType { get; set; }
-
- ///
- /// Gets or sets the name.
- ///
- /// The name.
- public string Name { get; set; }
-
- ///
- /// Gets or sets the identifier.
- ///
- /// The identifier.
- public string Id { get; set; }
-
- ///
- /// Gets or sets the URL.
- ///
- /// The URL.
- public string Url { get; set; }
-
- ///
- /// Gets or sets the status.
- ///
- /// The status.
- public LiveTvTunerStatus Status { get; set; }
-
- ///
- /// Gets or sets the channel identifier.
- ///
- /// The channel identifier.
- public string ChannelId { get; set; }
-
- ///
- /// Gets or sets the recording identifier.
- ///
- /// The recording identifier.
- public string RecordingId { get; set; }
-
- ///
- /// Gets or sets the name of the program.
- ///
- /// The name of the program.
- public string ProgramName { get; set; }
-
- ///
- /// Gets or sets the clients.
- ///
- /// The clients.
- public List Clients { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance can reset.
- ///
- /// true if this instance can reset; otherwise, false.
- public bool CanReset { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
deleted file mode 100644
index 1dcf7a58fe..0000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingInfo
- {
- public RecordingInfo()
- {
- Genres = new List();
- }
-
- ///
- /// Gets or sets the id of the recording.
- ///
- public string Id { get; set; }
-
- ///
- /// Gets or sets the series timer identifier.
- ///
- /// The series timer identifier.
- public string SeriesTimerId { get; set; }
-
- ///
- /// Gets or sets the timer identifier.
- ///
- /// The timer identifier.
- public string TimerId { get; set; }
-
- ///
- /// Gets or sets the channelId of the recording.
- ///
- public string ChannelId { get; set; }
-
- ///
- /// Gets or sets the type of the channel.
- ///
- /// The type of the channel.
- public ChannelType ChannelType { get; set; }
-
- ///
- /// Gets or sets the name of the recording.
- ///
- public string Name { get; set; }
-
- ///
- /// Gets or sets the path.
- ///
- /// The path.
- public string Path { get; set; }
-
- ///
- /// Gets or sets the URL.
- ///
- /// The URL.
- public string Url { get; set; }
-
- ///
- /// Gets or sets the overview.
- ///
- /// The overview.
- public string Overview { get; set; }
-
- ///
- /// Gets or sets the start date of the recording, in UTC.
- ///
- public DateTime StartDate { get; set; }
-
- ///
- /// Gets or sets the end date of the recording, in UTC.
- ///
- public DateTime EndDate { get; set; }
-
- ///
- /// Gets or sets the program identifier.
- ///
- /// The program identifier.
- public string ProgramId { get; set; }
-
- ///
- /// Gets or sets the status.
- ///
- /// The status.
- public RecordingStatus Status { get; set; }
-
- ///
- /// Gets or sets the genre of the program.
- ///
- public List Genres { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is repeat.
- ///
- /// true if this instance is repeat; otherwise, false.
- public bool IsRepeat { get; set; }
-
- ///
- /// Gets or sets the episode title.
- ///
- /// The episode title.
- public string EpisodeTitle { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is hd.
- ///
- /// true if this instance is hd; otherwise, false.
- public bool? IsHD { get; set; }
-
- ///
- /// Gets or sets the audio.
- ///
- /// The audio.
- public ProgramAudio? Audio { get; set; }
-
- ///
- /// Gets or sets the original air date.
- ///
- /// The original air date.
- public DateTime? OriginalAirDate { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is movie.
- ///
- /// true if this instance is movie; otherwise, false.
- public bool IsMovie { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is sports.
- ///
- /// true if this instance is sports; otherwise, false.
- public bool IsSports { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is series.
- ///
- /// true if this instance is series; otherwise, false.
- public bool IsSeries { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is live.
- ///
- /// true if this instance is live; otherwise, false.
- public bool IsLive { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is news.
- ///
- /// true if this instance is news; otherwise, false.
- public bool IsNews { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is kids.
- ///
- /// true if this instance is kids; otherwise, false.
- public bool IsKids { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is premiere.
- ///
- /// true if this instance is premiere; otherwise, false.
- public bool IsPremiere { get; set; }
-
- ///
- /// Gets or sets the official rating.
- ///
- /// The official rating.
- public string OfficialRating { get; set; }
-
- ///
- /// Gets or sets the community rating.
- ///
- /// The community rating.
- public float? CommunityRating { get; set; }
-
- ///
- /// Gets or sets the image path if it can be accessed directly from the file system.
- ///
- /// The image path.
- public string ImagePath { get; set; }
-
- ///
- /// Gets or sets the image url if it can be downloaded.
- ///
- /// The image URL.
- public string ImageUrl { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance has image.
- ///
- /// null if [has image] contains no value, true if [has image]; otherwise, false.
- public bool? HasImage { get; set; }
-
- ///
- /// Gets or sets the show identifier.
- ///
- /// The show identifier.
- public string ShowId { get; set; }
-
- ///
- /// Gets or sets the date last updated.
- ///
- /// The date last updated.
- public DateTime DateLastUpdated { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
deleted file mode 100644
index 0b943c9396..0000000000
--- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Controller.LiveTv
-{
- public class RecordingStatusChangedEventArgs : EventArgs
- {
- public string RecordingId { get; set; }
-
- public RecordingStatus NewStatus { get; set; }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs
index 483d0a1d82..d79e4441aa 100644
--- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs
+++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs
@@ -9,6 +9,7 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
@@ -400,7 +401,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
if (state.VideoRequest is not null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
- var user = userId.Equals(default) ? null : _userManager.GetUserById(userId);
+ var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId);
if (user is not null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state);
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index bf18d46dcd..da683a17e6 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
@@ -1536,7 +1537,7 @@ namespace MediaBrowser.Model.Dlna
private static void ValidateMediaOptions(MediaOptions options, bool isMediaSource)
{
- if (options.ItemId.Equals(default))
+ if (options.ItemId.IsEmpty())
{
ArgumentException.ThrowIfNullOrEmpty(options.DeviceId);
}
diff --git a/MediaBrowser.Model/IO/IStreamHelper.cs b/MediaBrowser.Model/IO/IStreamHelper.cs
index f900da5567..034a6bf8bf 100644
--- a/MediaBrowser.Model/IO/IStreamHelper.cs
+++ b/MediaBrowser.Model/IO/IStreamHelper.cs
@@ -13,8 +13,6 @@ namespace MediaBrowser.Model.IO
Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken);
- Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken);
-
Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
deleted file mode 100644
index 80a6461957..0000000000
--- a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.LiveTv
-{
- public enum LiveTvTunerStatus
- {
- Available = 0,
- Disabled = 1,
- RecordingTv = 2,
- LiveTv = 3
- }
-}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 4ba8844182..b530b9de3f 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -706,7 +706,7 @@ namespace MediaBrowser.Providers.Manager
{
BaseItem? referenceItem = null;
- if (!searchInfo.ItemId.Equals(default))
+ if (!searchInfo.ItemId.IsEmpty())
{
referenceItem = _libraryManager.GetItemById(searchInfo.ItemId);
}
@@ -944,7 +944,7 @@ namespace MediaBrowser.Providers.Manager
public void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority)
{
ArgumentNullException.ThrowIfNull(itemId);
- if (itemId.Equals(default))
+ if (itemId.IsEmpty())
{
throw new ArgumentException("Guid can't be empty", nameof(itemId));
}
diff --git a/bump_version b/bump_version
index 41d27f5c8a..dd55e62c79 100755
--- a/bump_version
+++ b/bump_version
@@ -21,7 +21,7 @@ fi
shared_version_file="./SharedVersion.cs"
build_file="./build.yaml"
# csproj files for nuget packages
-jellyfin_subprojects=(
+jellyfin_subprojects=(
MediaBrowser.Common/MediaBrowser.Common.csproj
Jellyfin.Data/Jellyfin.Data.csproj
MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -97,7 +97,7 @@ cat ${debian_changelog_file} >> ${debian_changelog_temp}
# Move into place
mv ${debian_changelog_temp} ${debian_changelog_file}
-# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting
+# Write out a temporary Dnf changelog with our new stuff prepended and some templated formatting
fedora_spec_file="fedora/jellyfin.spec"
fedora_changelog_temp="$( mktemp )"
fedora_spec_temp_dir="$( mktemp -d )"
diff --git a/debian/rules b/debian/rules
index 069d48aad1..79cd55a15d 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,27 +7,27 @@ HOST_ARCH := $(shell arch)
BUILD_ARCH := ${DEB_HOST_MULTIARCH}
ifeq ($(HOST_ARCH),x86_64)
# Building AMD64
- DOTNETRUNTIME := debian-x64
+ DOTNETRUNTIME := linux-x64
ifeq ($(BUILD_ARCH),arm-linux-gnueabihf)
# Cross-building ARM on AMD64
- DOTNETRUNTIME := debian-arm
+ DOTNETRUNTIME := linux-arm
endif
ifeq ($(BUILD_ARCH),aarch64-linux-gnu)
# Cross-building ARM on AMD64
- DOTNETRUNTIME := debian-arm64
+ DOTNETRUNTIME := linux-arm64
endif
endif
ifeq ($(HOST_ARCH),armv7l)
# Building ARM
- DOTNETRUNTIME := debian-arm
+ DOTNETRUNTIME := linux-arm
endif
ifeq ($(HOST_ARCH),arm64)
# Building ARM
- DOTNETRUNTIME := debian-arm64
+ DOTNETRUNTIME := linux-arm64
endif
ifeq ($(HOST_ARCH),aarch64)
# Building ARM
- DOTNETRUNTIME := debian-arm64
+ DOTNETRUNTIME := linux-arm64
endif
export DH_VERBOSE=1
diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64
index 7c9bbf39e9..3db184f494 100644
--- a/deployment/Dockerfile.centos.amd64
+++ b/deployment/Dockerfile.centos.amd64
@@ -1,29 +1,36 @@
-FROM centos:7
+FROM quay.io/centos/centos:stream9
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
ENV IS_DOCKER=YES
# Prepare CentOS environment
-RUN yum update -yq \
- && yum install -yq epel-release \
- && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
+RUN dnf update -yq \
+ && dnf install -yq epel-release \
+ && dnf install -yq \
+ rpmdevtools libcurl-devel fontconfig-devel \
+ freetype-devel openssl-devel glibc-devel \
+ libicu-devel git wget dnf-plugins-core \
+ && dnf clean all \
+ && rm -rf /var/cache/dnf
# Install DotNET SDK
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
- && mkdir -p dotnet-sdk \
- && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
- && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
# Create symlinks and directories
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \
- && mkdir -p ${SOURCE_DIR}/SPECS \
- && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
- && mkdir -p ${SOURCE_DIR}/SOURCES \
- && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES
+ && mkdir -p ${SOURCE_DIR}/SPECS \
+ && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
+ && mkdir -p ${SOURCE_DIR}/SOURCES \
+ && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES
VOLUME ${SOURCE_DIR}/
diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64
index d344c59646..da0c9dabd3 100644
--- a/deployment/Dockerfile.debian.amd64
+++ b/deployment/Dockerfile.debian.amd64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -10,11 +14,14 @@ ENV ARCH=amd64
ENV IS_DOCKER=YES
# Prepare Debian build environment
-RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+RUN apt-get update -yq \
+ && apt-get install --no-install-recommends -yq \
debhelper gnupg devscripts build-essential mmv \
- libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev \
- libssl1.1 liblttng-ust0
+ libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev \
+ libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yq \
+ && apt-get autoremove -yq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.amd64 /build.sh
diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64
index 8a5411f059..6c4cb816f5 100644
--- a/deployment/Dockerfile.debian.arm64
+++ b/deployment/Dockerfile.debian.arm64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,23 +15,26 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts build-essential mmv
# Prepare the cross-toolchain
RUN dpkg --add-architecture arm64 \
- && apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends cross-gcc-dev \
- && TARGET_LIST="arm64" cross-gcc-gensource 9 \
- && cd cross-gcc-packages-amd64/cross-gcc-9-arm64 \
- && apt-get install -yqq --no-install-recommends \
- gcc-9-source libstdc++-9-dev-arm64-cross \
+ && apt-get update -yqq \
+ && apt-get install --no-install-recommends -yqq cross-gcc-dev \
+ && TARGET_LIST="arm64" cross-gcc-gensource 12 \
+ && cd cross-gcc-packages-amd64/cross-gcc-12-arm64 \
+ && apt-get install --no-install-recommends -yqq \
+ gcc-12-source libstdc++-12-dev-arm64-cross \
binutils-aarch64-linux-gnu bison flex libtool \
gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip \
libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 \
libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 \
- libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-9-dev:arm64
+ libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust1:arm64 libstdc++-12-dev:arm64 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh
diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf
index e95ba16962..b1fa6cee52 100644
--- a/deployment/Dockerfile.debian.armhf
+++ b/deployment/Dockerfile.debian.armhf
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,24 +15,27 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts build-essential mmv
# Prepare the cross-toolchain
RUN dpkg --add-architecture armhf \
- && apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends cross-gcc-dev \
- && TARGET_LIST="armhf" cross-gcc-gensource 9 \
- && cd cross-gcc-packages-amd64/cross-gcc-9-armhf \
- && apt-get install -yqq --no-install-recommends\
- gcc-9-source libstdc++-9-dev-armhf-cross \
+ && apt-get update -yqq \
+ && apt-get install --no-install-recommends -yqq cross-gcc-dev \
+ && TARGET_LIST="armhf" cross-gcc-gensource 12 \
+ && cd cross-gcc-packages-amd64/cross-gcc-12-armhf \
+ && apt-get install --no-install-recommends -yqq \
+ gcc-12-source libstdc++-12-dev-armhf-cross \
binutils-aarch64-linux-gnu bison flex libtool gdb \
sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
zip binutils-arm-linux-gnueabihf libc6-dev:armhf \
linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \
libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf \
- liblttng-ust0:armhf libstdc++-9-dev:armhf
+ liblttng-ust1:armhf libstdc++-12-dev:armhf \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh
diff --git a/deployment/Dockerfile.docker.amd64 b/deployment/Dockerfile.docker.amd64
index 1749ca563c..ca16a08fbc 100644
--- a/deployment/Dockerfile.docker.amd64
+++ b/deployment/Dockerfile.docker.amd64
@@ -1,13 +1,12 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
ARG SOURCE_DIR=/src
ARG ARTIFACT_DIR=/jellyfin
WORKDIR ${SOURCE_DIR}
COPY . .
-
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# because of changes in docker and systemd we need to not build in parallel at the moment
-# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none
+RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none
diff --git a/deployment/Dockerfile.docker.arm64 b/deployment/Dockerfile.docker.arm64
index bbddb61e4d..6e0f7d18e9 100644
--- a/deployment/Dockerfile.docker.arm64
+++ b/deployment/Dockerfile.docker.arm64
@@ -1,13 +1,12 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
ARG SOURCE_DIR=/src
ARG ARTIFACT_DIR=/jellyfin
WORKDIR ${SOURCE_DIR}
COPY . .
-
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# because of changes in docker and systemd we need to not build in parallel at the moment
-# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none
+RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none
diff --git a/deployment/Dockerfile.docker.armhf b/deployment/Dockerfile.docker.armhf
index 3de1d68878..44fb705e6d 100644
--- a/deployment/Dockerfile.docker.armhf
+++ b/deployment/Dockerfile.docker.armhf
@@ -1,13 +1,12 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
ARG SOURCE_DIR=/src
ARG ARTIFACT_DIR=/jellyfin
WORKDIR ${SOURCE_DIR}
COPY . .
-
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-# because of changes in docker and systemd we need to not build in parallel at the moment
-# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
-RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none
+RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none
diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64
index 66ead37d7d..75a6d1e649 100644
--- a/deployment/Dockerfile.fedora.amd64
+++ b/deployment/Dockerfile.fedora.amd64
@@ -1,7 +1,9 @@
FROM fedora:39
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -9,21 +11,26 @@ ENV IS_DOCKER=YES
# Prepare Fedora environment
RUN dnf update -yq \
- && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make
+ && dnf install -yq \
+ @buildsys-build rpmdevtools git \
+ dnf-plugins-core libcurl-devel fontconfig-devel \
+ freetype-devel openssl-devel glibc-devel \
+ libicu-devel systemd wget make \
+ && dnf clean all \
+ && rm -rf /var/cache/dnf
# Install DotNET SDK
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
- && mkdir -p dotnet-sdk \
- && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
- && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
-
+ && mkdir -p dotnet-sdk \
+ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
+ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
# Create symlinks and directories
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \
- && mkdir -p ${SOURCE_DIR}/SPECS \
- && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
- && mkdir -p ${SOURCE_DIR}/SOURCES \
- && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES
+ && mkdir -p ${SOURCE_DIR}/SPECS \
+ && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
+ && mkdir -p ${SOURCE_DIR}/SOURCES \
+ && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES
VOLUME ${SOURCE_DIR}/
diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64
index 386f7cefe0..6b8de3773f 100644
--- a/deployment/Dockerfile.linux.amd64
+++ b/deployment/Dockerfile.linux.amd64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.amd64 /build.sh
diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl
index 56c8773332..49d98da2ac 100644
--- a/deployment/Dockerfile.linux.amd64-musl
+++ b/deployment/Dockerfile.linux.amd64-musl
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.amd64-musl /build.sh
diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64
index c9692c440a..aba33c8b23 100644
--- a/deployment/Dockerfile.linux.arm64
+++ b/deployment/Dockerfile.linux.arm64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.arm64 /build.sh
diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf
index 2304615560..247f756150 100644
--- a/deployment/Dockerfile.linux.armhf
+++ b/deployment/Dockerfile.linux.armhf
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.armhf /build.sh
diff --git a/deployment/Dockerfile.linux.musl-linux-arm64 b/deployment/Dockerfile.linux.musl-linux-arm64
index 240d091869..a6e1ba217e 100644
--- a/deployment/Dockerfile.linux.musl-linux-arm64
+++ b/deployment/Dockerfile.linux.musl-linux-arm64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
- apt-transport-https debhelper gnupg devscripts unzip \
+ && apt-get install --no-install-recommends -yqq \
+ debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.musl-linux-arm64 /build.sh
diff --git a/deployment/Dockerfile.macos.amd64 b/deployment/Dockerfile.macos.amd64
index 1b054dfc47..45980c363e 100644
--- a/deployment/Dockerfile.macos.amd64
+++ b/deployment/Dockerfile.macos.amd64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.amd64 /build.sh
diff --git a/deployment/Dockerfile.macos.arm64 b/deployment/Dockerfile.macos.arm64
index 07e18da55a..ee3a813dde 100644
--- a/deployment/Dockerfile.macos.arm64
+++ b/deployment/Dockerfile.macos.arm64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,10 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.arm64 /build.sh
diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable
index 36135f7a6e..0ab1b19147 100644
--- a/deployment/Dockerfile.portable
+++ b/deployment/Dockerfile.portable
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -10,10 +14,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index 84fa2028e5..2326d3e852 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -1,7 +1,11 @@
-FROM ubuntu:bionic
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,16 +15,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg wget ca-certificates devscripts \
mmv build-essential libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
-
-# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
- && mkdir -p dotnet-sdk \
- && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
- && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.amd64 /build.sh
diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64
index ca3aa35085..461a287a18 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -1,7 +1,11 @@
-FROM ubuntu:bionic
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,39 +15,36 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg wget ca-certificates devscripts \
mmv build-essential lsb-release
-# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
- && mkdir -p dotnet-sdk \
- && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
- && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
-
# Prepare the cross-toolchain
RUN rm /etc/apt/sources.list \
- && export CODENAME="$( lsb_release -c -s )" \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
- && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
- && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
- && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
- && dpkg --add-architecture arm64 \
- && apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends cross-gcc-dev \
- && TARGET_LIST="arm64" cross-gcc-gensource 6 \
- && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \
- && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \
- && apt-get install -yqq --no-install-recommends \
- gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu \
- bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev \
+ && export CODENAME="$( lsb_release -c -s )" \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \
+ && dpkg --add-architecture arm64 \
+ && apt-get update -yqq \
+ && apt-get install --no-install-recommends -yqq cross-gcc-dev \
+ && TARGET_LIST="arm64" cross-gcc-gensource 12 \
+ && cd cross-gcc-packages-amd64/cross-gcc-12-arm64 \
+ && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \
+ && apt-get install --no-install-recommends -yqq \
+ gcc-12-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu \
+ bison flex libtool gdb sharutils netbase libmpc-dev \
libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 \
- libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64
+ libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust1:arm64 libstdc++6:arm64 libssl-dev:arm64 \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.arm64 /build.sh
diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf
index e52b7fba35..83fe32acf8 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -1,7 +1,11 @@
-FROM ubuntu:bionic
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -11,39 +15,36 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg wget ca-certificates devscripts \
mmv build-essential lsb-release
-# Install dotnet repository
-RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
- && mkdir -p dotnet-sdk \
- && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
- && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
-
# Prepare the cross-toolchain
RUN rm /etc/apt/sources.list \
- && export CODENAME="$( lsb_release -c -s )" \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
- && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
- && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
- && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
- && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
- && dpkg --add-architecture armhf \
- && apt-get update -yqq \
- && apt-get install -yqq cross-gcc-dev \
- && TARGET_LIST="armhf" cross-gcc-gensource 6 \
- && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \
- && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \
- && apt-get install -yqq --no-install-recommends \
- gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf \
- bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev \
+ && export CODENAME="$( lsb_release -c -s )" \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \
+ && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
+ && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
+ && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
+ && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \
+ && dpkg --add-architecture armhf \
+ && apt-get update -yqq \
+ && apt-get install --no-install-recommends -yqq cross-gcc-dev \
+ && TARGET_LIST="armhf" cross-gcc-gensource 12 \
+ && cd cross-gcc-packages-amd64/cross-gcc-12-armhf \
+ && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \
+ && apt-get install --no-install-recommends -yqq \
+ gcc-12-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf \
+ bison flex libtool gdb sharutils netbase libmpc-dev \
libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \
- libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf
+ libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust1:armhf libstdc++6:armhf libssl-dev:armhf \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh
diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64
index 08587aa7eb..358fb620ac 100644
--- a/deployment/Dockerfile.windows.amd64
+++ b/deployment/Dockerfile.windows.amd64
@@ -1,7 +1,11 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim
+ARG DOTNET_VERSION=8.0
+
+FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim
+
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
+
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
@@ -10,10 +14,13 @@ ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update -yqq \
- && apt-get install -yqq --no-install-recommends \
+ && apt-get install --no-install-recommends -yqq \
debhelper gnupg devscripts unzip \
mmv libcurl4-openssl-dev libfontconfig1-dev \
- libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip
+ libfreetype6-dev libssl-dev libssl3 liblttng-ust1 zip \
+ && apt-get clean autoclean -yqq \
+ && apt-get autoremove -yqq \
+ && rm -rf /var/lib/apt/lists/*
# Link to docker-build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.windows.amd64 /build.sh
diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64
index a0ab93e4e0..af73e31533 100755
--- a/deployment/build.centos.amd64
+++ b/deployment/build.centos.amd64
@@ -1,6 +1,6 @@
#!/bin/bash
-#= CentOS/RHEL 7+ amd64 .rpm
+#= CentOS/RHEL 8+ amd64 .rpm
set -o errexit
set -o xtrace
@@ -42,7 +42,7 @@ rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
rm -f fedora/jellyfin*.tar.gz
@@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then
pushd fedora
cp -a /tmp/spec.orig jellyfin.spec
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
popd
fi
diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64
index 1a59d02e91..85776ad6a0 100755
--- a/deployment/build.debian.amd64
+++ b/deployment/build.debian.amd64
@@ -37,7 +37,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64
index e1e30fab4a..d37cc5a64a 100755
--- a/deployment/build.debian.arm64
+++ b/deployment/build.debian.arm64
@@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf
index e3e8ae004e..f3505b1478 100755
--- a/deployment/build.debian.armhf
+++ b/deployment/build.debian.armhf
@@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64
index da345ec08a..21859cbf9f 100755
--- a/deployment/build.fedora.amd64
+++ b/deployment/build.fedora.amd64
@@ -42,7 +42,7 @@ rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
rm -f fedora/jellyfin*.tar.gz
@@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then
pushd fedora
cp -a /tmp/spec.orig jellyfin.spec
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
popd
fi
diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64
index c6baa61f6e..2998d2f9e3 100755
--- a/deployment/build.linux.amd64
+++ b/deployment/build.linux.amd64
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_linux-amd64.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.linux.amd64-musl b/deployment/build.linux.amd64-musl
index 6523f8319a..0fa1764650 100755
--- a/deployment/build.linux.amd64-musl
+++ b/deployment/build.linux.amd64-musl
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_linux-amd64-musl.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_linux-amd64-musl.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.linux.arm64 b/deployment/build.linux.arm64
index 6d6a8f803a..dc44ca330d 100755
--- a/deployment/build.linux.arm64
+++ b/deployment/build.linux.arm64
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_linux-arm64.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_linux-arm64.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.linux.armhf b/deployment/build.linux.armhf
index 5167dfcb8d..f9de9ff0a3 100755
--- a/deployment/build.linux.armhf
+++ b/deployment/build.linux.armhf
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_linux-armhf.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_linux-armhf.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.linux.musl-linux-arm64 b/deployment/build.linux.musl-linux-arm64
index 57980314d4..ae9ab010f5 100755
--- a/deployment/build.linux.musl-linux-arm64
+++ b/deployment/build.linux.musl-linux-arm64
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_linux-arm64-musl.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_linux-arm64-musl.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.macos.amd64 b/deployment/build.macos.amd64
index c7711e82c7..81e0f43f62 100755
--- a/deployment/build.macos.amd64
+++ b/deployment/build.macos.amd64
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_macos-amd64.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.macos.arm64 b/deployment/build.macos.arm64
index b07eaad4e0..0a6f37edea 100755
--- a/deployment/build.macos.arm64
+++ b/deployment/build.macos.arm64
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
-tar -czf jellyfin-server_${version}_macos-arm64.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+tar -czf jellyfin-server_"${version}"_macos-arm64.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.portable b/deployment/build.portable
index ec151d2956..fad14fccfc 100755
--- a/deployment/build.portable
+++ b/deployment/build.portable
@@ -16,16 +16,16 @@ else
fi
# Build archives
-dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=false
-tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version}
-rm -rf dist/jellyfin-server_${version}
+dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=false
+tar -czf jellyfin-server_"${version}"_portable.tar.gz -C dist jellyfin-server_"${version}"
+rm -rf dist/jellyfin-server_"${version}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64
index 17968a6e93..3658676035 100755
--- a/deployment/build.ubuntu.amd64
+++ b/deployment/build.ubuntu.amd64
@@ -37,7 +37,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64
index ee7da9bb98..8cf24b955f 100755
--- a/deployment/build.ubuntu.arm64
+++ b/deployment/build.ubuntu.arm64
@@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf
index 85c993282e..896486dcd4 100755
--- a/deployment/build.ubuntu.armhf
+++ b/deployment/build.ubuntu.armhf
@@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
cp -a /tmp/control.orig debian/control
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64
index 20f976365d..cd07f4e0b2 100755
--- a/deployment/build.windows.amd64
+++ b/deployment/build.windows.amd64
@@ -23,30 +23,30 @@ fi
output_dir="dist/jellyfin-server_${version}"
# Build binary
-dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
+dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output "${output_dir}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true
# Prepare addins
addin_build_dir="$( mktemp -d )"
-wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip
-wget ${FFMPEG_URL} -O ${addin_build_dir}/jellyfin-ffmpeg.zip
-unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir}
-cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe ${output_dir}/nssm.exe
-unzip ${addin_build_dir}/jellyfin-ffmpeg.zip -d ${addin_build_dir}/jellyfin-ffmpeg
-cp ${addin_build_dir}/jellyfin-ffmpeg/* ${output_dir}
-rm -rf ${addin_build_dir}
+wget ${NSSM_URL} -O "${addin_build_dir}"/nssm.zip
+wget ${FFMPEG_URL} -O "${addin_build_dir}"/jellyfin-ffmpeg.zip
+unzip "${addin_build_dir}"/nssm.zip -d "${addin_build_dir}"
+cp "${addin_build_dir}"/${NSSM_VERSION}/win64/nssm.exe "${output_dir}"/nssm.exe
+unzip "${addin_build_dir}"/jellyfin-ffmpeg.zip -d "${addin_build_dir}"/jellyfin-ffmpeg
+cp "${addin_build_dir}"/jellyfin-ffmpeg/* "${output_dir}"
+rm -rf "${addin_build_dir}"
# Create zip package
pushd dist
-zip -qr jellyfin-server_${version}.portable.zip jellyfin-server_${version}
+zip -qr jellyfin-server_"${version}".portable.zip jellyfin-server_"${version}"
popd
-rm -rf ${output_dir}
+rm -rf "${output_dir}"
# Move the artifacts out
mkdir -p "${ARTIFACT_DIR}/"
mv dist/jellyfin[-_]*.zip "${ARTIFACT_DIR}/"
if [[ ${IS_DOCKER} == YES ]]; then
- chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}"
+ chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}"
fi
popd
diff --git a/fedora/README.md b/fedora/README.md
index d449b51c16..6ea87740f2 100644
--- a/fedora/README.md
+++ b/fedora/README.md
@@ -14,8 +14,10 @@ The RPM package for Fedora/CentOS requires some additional repositories as ffmpe
# ffmpeg from RPMfusion free
# Fedora
$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
-# CentOS 7
-$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm
+# CentOS 8
+$ sudo dnf localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
+# CentOS 9
+$ sudo dnf localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-9.noarch.rpm
```
## Building with dotnet
@@ -26,8 +28,10 @@ Jellyfin is build with `--self-contained` so no dotnet required for runtime.
# dotnet required for building the RPM
# Fedora
$ sudo dnf copr enable @dotnet-sig/dotnet
-# CentOS
-$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
+# CentOS 8
+$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm
+# CentOS 9
+$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/9/packages-microsoft-prod.rpm
```
## TODO
diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec
index fb9fb2f7da..5327495ad3 100644
--- a/fedora/jellyfin.spec
+++ b/fedora/jellyfin.spec
@@ -1,10 +1,4 @@
%global debug_package %{nil}
-# Set the dotnet runtime
-%if 0%{?fedora}
-%global dotnet_runtime fedora.%{fedora}-x64
-%else
-%global dotnet_runtime centos-x64
-%endif
Name: jellyfin
Version: 10.9.0
@@ -29,12 +23,6 @@ BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel,
BuildRequires: dotnet-runtime-8.0, dotnet-sdk-8.0
Requires: %{name}-server = %{version}-%{release}, %{name}-web = %{version}-%{release}
-# Temporary (hopefully?) fix for https://github.com/jellyfin/jellyfin/issues/7471
-%if 0%{?fedora} >= 36
-%global __requires_exclude ^liblttng-ust\\.so\\.0.*$
-%endif
-
-
%description
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
@@ -66,14 +54,14 @@ the Jellyfin server to bind to ports 80 and/or 443 for example.
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export PATH=$PATH:/usr/local/bin
# cannot use --output due to https://github.com/dotnet/sdk/issues/22220
-dotnet publish --configuration Release --self-contained --runtime %{dotnet_runtime} \
+dotnet publish --configuration Release --self-contained --runtime linux-x64 \
-p:DebugSymbols=false -p:DebugType=none Jellyfin.Server
%install
# Jellyfin files
%{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir}
-%{__cp} -r Jellyfin.Server/bin/Release/net8.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin
+%{__cp} -r Jellyfin.Server/bin/Release/net8.0/linux-x64/publish/* %{buildroot}%{_libdir}/jellyfin
%{__install} -D %{SOURCE10} %{buildroot}%{_bindir}/jellyfin
sed -i -e 's|/usr/lib64|%{_libdir}|g' %{buildroot}%{_bindir}/jellyfin
diff --git a/src/Jellyfin.Extensions/GuidExtensions.cs b/src/Jellyfin.Extensions/GuidExtensions.cs
new file mode 100644
index 0000000000..95c591a82e
--- /dev/null
+++ b/src/Jellyfin.Extensions/GuidExtensions.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Jellyfin.Extensions;
+
+///
+/// Guid specific extensions.
+///
+public static class GuidExtensions
+{
+ ///
+ /// Determine whether the guid is default.
+ ///
+ /// The guid.
+ /// Whether the guid is the default value.
+ public static bool IsEmpty(this Guid guid)
+ => guid.Equals(default);
+
+ ///
+ /// Determine whether the guid is null or default.
+ ///
+ /// The guid.
+ /// Whether the guid is null or the default valueF.
+ public static bool IsNullOrEmpty([NotNullWhen(false)] this Guid? guid)
+ => guid is null || guid.Value.IsEmpty();
+}
diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs
index 656e3c3dab..0a50b5c3b9 100644
--- a/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs
+++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs
@@ -18,7 +18,7 @@ namespace Jellyfin.Extensions.Json.Converters
{
// null got handled higher up the call stack
var val = value!.Value;
- if (val.Equals(default))
+ if (val.IsEmpty())
{
writer.WriteNullValue();
}
diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
index f5ce75ff4d..bc968f8ee1 100644
--- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
+++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs
@@ -113,15 +113,6 @@ namespace Jellyfin.LiveTv.Channels
return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item);
}
- ///
- public bool EnableMediaProbe(BaseItem item)
- {
- var internalChannel = _libraryManager.GetItemById(item.ChannelId);
- var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
-
- return channel is ISupportsMediaProbe;
- }
-
///
public Task DeleteItem(BaseItem item)
{
@@ -159,7 +150,7 @@ namespace Jellyfin.LiveTv.Channels
///
public async Task> GetChannelsInternalAsync(ChannelQuery query)
{
- var user = query.UserId.Equals(default)
+ var user = query.UserId.IsEmpty()
? null
: _userManager.GetUserById(query.UserId);
@@ -272,7 +263,7 @@ namespace Jellyfin.LiveTv.Channels
///
public async Task> GetChannelsAsync(ChannelQuery query)
{
- var user = query.UserId.Equals(default)
+ var user = query.UserId.IsEmpty()
? null
: _userManager.GetUserById(query.UserId);
@@ -562,18 +553,6 @@ namespace Jellyfin.LiveTv.Channels
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
}
- ///
- /// Checks whether the provided Guid supports external transfer.
- ///
- /// The Guid.
- /// Whether or not the provided Guid supports external transfer.
- public bool SupportsExternalTransfer(Guid channelId)
- {
- var channelProvider = GetChannelProvider(channelId);
-
- return channelProvider.GetChannelFeatures().SupportsContentDownloading;
- }
-
///
/// Gets the provided channel's supported features.
///
@@ -716,7 +695,7 @@ namespace Jellyfin.LiveTv.Channels
// Find the corresponding channel provider plugin
var channelProvider = GetChannelProvider(channel);
- var parentItem = query.ParentId.Equals(default)
+ var parentItem = query.ParentId.IsEmpty()
? channel
: _libraryManager.GetItemById(query.ParentId);
@@ -729,7 +708,7 @@ namespace Jellyfin.LiveTv.Channels
cancellationToken)
.ConfigureAwait(false);
- if (query.ParentId.Equals(default))
+ if (query.ParentId.IsEmpty())
{
query.Parent = channel;
}
@@ -1215,19 +1194,6 @@ namespace Jellyfin.LiveTv.Channels
return result;
}
- internal IChannel GetChannelProvider(Guid internalChannelId)
- {
- var result = GetAllChannels()
- .FirstOrDefault(i => internalChannelId.Equals(GetInternalChannelId(i.Name)));
-
- if (result is null)
- {
- throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId);
- }
-
- return result;
- }
-
///
public void Dispose()
{
diff --git a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs
new file mode 100644
index 0000000000..67d0e5295b
--- /dev/null
+++ b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs
@@ -0,0 +1,18 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.LiveTv;
+
+namespace Jellyfin.LiveTv.Configuration;
+
+///
+/// extensions for Live TV.
+///
+public static class LiveTvConfigurationExtensions
+{
+ ///
+ /// Gets the .
+ ///
+ /// The .
+ /// The .
+ public static LiveTvOptions GetLiveTvConfiguration(this IConfigurationManager configurationManager)
+ => configurationManager.GetConfiguration("livetv");
+}
diff --git a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs
new file mode 100644
index 0000000000..258afbb056
--- /dev/null
+++ b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.LiveTv;
+
+namespace Jellyfin.LiveTv.Configuration;
+
+///
+/// implementation for .
+///
+public class LiveTvConfigurationFactory : IConfigurationFactory
+{
+ ///
+ public IEnumerable GetConfigurations()
+ {
+ return new[]
+ {
+ new ConfigurationStore
+ {
+ ConfigurationType = typeof(LiveTvOptions),
+ Key = "livetv"
+ }
+ };
+ }
+}
diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs
index 439ed965b0..e7e927b2d0 100644
--- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs
+++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs
@@ -17,6 +17,7 @@ using System.Xml;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using Jellyfin.Extensions;
+using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -43,8 +44,6 @@ namespace Jellyfin.LiveTv.EmbyTV
{
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
- private const int TunerDiscoveryDurationMs = 3000;
-
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerConfigurationManager _config;
@@ -53,6 +52,7 @@ namespace Jellyfin.LiveTv.EmbyTV
private readonly TimerManager _timerProvider;
private readonly LiveTvManager _liveTvManager;
+ private readonly ITunerHostManager _tunerHostManager;
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
@@ -79,6 +79,7 @@ namespace Jellyfin.LiveTv.EmbyTV
IHttpClientFactory httpClientFactory,
IServerConfigurationManager config,
ILiveTvManager liveTvManager,
+ ITunerHostManager tunerHostManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
ILibraryMonitor libraryMonitor,
@@ -96,6 +97,7 @@ namespace Jellyfin.LiveTv.EmbyTV
_providerManager = providerManager;
_mediaEncoder = mediaEncoder;
_liveTvManager = (LiveTvManager)liveTvManager;
+ _tunerHostManager = tunerHostManager;
_mediaSourceManager = mediaSourceManager;
_streamHelper = streamHelper;
@@ -126,7 +128,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
get
{
- var path = GetConfiguration().RecordingPath;
+ var path = _config.GetLiveTvConfiguration().RecordingPath;
return string.IsNullOrWhiteSpace(path)
? DefaultRecordingPath
@@ -189,7 +191,7 @@ namespace Jellyfin.LiveTv.EmbyTV
pathsAdded.AddRange(pathsToCreate);
}
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
var pathsToRemove = config.MediaLocationsCreated
.Except(recordingFolders.SelectMany(i => i.Locations))
@@ -309,7 +311,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
var list = new List();
- foreach (var hostInstance in _liveTvManager.TunerHosts)
+ foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@@ -509,7 +511,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
var list = new List();
- foreach (var hostInstance in _liveTvManager.TunerHosts)
+ foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@@ -831,7 +833,7 @@ namespace Jellyfin.LiveTv.EmbyTV
public Task GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null)
{
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
var defaults = new SeriesTimerInfo()
{
@@ -932,7 +934,7 @@ namespace Jellyfin.LiveTv.EmbyTV
private List> GetListingProviders()
{
- return GetConfiguration().ListingProviders
+ return _config.GetLiveTvConfiguration().ListingProviders
.Select(i =>
{
var provider = _liveTvManager.ListingProviders.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
@@ -965,7 +967,7 @@ namespace Jellyfin.LiveTv.EmbyTV
return result;
}
- foreach (var hostInstance in _liveTvManager.TunerHosts)
+ foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@@ -997,7 +999,7 @@ namespace Jellyfin.LiveTv.EmbyTV
throw new ArgumentNullException(nameof(channelId));
}
- foreach (var hostInstance in _liveTvManager.TunerHosts)
+ foreach (var hostInstance in _tunerHostManager.TunerHosts)
{
try
{
@@ -1021,11 +1023,6 @@ namespace Jellyfin.LiveTv.EmbyTV
return Task.CompletedTask;
}
- public Task RecordLiveStream(string id, CancellationToken cancellationToken)
- {
- return Task.CompletedTask;
- }
-
public Task ResetTuner(string id, CancellationToken cancellationToken)
{
return Task.CompletedTask;
@@ -1076,7 +1073,7 @@ namespace Jellyfin.LiveTv.EmbyTV
private string GetRecordingPath(TimerInfo timer, RemoteSearchResult metadata, out string seriesPath)
{
var recordPath = RecordingPath;
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
seriesPath = null;
if (timer.IsProgramSeries)
@@ -1596,7 +1593,7 @@ namespace Jellyfin.LiveTv.EmbyTV
private void PostProcessRecording(TimerInfo timer, string path)
{
- var options = GetConfiguration();
+ var options = _config.GetLiveTvConfiguration();
if (string.IsNullOrWhiteSpace(options.RecordingPostProcessor))
{
return;
@@ -1777,7 +1774,7 @@ namespace Jellyfin.LiveTv.EmbyTV
program.AddGenre("News");
}
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
if (config.SaveRecordingNFO)
{
@@ -1995,7 +1992,7 @@ namespace Jellyfin.LiveTv.EmbyTV
await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false);
}
- var people = item.Id.Equals(default) ? new List() : _libraryManager.GetPeople(item);
+ var people = item.Id.IsEmpty() ? new List() : _libraryManager.GetPeople(item);
var directors = people
.Where(i => i.IsType(PersonKind.Director))
@@ -2128,11 +2125,6 @@ namespace Jellyfin.LiveTv.EmbyTV
return _libraryManager.GetItemList(query).Cast().FirstOrDefault();
}
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration("livetv");
- }
-
private bool ShouldCancelTimerForSeriesTimer(SeriesTimerInfo seriesTimer, TimerInfo timer)
{
if (timer.IsManual)
@@ -2325,7 +2317,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
string channelId = seriesTimer.RecordAnyChannel ? null : seriesTimer.ChannelId;
- if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.Equals(default))
+ if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.IsEmpty())
{
if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel))
{
@@ -2384,7 +2376,7 @@ namespace Jellyfin.LiveTv.EmbyTV
{
string channelId = null;
- if (!programInfo.ChannelId.Equals(default))
+ if (!programInfo.ChannelId.IsEmpty())
{
if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel))
{
@@ -2519,7 +2511,7 @@ namespace Jellyfin.LiveTv.EmbyTV
};
}
- var customPath = GetConfiguration().MovieRecordingPath;
+ var customPath = _config.GetLiveTvConfiguration().MovieRecordingPath;
if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{
yield return new VirtualFolderInfo
@@ -2530,7 +2522,7 @@ namespace Jellyfin.LiveTv.EmbyTV
};
}
- customPath = GetConfiguration().SeriesRecordingPath;
+ customPath = _config.GetLiveTvConfiguration().SeriesRecordingPath;
if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath))
{
yield return new VirtualFolderInfo
@@ -2541,81 +2533,5 @@ namespace Jellyfin.LiveTv.EmbyTV
};
}
}
-
- public async Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
- {
- var list = new List();
-
- var configuredDeviceIds = GetConfiguration().TunerHosts
- .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
- .Select(i => i.DeviceId)
- .ToList();
-
- foreach (var host in _liveTvManager.TunerHosts)
- {
- var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
-
- if (newDevicesOnly)
- {
- discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
-
- list.AddRange(discoveredDevices);
- }
-
- return list;
- }
-
- public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
- {
- foreach (var host in _liveTvManager.TunerHosts)
- {
- await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
- {
- var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
-
- var configuredDevices = GetConfiguration().TunerHosts
- .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var device in discoveredDevices)
- {
- var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
-
- if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url);
-
- configuredDevice.Url = device.Url;
- await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false);
- }
- }
- }
-
- private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
- {
- try
- {
- var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
-
- foreach (var device in discoveredDevices)
- {
- _logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url);
- }
-
- return discoveredDevices;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error discovering tuner devices");
-
- return new List();
- }
- }
}
}
diff --git a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..5490547ec3
--- /dev/null
+++ b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs
@@ -0,0 +1,31 @@
+using Jellyfin.LiveTv.Channels;
+using Jellyfin.LiveTv.TunerHosts;
+using Jellyfin.LiveTv.TunerHosts.HdHomerun;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.IO;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jellyfin.LiveTv.Extensions;
+
+///
+/// Live TV extensions for .
+///
+public static class LiveTvServiceCollectionExtensions
+{
+ ///
+ /// Adds Live TV services to the .
+ ///
+ /// The to add services to.
+ public static void AddLiveTvServices(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ services.AddSingleton();
+ services.AddSingleton();
+ }
+}
diff --git a/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs
deleted file mode 100644
index ddbf6345c5..0000000000
--- a/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.LiveTv;
-
-namespace Jellyfin.LiveTv
-{
- ///
- /// implementation for .
- ///
- public class LiveTvConfigurationFactory : IConfigurationFactory
- {
- ///
- public IEnumerable GetConfigurations()
- {
- return new ConfigurationStore[]
- {
- new ConfigurationStore
- {
- ConfigurationType = typeof(LiveTvOptions),
- Key = "livetv"
- }
- };
- }
- }
-}
diff --git a/src/Jellyfin.LiveTv/LiveTvDtoService.cs b/src/Jellyfin.LiveTv/LiveTvDtoService.cs
index 7c7c26eb4a..55b056d3d8 100644
--- a/src/Jellyfin.LiveTv/LiveTvDtoService.cs
+++ b/src/Jellyfin.LiveTv/LiveTvDtoService.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Drawing;
@@ -456,7 +457,7 @@ namespace Jellyfin.LiveTv
info.Id = timer.ExternalId;
}
- if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.IsEmpty() && string.IsNullOrEmpty(info.ChannelId))
{
var channel = _libraryManager.GetItemById(dto.ChannelId);
@@ -522,7 +523,7 @@ namespace Jellyfin.LiveTv
info.Id = timer.ExternalId;
}
- if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.IsEmpty() && string.IsNullOrEmpty(info.ChannelId))
{
var channel = _libraryManager.GetItemById(dto.ChannelId);
diff --git a/src/Jellyfin.LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs
index 4fc9956538..bada4249aa 100644
--- a/src/Jellyfin.LiveTv/LiveTvManager.cs
+++ b/src/Jellyfin.LiveTv/LiveTvManager.cs
@@ -12,7 +12,8 @@ using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
-using MediaBrowser.Common.Configuration;
+using Jellyfin.Extensions;
+using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
@@ -57,9 +58,9 @@ namespace Jellyfin.LiveTv
private readonly IFileSystem _fileSystem;
private readonly IChannelManager _channelManager;
private readonly LiveTvDtoService _tvDtoService;
+ private readonly ITunerHostManager _tunerHostManager;
private ILiveTvService[] _services = Array.Empty();
- private ITunerHost[] _tunerHosts = Array.Empty();
private IListingsProvider[] _listingProviders = Array.Empty();
public LiveTvManager(
@@ -74,7 +75,8 @@ namespace Jellyfin.LiveTv
ILocalizationManager localization,
IFileSystem fileSystem,
IChannelManager channelManager,
- LiveTvDtoService liveTvDtoService)
+ LiveTvDtoService liveTvDtoService,
+ ITunerHostManager tunerHostManager)
{
_config = config;
_logger = logger;
@@ -88,6 +90,7 @@ namespace Jellyfin.LiveTv
_userDataManager = userDataManager;
_channelManager = channelManager;
_tvDtoService = liveTvDtoService;
+ _tunerHostManager = tunerHostManager;
}
public event EventHandler> SeriesTimerCancelled;
@@ -104,30 +107,17 @@ namespace Jellyfin.LiveTv
/// The services.
public IReadOnlyList Services => _services;
- public IReadOnlyList TunerHosts => _tunerHosts;
-
public IReadOnlyList ListingProviders => _listingProviders;
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration("livetv");
- }
-
public string GetEmbyTvActiveRecordingPath(string id)
{
return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
}
- ///
- /// Adds the parts.
- ///
- /// The services.
- /// The tuner hosts.
- /// The listing providers.
- public void AddParts(IEnumerable services, IEnumerable tunerHosts, IEnumerable listingProviders)
+ ///
+ public void AddParts(IEnumerable services, IEnumerable listingProviders)
{
_services = services.ToArray();
- _tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray();
_listingProviders = listingProviders.ToArray();
@@ -159,23 +149,9 @@ namespace Jellyfin.LiveTv
}));
}
- public List GetTunerHostTypes()
- {
- return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = i.Type
- }).ToList();
- }
-
- public Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
- {
- return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
- }
-
public QueryResult GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
- var user = query.UserId.Equals(default)
+ var user = query.UserId.IsEmpty()
? null
: _userManager.GetUserById(query.UserId);
@@ -1034,7 +1010,7 @@ namespace Jellyfin.LiveTv
{
await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
- await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
+ await _tunerHostManager.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
var numComplete = 0;
double progressPerService = _services.Length == 0
@@ -1270,7 +1246,7 @@ namespace Jellyfin.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
- if (itemId.Equals(default))
+ if (itemId.IsEmpty())
{
// Somehow some invalid data got into the db. It probably predates the boundary checking
continue;
@@ -1302,7 +1278,7 @@ namespace Jellyfin.LiveTv
private double GetGuideDays()
{
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
if (config.GuideDays.HasValue)
{
@@ -1529,7 +1505,7 @@ namespace Jellyfin.LiveTv
public async Task> GetRecordingsAsync(RecordingQuery query, DtoOptions options)
{
- var user = query.UserId.Equals(default)
+ var user = query.UserId.IsEmpty()
? null
: _userManager.GetUserById(query.UserId);
@@ -2125,7 +2101,7 @@ namespace Jellyfin.LiveTv
private bool IsLiveTvEnabled(User user)
{
- return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
+ return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || _config.GetLiveTvConfiguration().TunerHosts.Length > 0);
}
public IEnumerable GetEnabledUsers()
@@ -2171,48 +2147,6 @@ namespace Jellyfin.LiveTv
return _libraryManager.GetNamedView(name, CollectionType.livetv, name);
}
- public async Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
- {
- info = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(info));
-
- var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- if (provider is null)
- {
- throw new ResourceNotFoundException();
- }
-
- if (provider is IConfigurableTunerHost configurable)
- {
- await configurable.Validate(info).ConfigureAwait(false);
- }
-
- var config = GetConfiguration();
-
- var list = config.TunerHosts.ToList();
- var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
-
- if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
- {
- info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
- list.Add(info);
- config.TunerHosts = list.ToArray();
- }
- else
- {
- config.TunerHosts[index] = info;
- }
-
- _config.SaveConfiguration("livetv", config);
-
- if (dataSourceChanged)
- {
- _taskManager.CancelIfRunningAndQueue();
- }
-
- return info;
- }
-
public async Task SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
@@ -2232,7 +2166,7 @@ namespace Jellyfin.LiveTv
await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false);
- LiveTvOptions config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
var list = config.ListingProviders.ToList();
int index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
@@ -2257,7 +2191,7 @@ namespace Jellyfin.LiveTv
public void DeleteListingsProvider(string id)
{
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
@@ -2267,7 +2201,7 @@ namespace Jellyfin.LiveTv
public async Task SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber)
{
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)).ToArray();
@@ -2327,7 +2261,7 @@ namespace Jellyfin.LiveTv
public Task> GetLineups(string providerType, string providerId, string country, string location)
{
- var config = GetConfiguration();
+ var config = _config.GetLiveTvConfiguration();
if (string.IsNullOrWhiteSpace(providerId))
{
@@ -2357,13 +2291,13 @@ namespace Jellyfin.LiveTv
public Task> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
{
- var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
+ var info = _config.GetLiveTvConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
return EmbyTV.EmbyTV.Current.GetChannelsForListingsProvider(info, cancellationToken);
}
public Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken)
{
- var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
+ var info = _config.GetLiveTvConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
var provider = _listingProviders.First(i => string.Equals(i.Type, info.Type, StringComparison.OrdinalIgnoreCase));
return provider.GetChannels(info, cancellationToken);
}
diff --git a/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs
index e58296a70a..18bd61d999 100644
--- a/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs
+++ b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs
@@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Tasks;
namespace Jellyfin.LiveTv
@@ -38,7 +38,7 @@ namespace Jellyfin.LiveTv
public string Category => "Live TV";
///
- public bool IsHidden => _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Length == 0;
+ public bool IsHidden => _liveTvManager.Services.Count == 1 && _config.GetLiveTvConfiguration().TunerHosts.Length == 0;
///
public bool IsEnabled => true;
@@ -66,10 +66,5 @@ namespace Jellyfin.LiveTv
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration("livetv");
- }
}
}
diff --git a/src/Jellyfin.LiveTv/StreamHelper.cs b/src/Jellyfin.LiveTv/StreamHelper.cs
index ab4b6e9b15..e9644e95e7 100644
--- a/src/Jellyfin.LiveTv/StreamHelper.cs
+++ b/src/Jellyfin.LiveTv/StreamHelper.cs
@@ -81,36 +81,6 @@ namespace Jellyfin.LiveTv
}
}
- public async Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
- {
- byte[] buffer = ArrayPool.Shared.Rent(IODefaults.CopyToBufferSize);
- try
- {
- int bytesRead;
-
- while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
- {
- var bytesToWrite = Math.Min(bytesRead, copyLength);
-
- if (bytesToWrite > 0)
- {
- await destination.WriteAsync(buffer.AsMemory(0, Convert.ToInt32(bytesToWrite)), cancellationToken).ConfigureAwait(false);
- }
-
- copyLength -= bytesToWrite;
-
- if (copyLength <= 0)
- {
- break;
- }
- }
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
public async Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken)
{
byte[] buffer = ArrayPool.Shared.Rent(bufferSize);
diff --git a/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs
index 769f196bdf..afc2e4f9ce 100644
--- a/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs
@@ -10,7 +10,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
+using Jellyfin.LiveTv.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
@@ -69,7 +69,7 @@ namespace Jellyfin.LiveTv.TunerHosts
protected virtual IList GetTunerHosts()
{
- return GetConfiguration().TunerHosts
+ return Config.GetLiveTvConfiguration().TunerHosts
.Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
.ToList();
}
@@ -228,10 +228,5 @@ namespace Jellyfin.LiveTv.TunerHosts
return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
}
-
- protected LiveTvOptions GetConfiguration()
- {
- return Config.GetConfiguration("livetv");
- }
}
}
diff --git a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index b1b08e992b..fef84dd000 100644
--- a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -163,152 +162,6 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun
}
}
- private async Task> GetTunerInfosHttp(TunerHostInfo info, CancellationToken cancellationToken)
- {
- var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
-
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken)
- .ConfigureAwait(false);
- var tuners = new List();
- var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- await using (stream.ConfigureAwait(false))
- {
- using var sr = new StreamReader(stream, System.Text.Encoding.UTF8);
- await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false))
- {
- string stripedLine = StripXML(line);
- if (stripedLine.Contains("Channel", StringComparison.Ordinal))
- {
- LiveTvTunerStatus status;
- var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
- var name = stripedLine.Substring(0, index - 1);
- var currentChannel = stripedLine.Substring(index + 7);
- if (string.Equals(currentChannel, "none", StringComparison.Ordinal))
- {
- status = LiveTvTunerStatus.LiveTv;
- }
- else
- {
- status = LiveTvTunerStatus.Available;
- }
-
- tuners.Add(new LiveTvTunerInfo
- {
- Name = name,
- SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
- ProgramName = currentChannel,
- Status = status
- });
- }
- }
- }
-
- return tuners;
- }
-
- private static string StripXML(string source)
- {
- if (string.IsNullOrEmpty(source))
- {
- return string.Empty;
- }
-
- char[] buffer = new char[source.Length];
- int bufferIndex = 0;
- bool inside = false;
-
- for (int i = 0; i < source.Length; i++)
- {
- char let = source[i];
- if (let == '<')
- {
- inside = true;
- continue;
- }
-
- if (let == '>')
- {
- inside = false;
- continue;
- }
-
- if (!inside)
- {
- buffer[bufferIndex++] = let;
- }
- }
-
- return new string(buffer, 0, bufferIndex);
- }
-
- private async Task> GetTunerInfosUdp(TunerHostInfo info, CancellationToken cancellationToken)
- {
- var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
-
- var tuners = new List(model.TunerCount);
-
- var uri = new Uri(GetApiUrl(info));
-
- using (var manager = new HdHomerunManager())
- {
- // Legacy HdHomeruns are IPv4 only
- var ipInfo = IPAddress.Parse(uri.Host);
-
- for (int i = 0; i < model.TunerCount; i++)
- {
- var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1);
- var currentChannel = "none"; // TODO: Get current channel and map back to Station Id
- var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
- var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
- tuners.Add(new LiveTvTunerInfo
- {
- Name = name,
- SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber,
- ProgramName = currentChannel,
- Status = status
- });
- }
- }
-
- return tuners;
- }
-
- public async Task> GetTunerInfos(CancellationToken cancellationToken)
- {
- var list = new List();
-
- foreach (var host in GetConfiguration().TunerHosts
- .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)))
- {
- try
- {
- list.AddRange(await GetTunerInfos(host, cancellationToken).ConfigureAwait(false));
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error getting tuner info");
- }
- }
-
- return list;
- }
-
- public async Task> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken)
- {
- // TODO Need faster way to determine UDP vs HTTP
- var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
-
- var hdHomerunChannelInfo = channels.FirstOrDefault() as HdHomerunChannelInfo;
-
- if (hdHomerunChannelInfo is null || hdHomerunChannelInfo.IsLegacyTuner)
- {
- return await GetTunerInfosUdp(info, cancellationToken).ConfigureAwait(false);
- }
-
- return await GetTunerInfosHttp(info, cancellationToken).ConfigureAwait(false);
- }
-
private static string GetApiUrl(TunerHostInfo info)
{
var url = info.Url;
@@ -574,40 +427,24 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun
_streamHelper);
}
- var enableHttpStream = true;
- if (enableHttpStream)
- {
- mediaSource.Protocol = MediaProtocol.Http;
-
- var httpUrl = channel.Path;
-
- // If raw was used, the tuner doesn't support params
- if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
- {
- httpUrl += "?transcode=" + profile;
- }
+ mediaSource.Protocol = MediaProtocol.Http;
- mediaSource.Path = httpUrl;
+ var httpUrl = channel.Path;
- return new SharedHttpStream(
- mediaSource,
- tunerHost,
- streamId,
- FileSystem,
- _httpClientFactory,
- Logger,
- Config,
- _appHost,
- _streamHelper);
+ // If raw was used, the tuner doesn't support params
+ if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
+ {
+ httpUrl += "?transcode=" + profile;
}
- return new HdHomerunUdpStream(
+ mediaSource.Path = httpUrl;
+
+ return new SharedHttpStream(
mediaSource,
tunerHost,
streamId,
- new HdHomerunChannelCommands(hdhomerunChannel.Number, profile),
- modelInfo.TunerCount,
FileSystem,
+ _httpClientFactory,
Logger,
Config,
_appHost,
diff --git a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
index 7235e65b64..3666d342ed 100644
--- a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs
@@ -80,22 +80,6 @@ namespace Jellyfin.LiveTv.TunerHosts
.ConfigureAwait(false);
}
- public Task> GetTunerInfos(CancellationToken cancellationToken)
- {
- var list = GetTunerHosts()
- .Select(i => new LiveTvTunerInfo()
- {
- Name = Name,
- SourceType = Type,
- Status = LiveTvTunerStatus.Available,
- Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture),
- Url = i.Url
- })
- .ToList();
-
- return Task.FromResult(list);
- }
-
protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, IList currentLiveStreams, CancellationToken cancellationToken)
{
var tunerCount = tunerHost.TunerCount;
diff --git a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
new file mode 100644
index 0000000000..3e4b0e13fc
--- /dev/null
+++ b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.LiveTv.Configuration;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.LiveTv.TunerHosts;
+
+///
+public class TunerHostManager : ITunerHostManager
+{
+ private const int TunerDiscoveryDurationMs = 3000;
+
+ private readonly ILogger _logger;
+ private readonly IConfigurationManager _config;
+ private readonly ITaskManager _taskManager;
+ private readonly ITunerHost[] _tunerHosts;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The .
+ /// The .
+ /// The .
+ public TunerHostManager(
+ ILogger logger,
+ IConfigurationManager config,
+ ITaskManager taskManager,
+ IEnumerable tunerHosts)
+ {
+ _logger = logger;
+ _config = config;
+ _taskManager = taskManager;
+ _tunerHosts = tunerHosts.Where(t => t.IsSupported).ToArray();
+ }
+
+ ///
+ public IReadOnlyList TunerHosts => _tunerHosts;
+
+ ///
+ public IEnumerable GetTunerHostTypes()
+ => _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
+ {
+ Name = i.Name,
+ Id = i.Type
+ });
+
+ ///
+ public async Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
+ {
+ info = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(info))!;
+
+ var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
+
+ if (provider is null)
+ {
+ throw new ResourceNotFoundException();
+ }
+
+ if (provider is IConfigurableTunerHost configurable)
+ {
+ await configurable.Validate(info).ConfigureAwait(false);
+ }
+
+ var config = _config.GetLiveTvConfiguration();
+
+ var list = config.TunerHosts.ToList();
+ var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
+ {
+ info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
+ list.Add(info);
+ config.TunerHosts = list.ToArray();
+ }
+ else
+ {
+ config.TunerHosts[index] = info;
+ }
+
+ _config.SaveConfiguration("livetv", config);
+
+ if (dataSourceChanged)
+ {
+ _taskManager.CancelIfRunningAndQueue();
+ }
+
+ return info;
+ }
+
+ ///
+ public async IAsyncEnumerable DiscoverTuners(bool newDevicesOnly)
+ {
+ var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts
+ .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
+ .Select(i => i.DeviceId)
+ .ToList();
+
+ foreach (var host in _tunerHosts)
+ {
+ var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, CancellationToken.None).ConfigureAwait(false);
+ foreach (var tuner in discoveredDevices)
+ {
+ if (!newDevicesOnly || !configuredDeviceIds.Contains(tuner.DeviceId, StringComparer.OrdinalIgnoreCase))
+ {
+ yield return tuner;
+ }
+ }
+ }
+ }
+
+ ///
+ public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
+ {
+ foreach (var host in _tunerHosts)
+ {
+ await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
+ {
+ var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
+
+ var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts
+ .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ foreach (var device in discoveredDevices)
+ {
+ var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
+
+ if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url);
+
+ configuredDevice.Url = device.Url;
+ await SaveTunerHost(configuredDevice).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false);
+
+ foreach (var device in discoveredDevices)
+ {
+ _logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url);
+ }
+
+ return discoveredDevices;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error discovering tuner devices");
+
+ return Array.Empty();
+ }
+ }
+}
diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs
index 4e8aec9f19..2f21495044 100644
--- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs
@@ -7,6 +7,7 @@ using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Api.Models.StartupDtos;
using Jellyfin.Api.Models.UserDtos;
+using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Model.Dto;
using Xunit;
@@ -56,7 +57,7 @@ namespace Jellyfin.Server.Integration.Tests
public static async Task GetRootFolderDtoAsync(HttpClient client, Guid userId = default)
{
- if (userId.Equals(default))
+ if (userId.IsEmpty())
{
var userDto = await GetUserDtoAsync(client);
userId = userDto.Id;