aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/azure-pipelines.yml68
-rw-r--r--.gitmodules4
-rw-r--r--BDInfo/BDROM.cs9
-rw-r--r--Dockerfile5
-rw-r--r--Dockerfile.arm5
-rw-r--r--Dockerfile.arm645
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs10
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs30
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs2
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs1
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs45
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs4
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json6
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs2
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs11
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj2
-rw-r--r--Jellyfin.Server/Program.cs6
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs15
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs4
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs12
-rw-r--r--MediaBrowser.Common/Extensions/CollectionExtensions.cs19
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs21
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs3
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs8
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs2
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj2
m---------MediaBrowser.WebDashboard/jellyfin-web0
-rw-r--r--MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs1
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj4
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs34
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs22
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs76
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs18
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs21
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs4
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs9
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs17
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs35
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs355
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs53
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs50
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs33
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs45
-rw-r--r--RSSDP/SsdpDevicePublisher.cs4
-rwxr-xr-xbuild36
-rw-r--r--deployment/README.md57
-rw-r--r--deployment/centos-package-x64/Dockerfile21
-rwxr-xr-xdeployment/centos-package-x64/clean.sh2
-rwxr-xr-xdeployment/centos-package-x64/docker-build.sh64
-rwxr-xr-xdeployment/centos-package-x64/package.sh57
-rwxr-xr-xdeployment/common.build.sh110
-rw-r--r--deployment/debian-package-arm64/Dockerfile.amd646
-rwxr-xr-xdeployment/debian-package-arm64/clean.sh2
-rwxr-xr-xdeployment/debian-package-arm64/docker-build.sh14
-rwxr-xr-xdeployment/debian-package-arm64/package.sh8
-rw-r--r--deployment/debian-package-armhf/Dockerfile.amd646
-rw-r--r--deployment/debian-package-armhf/Dockerfile.armhf6
-rwxr-xr-xdeployment/debian-package-armhf/clean.sh2
-rwxr-xr-xdeployment/debian-package-armhf/docker-build.sh14
-rwxr-xr-xdeployment/debian-package-armhf/package.sh8
-rw-r--r--deployment/debian-package-x64/Dockerfile6
-rwxr-xr-xdeployment/debian-package-x64/clean.sh2
-rwxr-xr-xdeployment/debian-package-x64/docker-build.sh14
-rwxr-xr-xdeployment/debian-package-x64/package.sh8
-rw-r--r--deployment/debian-package-x64/pkg-src/changelog294
-rwxr-xr-xdeployment/docker/build.sh12
-rwxr-xr-xdeployment/docker/package.sh12
-rw-r--r--deployment/fedora-package-x64/Dockerfile22
-rwxr-xr-xdeployment/fedora-package-x64/clean.sh2
-rwxr-xr-xdeployment/fedora-package-x64/create_tarball.sh55
-rwxr-xr-xdeployment/fedora-package-x64/docker-build.sh64
-rwxr-xr-xdeployment/fedora-package-x64/package.sh12
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec210
-rw-r--r--deployment/linux-x64/Dockerfile37
-rwxr-xr-xdeployment/linux-x64/build.sh7
-rwxr-xr-xdeployment/linux-x64/clean.sh26
-rw-r--r--deployment/linux-x64/dependencies.txt2
-rwxr-xr-xdeployment/linux-x64/docker-build.sh36
-rwxr-xr-xdeployment/linux-x64/package.sh33
-rw-r--r--deployment/macos/Dockerfile37
-rwxr-xr-xdeployment/macos/build.sh7
-rwxr-xr-xdeployment/macos/clean.sh26
-rw-r--r--deployment/macos/dependencies.txt2
-rwxr-xr-xdeployment/macos/docker-build.sh36
-rwxr-xr-xdeployment/macos/package.sh33
-rw-r--r--deployment/portable/Dockerfile37
-rwxr-xr-xdeployment/portable/build.sh8
-rwxr-xr-xdeployment/portable/clean.sh26
-rw-r--r--deployment/portable/dependencies.txt (renamed from deployment/docker/dependencies.txt)0
-rwxr-xr-xdeployment/portable/docker-build.sh36
-rwxr-xr-xdeployment/portable/package.sh33
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.amd648
-rw-r--r--deployment/ubuntu-package-arm64/Dockerfile.arm642
-rwxr-xr-xdeployment/ubuntu-package-arm64/clean.sh2
-rwxr-xr-xdeployment/ubuntu-package-arm64/docker-build.sh14
-rwxr-xr-xdeployment/ubuntu-package-arm64/package.sh8
-rw-r--r--deployment/ubuntu-package-armhf/Dockerfile.amd648
-rw-r--r--deployment/ubuntu-package-armhf/Dockerfile.armhf8
-rwxr-xr-xdeployment/ubuntu-package-armhf/clean.sh2
-rwxr-xr-xdeployment/ubuntu-package-armhf/docker-build.sh14
-rwxr-xr-xdeployment/ubuntu-package-armhf/package.sh8
-rw-r--r--deployment/ubuntu-package-x64/Dockerfile8
-rwxr-xr-xdeployment/ubuntu-package-x64/clean.sh2
-rwxr-xr-xdeployment/ubuntu-package-x64/docker-build.sh14
-rwxr-xr-xdeployment/ubuntu-package-x64/package.sh8
-rw-r--r--deployment/win-x64/Dockerfile37
-rwxr-xr-xdeployment/win-x64/build.sh7
-rwxr-xr-xdeployment/win-x64/clean.sh26
-rw-r--r--deployment/win-x64/dependencies.txt2
-rwxr-xr-xdeployment/win-x64/docker-build.sh61
-rwxr-xr-xdeployment/win-x64/package.sh71
-rw-r--r--deployment/win-x86/Dockerfile37
-rwxr-xr-xdeployment/win-x86/build.sh7
-rwxr-xr-xdeployment/win-x86/clean.sh26
-rw-r--r--deployment/win-x86/dependencies.txt2
-rwxr-xr-xdeployment/win-x86/docker-build.sh61
-rwxr-xr-xdeployment/win-x86/package.sh71
-rw-r--r--deployment/windows/jellyfin.nsi4
132 files changed, 1638 insertions, 1610 deletions
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 705a64d85..6c95ad4ce 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -31,18 +31,40 @@ jobs:
persistCredentials: true
- task: CmdLine@2
- displayName: "Update submodules"
+ displayName: "Check out web"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
- script: 'git submodule foreach --recursive git checkout $(Build.SourceBranch)'
- workingDirectory: '$(Build.SourcesDirectory)'
+ script: 'git clone --single-branch --branch $(Build.SourceBranch) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: CmdLine@2
- displayName: "Update submodules (PR)"
+ displayName: "Check out web (PR)"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
inputs:
- script: 'git submodule foreach --recursive git checkout $(System.PullRequest.TargetBranch)'
- workingDirectory: '$(Build.SourcesDirectory)'
+ script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
+
+ - task: NodeTool@0
+ displayName: 'Install Node.js'
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ versionSpec: '10.x'
+
+ - task: CmdLine@2
+ displayName: "Build Web UI"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: yarn install
+ workingDirectory: $(Agent.TempDirectory)/jellyfin-web
+
+ - task: CopyFiles@2
+ displayName: Copy the web UI
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
+ contents: '**'
+ targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
+ cleanTargetFolder: true # Optional
+ overWrite: true # Optional
+ flattenFolders: false # Optional
- task: DotNetCoreCLI@2
displayName: Publish
@@ -178,18 +200,40 @@ jobs:
persistCredentials: true
- task: CmdLine@2
- displayName: "Update submodules"
+ displayName: "Check out web"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
- script: 'git submodule foreach --recursive git checkout $(Build.SourceBranch)'
- workingDirectory: '$(Build.SourcesDirectory)'
+ script: 'git clone --single-branch --branch $(Build.SourceBranch) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: CmdLine@2
- displayName: "Update submodules (PR)"
+ displayName: "Check out web (PR)"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
inputs:
- script: 'git submodule foreach --recursive git checkout $(System.PullRequest.TargetBranch)'
- workingDirectory: '$(Build.SourcesDirectory)'
+ script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
+
+ - task: NodeTool@0
+ displayName: 'Install Node.js'
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ versionSpec: '10.x'
+
+ - task: CmdLine@2
+ displayName: "Build Web UI"
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ script: yarn install
+ workingDirectory: $(Agent.TempDirectory)/jellyfin-web
+
+ - task: CopyFiles@2
+ displayName: Copy the web UI
+ condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ inputs:
+ sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
+ contents: '**'
+ targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
+ cleanTargetFolder: true # Optional
+ overWrite: true # Optional
+ flattenFolders: false # Optional
- task: CmdLine@2
displayName: Clone the UX repository
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 2b97b1331..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,4 +0,0 @@
-[submodule "MediaBrowser.WebDashboard/jellyfin-web"]
- path = MediaBrowser.WebDashboard/jellyfin-web
- url = https://github.com/jellyfin/jellyfin-web.git
- branch = .
diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs
index 6759ed55a..3a0c14ffd 100644
--- a/BDInfo/BDROM.cs
+++ b/BDInfo/BDROM.cs
@@ -212,7 +212,6 @@ namespace BDInfo
public void Scan()
{
- var errorStreamClipFiles = new List<TSStreamClipFile>();
foreach (var streamClipFile in StreamClipFiles.Values)
{
try
@@ -221,7 +220,6 @@ namespace BDInfo
}
catch (Exception ex)
{
- errorStreamClipFiles.Add(streamClipFile);
if (StreamClipFileScanError != null)
{
if (StreamClipFileScanError(streamClipFile, ex))
@@ -250,7 +248,6 @@ namespace BDInfo
StreamFiles.Values.CopyTo(streamFiles, 0);
Array.Sort(streamFiles, CompareStreamFiles);
- var errorPlaylistFiles = new List<TSPlaylistFile>();
foreach (var playlistFile in PlaylistFiles.Values)
{
try
@@ -259,7 +256,6 @@ namespace BDInfo
}
catch (Exception ex)
{
- errorPlaylistFiles.Add(playlistFile);
if (PlaylistFileScanError != null)
{
if (PlaylistFileScanError(playlistFile, ex))
@@ -275,7 +271,6 @@ namespace BDInfo
}
}
- var errorStreamFiles = new List<TSStreamFile>();
foreach (var streamFile in streamFiles)
{
try
@@ -296,7 +291,6 @@ namespace BDInfo
}
catch (Exception ex)
{
- errorStreamFiles.Add(streamFile);
if (StreamFileScanError != null)
{
if (StreamFileScanError(streamFile, ex))
@@ -431,7 +425,7 @@ namespace BDInfo
{
return 1;
}
- else if ((x != null || x.FileInfo != null) && (y == null || y.FileInfo == null))
+ else if ((x != null && x.FileInfo != null) && (y == null || y.FileInfo == null))
{
return -1;
}
@@ -451,6 +445,5 @@ namespace BDInfo
}
}
}
-
}
}
diff --git a/Dockerfile b/Dockerfile
index 017f8c697..69cb5e0dd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,8 +14,7 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
-RUN bash -c "source deployment/common.build.sh && \
- build_jellyfin Jellyfin.Server Release linux-x64 /jellyfin"
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}
@@ -30,7 +29,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-COPY --from=web-builder /dist /jellyfin/jellyfin-web/src
+COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Dockerfile.arm b/Dockerfile.arm
index d06eeb561..742e050d5 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -22,8 +22,7 @@ RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
-RUN bash -c "source deployment/common.build.sh && \
- build_jellyfin Jellyfin.Server Release linux-arm /jellyfin"
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-arm as qemu
@@ -35,7 +34,7 @@ RUN apt-get update \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-COPY --from=web-builder /dist /jellyfin/jellyfin-web/src
+COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 1c5de4ed0..8c0baf925 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -22,8 +22,7 @@ RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
-RUN bash -c "source deployment/common.build.sh && \
- build_jellyfin Jellyfin.Server Release linux-arm64 /jellyfin"
+RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
@@ -35,7 +34,7 @@ RUN apt-get update \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-COPY --from=web-builder /dist /jellyfin/jellyfin-web/src
+COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 75192a8f1..a3201f0bc 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -218,14 +218,12 @@ namespace Emby.Server.Implementations.Dto
AttachUserSpecificInfo(dto, item, user, options);
}
- if (item is IHasMediaSources hasMediaSources)
+ if (item is IHasMediaSources
+ && options.ContainsField(ItemFields.MediaSources))
{
- if (options.ContainsField(ItemFields.MediaSources))
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
+ dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
- NormalizeMediaSourceContainers(dto);
- }
+ NormalizeMediaSourceContainers(dto);
}
if (options.ContainsField(ItemFields.Studios))
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index bdcf5d0b7..d60f5c055 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
{
- //Exec all RequestFilter attributes with Priority < 0
+ // Exec all RequestFilter attributes with Priority < 0
var attributes = GetRequestFilterAttributes(requestDto.GetType());
int count = attributes.Count;
@@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.HttpServer
attribute.RequestFilter(req, res, requestDto);
}
- //Exec remaining RequestFilter attributes with Priority >= 0
+ // Exec remaining RequestFilter attributes with Priority >= 0
for (; i < count && attributes[i].Priority >= 0; i++)
{
var attribute = attributes[i];
@@ -276,9 +276,9 @@ namespace Emby.Server.Implementations.HttpServer
{
connection.Dispose();
}
- catch
+ catch (Exception ex)
{
-
+ _logger.LogError(ex, "Error disposing connection");
}
}
}
@@ -603,7 +603,14 @@ namespace Emby.Server.Implementations.HttpServer
Summary = route.Summary
});
- routes.Add(new RouteAttribute(NormalizeOldRoutePath(route.Path), route.Verbs)
+ routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
+ {
+ Notes = route.Notes,
+ Priority = route.Priority,
+ Summary = route.Summary
+ });
+
+ routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs)
{
Notes = route.Notes,
Priority = route.Priority,
@@ -645,7 +652,7 @@ namespace Emby.Server.Implementations.HttpServer
}
// this method was left for compatibility with third party clients
- private static string NormalizeOldRoutePath(string path)
+ private static string NormalizeEmbyRoutePath(string path)
{
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
{
@@ -655,6 +662,17 @@ namespace Emby.Server.Implementations.HttpServer
return "emby/" + path;
}
+ // this method was left for compatibility with third party clients
+ private static string NormalizeMediaBrowserRoutePath(string path)
+ {
+ if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+ {
+ return "/mediabrowser" + path;
+ }
+
+ return "mediabrowser/" + path;
+ }
+
private static string NormalizeCustomRoutePath(string baseUrl, string path)
{
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 3d3f67ca2..93a61fe67 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
var user = auth.User;
- if (user == null & !auth.UserId.Equals(Guid.Empty))
+ if (user == null && auth.UserId != Guid.Empty)
{
throw new SecurityException("User with Id " + auth.UserId + " not found");
}
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index f1ae2fc9c..8bdb38784 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Library
}
var filename = fileInfo.Name;
- var path = fileInfo.FullName;
// Ignore hidden files on UNIX
if (Environment.OSVersion.Platform != PlatformID.Win32NT
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index ac6b4a209..52b2f56ff 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -273,14 +273,12 @@ namespace Emby.Server.Implementations.Library
var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
var success = false;
- string updatedUsername = null;
IAuthenticationProvider authenticationProvider = null;
if (user != null)
{
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
success = authResult.success;
}
else
@@ -288,7 +286,7 @@ namespace Emby.Server.Implementations.Library
// user is null
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.authenticationProvider;
- updatedUsername = authResult.username;
+ string updatedUsername = authResult.username;
success = authResult.success;
if (success
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 4d79cae13..88e2a8fa6 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -236,7 +236,7 @@ namespace Emby.Server.Implementations.Library
if (!parentId.Equals(Guid.Empty))
{
var parentItem = _libraryManager.GetItemById(parentId);
- if (parentItem is Channel parentItemChannel)
+ if (parentItem is Channel)
{
return _channelManager.GetLatestChannelItemsInternal(
new InternalItemsQuery(user)
@@ -248,7 +248,7 @@ namespace Emby.Server.Implementations.Library
IncludeItemTypes = request.IncludeItemTypes,
EnableTotalRecordCount = false
},
- CancellationToken.None).Result.Items;
+ CancellationToken.None).GetAwaiter().GetResult().Items;
}
if (parentItem is Folder parent)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 3cc0541e7..cc9c8e5d2 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -208,9 +208,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private static string GetAudioArgs(MediaSourceInfo mediaSource)
{
- var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
- var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty;
-
return "-codec:a:0 copy";
//var audioChannels = 2;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index f5dffc22a..9a4c91d0b 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -17,7 +17,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.LiveTv.Listings
{
@@ -41,6 +40,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private string UserAgent => _appHost.ApplicationUserAgent;
+ /// <inheritdoc />
+ public string Name => "Schedules Direct";
+
+ /// <inheritdoc />
+ public string Type => nameof(SchedulesDirect);
+
private static List<string> GetScheduleRequestDates(DateTime startDateUtc, DateTime endDateUtc)
{
var dates = new List<string>();
@@ -103,7 +108,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
- using (var reader = new StreamReader(response.Content))
{
var dailySchedules = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.Day>>(response.Content).ConfigureAwait(false);
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
@@ -122,7 +126,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestContent = "[\"" + string.Join("\", \"", programsID) + "\"]";
using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
- using (var innerReader = new StreamReader(innerResponse.Content))
{
var programDetails = await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ProgramDetails>>(innerResponse.Content).ConfigureAwait(false);
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
@@ -152,14 +155,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
- const double desiredAspect = 0.666666667;
+ const double DesiredAspect = 2.0 / 3;
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, desiredAspect) ??
- GetProgramImage(ApiUrl, allImages, true, desiredAspect);
+ programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
- const double wideAspect = 1.77777778;
+ const double WideAspect = 16.0 / 9;
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, wideAspect);
+ programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
// Don't supply the same image twice
if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
@@ -167,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.thumbImage = null;
}
- programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, wideAspect);
+ programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
//programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -178,6 +181,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
}
+
return programsInfo;
}
}
@@ -185,12 +189,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static int GetSizeOrder(ScheduleDirect.ImageData image)
{
- if (!string.IsNullOrWhiteSpace(image.height))
+ if (!string.IsNullOrWhiteSpace(image.height)
+ && int.TryParse(image.height, out int value))
{
- if (int.TryParse(image.height, out int value))
- {
- return value;
- }
+ return value;
}
return 0;
@@ -736,16 +738,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
- using (var response = await _httpClient.SendAsync(httpOptions, "PUT"))
+ using (await _httpClient.SendAsync(httpOptions, "PUT"))
{
}
}
- public string Name => "Schedules Direct";
-
- public static string TypeName = "SchedulesDirect";
- public string Type => TypeName;
-
private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(info.ListingsId))
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index ee975e19a..89b92c999 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -60,16 +60,6 @@ namespace Emby.Server.Implementations.LiveTv
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
private readonly IFileSystem _fileSystem;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
-
- public string GetEmbyTvActiveRecordingPath(string id)
- {
- return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
- }
-
public LiveTvManager(
IServerApplicationHost appHost,
IServerConfigurationManager config,
@@ -102,17 +92,34 @@ namespace Emby.Server.Implementations.LiveTv
_tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory, appHost, _libraryManager);
}
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
+
+ public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
+
/// <summary>
/// Gets the services.
/// </summary>
/// <value>The services.</value>
public IReadOnlyList<ILiveTvService> Services => _services;
+ public ITunerHost[] TunerHosts => _tunerHosts;
+
+ public IListingsProvider[] ListingProviders => _listingProviders;
+
private LiveTvOptions GetConfiguration()
{
return _config.GetConfiguration<LiveTvOptions>("livetv");
}
+ public string GetEmbyTvActiveRecordingPath(string id)
+ {
+ return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
+ }
+
/// <summary>
/// Adds the parts.
/// </summary>
@@ -130,13 +137,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (service is EmbyTV.EmbyTV embyTv)
{
- embyTv.TimerCreated += EmbyTv_TimerCreated;
- embyTv.TimerCancelled += EmbyTv_TimerCancelled;
+ embyTv.TimerCreated += OnEmbyTvTimerCreated;
+ embyTv.TimerCancelled += OnEmbyTvTimerCancelled;
}
}
}
- private void EmbyTv_TimerCancelled(object sender, GenericEventArgs<string> e)
+ private void OnEmbyTvTimerCancelled(object sender, GenericEventArgs<string> e)
{
var timerId = e.Argument;
@@ -149,10 +156,9 @@ namespace Emby.Server.Implementations.LiveTv
});
}
- private void EmbyTv_TimerCreated(object sender, GenericEventArgs<TimerInfo> e)
+ private void OnEmbyTvTimerCreated(object sender, GenericEventArgs<TimerInfo> e)
{
var timer = e.Argument;
- var service = sender as ILiveTvService;
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>
{
@@ -164,10 +170,6 @@ namespace Emby.Server.Implementations.LiveTv
});
}
- public ITunerHost[] TunerHosts => _tunerHosts;
-
- public IListingsProvider[] ListingProviders => _listingProviders;
-
public List<NameIdPair> GetTunerHostTypes()
{
return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
@@ -966,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv
private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
{
- var timers = new Dictionary<string, List<TimerInfo>>();
- var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
-
IReadOnlyList<TimerInfo> timerList = null;
IReadOnlyList<SeriesTimerInfo> seriesTimerList = null;
@@ -1601,8 +1600,6 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(query.Id))
{
- var guid = new Guid(query.Id);
-
timers = timers
.Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 3699b988c..9702392b2 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -424,14 +424,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return false;
}
- var nameTag = buf[offset++];
+ offset++; // Name Tag
var nameLength = buf[offset++];
// skip the name field to get to value for return
offset += nameLength;
- var valueTag = buf[offset++];
+ offset++; // Value Tag
var valueLength = buf[offset++];
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index b01abafa1..cc8b7dbd5 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -23,7 +23,7 @@
"HeaderFavoriteEpisodes": "Favoritepisoder",
"HeaderFavoriteShows": "Favoritserier",
"HeaderFavoriteSongs": "Favoritsange",
- "HeaderLiveTV": "Live TV",
+ "HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Næste",
"HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
"NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
- "NotificationOptionInstallationFailed": "Installationsfejl",
+ "NotificationOptionInstallationFailed": "Installationen fejlede",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
"NotificationOptionPluginError": "Pluginfejl",
"NotificationOptionPluginInstalled": "Plugin installeret",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index c78794967..ee7479c1c 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"Application": "Aplicación",
"Artists": "Artistas",
- "AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
+ "AuthenticationSucceededWithUserName": "{0} identificado correctamente",
"Books": "Libros",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
@@ -16,7 +16,7 @@
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
- "HeaderCameraUploads": "Subidas desde cámara",
+ "HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
"NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada",
- "NotificationOptionInstallationFailed": "Error de instalación",
+ "NotificationOptionInstallationFailed": "Error en la instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
"NotificationOptionPluginError": "Error en plugin",
"NotificationOptionPluginInstalled": "Plugin instalado",
@@ -85,7 +85,7 @@
"UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
- "UserOfflineFromDevice": "{0} se ha desconectado de {1}",
+ "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index c4ce16dc8..faa8499b8 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -1,7 +1,7 @@
{
"Albums": "Álbuns",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
- "Application": "Aplicativo",
+ "Application": "Inscrição",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros",
@@ -16,7 +16,7 @@
"Folders": "Pastas",
"Genres": "Gêneros",
"HeaderAlbumArtists": "Artistas do Álbum",
- "HeaderCameraUploads": "Uploads da Câmera",
+ "HeaderCameraUploads": "Envios da Câmera",
"HeaderContinueWatching": "Continuar Assistindo",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index c0465def8..0ad4b37aa 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -15,7 +15,7 @@
"Favorites": "Избранное",
"Folders": "Папки",
"Genres": "Жанры",
- "HeaderAlbumArtists": "Исп-ли альбома",
+ "HeaderAlbumArtists": "Исполнители альбома",
"HeaderCameraUploads": "Камеры",
"HeaderContinueWatching": "Продолжение просмотра",
"HeaderFavoriteAlbums": "Избранные альбомы",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index d41b096e1..ba5e93982 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "应用: {0}, 设备: {1}",
"Application": "应用程序",
"Artists": "艺术家",
- "AuthenticationSucceededWithUserName": "{0} 成功验证",
+ "AuthenticationSucceededWithUserName": "{0} 认证成功",
"Books": "书籍",
"CameraImageUploadedFrom": "已从 {0} 上传了一张新的相机图像",
"Channels": "频道",
@@ -12,7 +12,7 @@
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
"FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
- "Favorites": "最爱",
+ "Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",
"HeaderAlbumArtists": "专辑作家",
@@ -30,7 +30,7 @@
"Inherit": "继承",
"ItemAddedWithName": "{0} 已添加到媒体库",
"ItemRemovedWithName": "{0} 已从媒体库中移除",
- "LabelIpAddressValue": "Ip 地址:{0}",
+ "LabelIpAddressValue": "IP 地址:{0}",
"LabelRunningTimeValue": "运行时间:{0}",
"Latest": "最新",
"MessageApplicationUpdated": "Jellyfin 服务器已更新",
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index c27eb7686..23e22afd5 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Services
{
PropertySetFn = propertySetFn;
PropertyParseStringFn = propertyParseStringFn;
- PropertyType = PropertyType;
+ PropertyType = propertyType;
}
public Action<object, object> PropertySetFn { get; private set; }
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 7947edeeb..0c0c77cda 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
@@ -457,13 +458,17 @@ namespace Emby.Server.Implementations.Updates
var hash = ToHexString(md5.ComputeHash(stream));
if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
{
- _logger.LogDebug("{0}, {1}", package.checksum, hash);
- throw new InvalidDataException($"The checksums didn't match while installing {package.name}.");
+ _logger.LogError(
+ "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ package.name,
+ package.checksum,
+ hash);
+ throw new InvalidDataException("The checksum of the received data doesn't match.");
}
if (Directory.Exists(targetDir))
{
- Directory.Delete(targetDir);
+ Directory.Delete(targetDir, true);
}
stream.Position = 0;
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 35f0c84cb..fa3e9cb35 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -22,7 +22,7 @@
<EmbeddedResource Include="Resources/Configuration/*" />
</ItemGroup>
- <!-- Code analysers-->
+ <!-- Code analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 716dd0fcd..6f1c111c6 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -125,7 +125,9 @@ namespace Jellyfin.Server
Shutdown();
};
- _logger.LogInformation("Jellyfin version: {Version}", Assembly.GetEntryAssembly().GetName().Version);
+ _logger.LogInformation(
+ "Jellyfin version: {Version}",
+ Assembly.GetEntryAssembly().GetName().Version.ToString(3));
ApplicationHost.LogEnvironmentInfo(_logger, appPaths);
@@ -312,7 +314,7 @@ namespace Jellyfin.Server
if (string.IsNullOrEmpty(webDir))
{
// Use default location under ResourcesPath
- webDir = Path.Combine(AppContext.BaseDirectory, "jellyfin-web", "src");
+ webDir = Path.Combine(AppContext.BaseDirectory, "jellyfin-web");
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index f5f753684..8fa6c3dac 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -965,14 +965,6 @@ namespace MediaBrowser.Api.Playback.Hls
var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
- var timeDeltaParam = string.Empty;
-
- if (isEncoding && state.TargetFramerate > 0)
- {
- float startTime = 1 / (state.TargetFramerate.Value * 2);
- timeDeltaParam = string.Format(CultureInfo.InvariantCulture, "-segment_time_delta {0:F3}", startTime);
- }
-
var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
{
@@ -980,7 +972,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
return string.Format(
- "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
@@ -988,11 +980,10 @@ namespace MediaBrowser.Api.Playback.Hls
GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(CultureInfo.InvariantCulture),
+ segmentFormat,
startNumberParam,
- outputPath,
outputTsArg,
- timeDeltaParam,
- segmentFormat
+ outputPath
).Trim();
}
}
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index ada540ba6..b4a302648 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -455,9 +455,7 @@ namespace MediaBrowser.Api.UserLibrary
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Name = i,
Limit = 1
-
- }).Select(albumId => albumId);
-
+ });
}).ToArray();
}
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 40c16b957..33473c2be 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -14,20 +14,20 @@ namespace MediaBrowser.Common.Extensions
/// Strips the HTML.
/// </summary>
/// <param name="htmlString">The HTML string.</param>
- /// <returns>System.String.</returns>
+ /// <returns><see cref="string" />.</returns>
public static string StripHtml(this string htmlString)
{
// http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
- const string pattern = @"<(.|\n)*?>";
+ const string Pattern = @"<(.|\n)*?>";
- return Regex.Replace(htmlString, pattern, string.Empty).Trim();
+ return Regex.Replace(htmlString, Pattern, string.Empty).Trim();
}
/// <summary>
- /// Gets the M d5.
+ /// Gets the Md5.
/// </summary>
- /// <param name="str">The STR.</param>
- /// <returns>Guid.</returns>
+ /// <param name="str">The string.</param>
+ /// <returns><see cref="Guid" />.</returns>
public static Guid GetMD5(this string str)
{
using (var provider = MD5.Create())
diff --git a/MediaBrowser.Common/Extensions/CollectionExtensions.cs b/MediaBrowser.Common/Extensions/CollectionExtensions.cs
index 3bc0295a0..75b9f59f8 100644
--- a/MediaBrowser.Common/Extensions/CollectionExtensions.cs
+++ b/MediaBrowser.Common/Extensions/CollectionExtensions.cs
@@ -5,13 +5,28 @@ namespace MediaBrowser.Common.Extensions
// The MS CollectionExtensions are only available in netcoreapp
public static class CollectionExtensions
{
- public static TValue GetValueOrDefault<TKey, TValue> (this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
+ public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
{
dictionary.TryGetValue(key, out var ret);
return ret;
}
- // REVIEW: Inline?
+ /// <summary>
+ /// Copies all the elements of the current collection to the specified list
+ /// starting at the specified destination array index. The index is specified as a 32-bit integer.
+ /// </summary>
+ /// <param name="source">The current collection that is the source of the elements.</param>
+ /// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
+ /// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
+ /// <typeparam name="T"></typeparam>
+ public static void CopyTo<T>(this IReadOnlyList<T> source, IList<T> destination, int index = 0)
+ {
+ for (int i = 0; i < source.Count; i++)
+ {
+ destination[index + i] = source[i];
+ }
+ }
+
/// <summary>
/// Copies all the elements of the current collection to the specified list
/// starting at the specified destination array index. The index is specified as a 32-bit integer.
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 0e9f7ee44..c3dc6f7f2 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -2045,7 +2045,7 @@ namespace MediaBrowser.Controller.Entities
if (itemByPath == null)
{
- //Logger.LogWarning("Unable to find linked item at path {0}", info.Path);
+ Logger.LogWarning("Unable to find linked item at path {0}", info.Path);
}
return itemByPath;
@@ -2057,7 +2057,7 @@ namespace MediaBrowser.Controller.Entities
if (item == null)
{
- //Logger.LogWarning("Unable to find linked item at path {0}", info.Path);
+ Logger.LogWarning("Unable to find linked item at path {0}", info.Path);
}
return item;
@@ -2085,14 +2085,17 @@ namespace MediaBrowser.Controller.Entities
if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
{
- if (current.Length == 0)
+ int curLen = current.Length;
+ if (curLen == 0)
{
Studios = new[] { name };
}
else
{
- var list =
- Studios = current.Concat(new[] { name }).ToArray();
+ var newArr = new string[curLen + 1];
+ current.CopyTo(newArr, 0);
+ newArr[curLen] = name;
+ Studios = newArr;
}
}
}
@@ -2231,8 +2234,12 @@ namespace MediaBrowser.Controller.Entities
else
{
- var currentCount = ImageInfos.Length;
- ImageInfos = ImageInfos.Concat(new[] { image }).ToArray();
+ var current = ImageInfos;
+ var currentCount = current.Length;
+ var newArr = new ItemImageInfo[currentCount + 1];
+ current.CopyTo(newArr, 0);
+ newArr[currentCount] = image;
+ ImageInfos = newArr;
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 841205d0c..eb3d2ab81 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1252,9 +1252,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (request.AudioBitRate.HasValue)
{
- // Make sure we don't request a bitrate higher than the source
- var currentBitrate = audioStream == null ? request.AudioBitRate.Value : audioStream.BitRate ?? request.AudioBitRate.Value;
-
// Don't encode any higher than this
return Math.Min(384000, request.AudioBitRate.Value);
}
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index 19009e577..bd727bcdf 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -158,8 +158,6 @@ namespace MediaBrowser.LocalMetadata.Savers
/// <returns>Task.</returns>
public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
{
- var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
-
if (!string.IsNullOrEmpty(item.OfficialRating))
{
writer.WriteElementString("ContentRating", item.OfficialRating);
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 9ddfb9b01..d5fa76c3a 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -506,12 +506,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (failed)
{
- var msg = string.Format("ffmpeg subtitle conversion failed for {Path}", inputPath);
+ _logger.LogError("ffmpeg subtitle conversion failed for {Path}", inputPath);
- _logger.LogError(msg);
-
- throw new Exception(msg);
+ throw new Exception(
+ string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle conversion failed for {0}", inputPath));
}
+
await SetAssFont(outputPath).ConfigureAwait(false);
_logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath);
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 9336c720f..9c3e1f980 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -79,6 +79,8 @@ namespace MediaBrowser.Model.Users
public UserPolicy()
{
+ IsHidden = true;
+
EnableContentDeletion = false;
EnableContentDeletionFromFolders = Array.Empty<string>();
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 883986894..a43949367 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
- <None Include="jellyfin-web\src\**\*.*">
+ <None Include="jellyfin-web\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
deleted file mode 160000
-Subproject 867a5e664cb968602b50dee4874fcb961eed480
diff --git a/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs b/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs
index f631439de..60dcde4db 100644
--- a/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs
+++ b/MediaBrowser.XbmcMetadata/Configuration/NfoOptions.cs
@@ -6,6 +6,7 @@ namespace MediaBrowser.XbmcMetadata.Configuration
{
public class ConfigurationFactory : IConfigurationFactory
{
+ /// <inheritdoc />
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new[]
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
index f653270a6..1ca9e43bb 100644
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
@@ -15,4 +15,8 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
+ <PropertyGroup>
+ <LangVersion>latest</LangVersion>
+ </PropertyGroup>
+
</Project>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 5896497ab..b8d0e6560 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -22,13 +22,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
public class BaseNfoParser<T>
where T : BaseItem
{
- /// <summary>
- /// The logger
- /// </summary>
- protected ILogger Logger { get; private set; }
- protected IProviderManager ProviderManager { get; private set; }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IConfigurationManager _config;
private Dictionary<string, string> _validProviderIds;
@@ -42,6 +35,19 @@ namespace MediaBrowser.XbmcMetadata.Parsers
ProviderManager = providerManager;
}
+ protected CultureInfo UsCulture { get; } = new CultureInfo("en-US");
+
+ /// <summary>
+ /// Gets the logger.
+ /// </summary>
+ protected ILogger Logger { get; }
+
+ protected IProviderManager ProviderManager { get; }
+
+ protected virtual bool SupportsUrlAfterClosingXmlTag => false;
+
+ protected virtual string MovieDbParserSearchString => "themoviedb.org/movie/";
+
/// <summary>
/// Fetches metadata for an item from one xml file
/// </summary>
@@ -83,8 +89,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
Fetch(item, metadataFile, GetXmlReaderSettings(), cancellationToken);
}
- protected virtual bool SupportsUrlAfterClosingXmlTag => false;
-
/// <summary>
/// Fetches the specified item.
/// </summary>
@@ -198,8 +202,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
- protected virtual string MovieDbParserSearchString => "themoviedb.org/movie/";
-
protected void ParseProviderLinks(T item, string xml)
{
//Look for a match for the Regex pattern "tt" followed by 7 digits
@@ -219,7 +221,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var tmdbId = xml.Substring(index + srch.Length).TrimEnd('/').Split('-')[0];
if (!string.IsNullOrWhiteSpace(tmdbId) && int.TryParse(tmdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
{
- item.SetProviderId(MetadataProviders.Tmdb, value.ToString(_usCulture));
+ item.SetProviderId(MetadataProviders.Tmdb, value.ToString(UsCulture));
}
}
@@ -234,7 +236,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
var tvdbId = xml.Substring(index + srch.Length).TrimEnd('/');
if (!string.IsNullOrWhiteSpace(tvdbId) && int.TryParse(tvdbId, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
{
- item.SetProviderId(MetadataProviders.Tvdb, value.ToString(_usCulture));
+ item.SetProviderId(MetadataProviders.Tvdb, value.ToString(UsCulture));
}
}
}
@@ -291,7 +293,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrEmpty(text))
{
- if (float.TryParse(text, NumberStyles.Any, _usCulture, out var value))
+ if (float.TryParse(text, NumberStyles.Any, UsCulture, out var value))
{
item.CriticRating = value;
}
@@ -417,7 +419,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(text))
{
- if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out var runtime))
+ if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, UsCulture, out var runtime))
{
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
}
@@ -870,7 +872,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val))
{
- if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var intVal))
+ if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var intVal))
{
sortOrder = intVal;
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index 7f4224076..82ac6c548 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
@@ -14,16 +13,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
public class EpisodeNfoParser : BaseNfoParser<Episode>
{
- public void Fetch(MetadataResult<Episode> item,
- List<LocalImageInfo> images,
- string metadataFile,
- CancellationToken cancellationToken)
+ public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, config, providerManager)
{
- Fetch(item, metadataFile, cancellationToken);
}
- private readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<Episode> item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
{
using (var fileStream = File.OpenRead(metadataFile))
@@ -73,11 +68,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
}
- /// <summary>
- /// Fetches the data from XML node.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="itemResult">The item result.</param>
+ /// <inheritdoc />
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Episode> itemResult)
{
var item = itemResult.Item;
@@ -212,10 +203,5 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
-
- public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
- {
- }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 0c4de9f33..79d9111fe 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -13,13 +13,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
public class MovieNfoParser : BaseNfoParser<Video>
{
+ public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, config, providerManager)
+ {
+ }
+
+ /// <inheritdoc />
protected override bool SupportsUrlAfterClosingXmlTag => true;
- /// <summary>
- /// Fetches the data from XML node.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="itemResult">The item result.</param>
+ /// <inheritdoc />
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Video> itemResult)
{
var item = itemResult.Item;
@@ -35,14 +37,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
imdbId = reader.ReadElementContentAsString();
}
+
if (!string.IsNullOrWhiteSpace(imdbId))
{
item.SetProviderId(MetadataProviders.Imdb, imdbId);
}
+
if (!string.IsNullOrWhiteSpace(tmdbId))
{
item.SetProviderId(MetadataProviders.Tmdb, tmdbId);
}
+
break;
}
case "set":
@@ -83,9 +88,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "artist":
{
var val = reader.ReadElementContentAsString();
- var movie = item as MusicVideo;
- if (!string.IsNullOrWhiteSpace(val) && movie != null)
+ if (!string.IsNullOrWhiteSpace(val) && item is MusicVideo movie)
{
var list = movie.Artists.ToList();
list.Add(val);
@@ -98,9 +102,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "album":
{
var val = reader.ReadElementContentAsString();
- var movie = item as MusicVideo;
- if (!string.IsNullOrWhiteSpace(val) && movie != null)
+ if (!string.IsNullOrWhiteSpace(val) && item is MusicVideo movie)
{
movie.Album = val;
}
@@ -119,48 +122,41 @@ namespace MediaBrowser.XbmcMetadata.Parsers
//xml = xml.Substring(xml.IndexOf('<'));
//xml = xml.Substring(0, xml.LastIndexOf('>'));
- using (var stringReader = new StringReader("<set>" + xml + "</set>"))
+ // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
+ try
{
- // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
- try
+ using (var stringReader = new StringReader("<set>" + xml + "</set>"))
+ using (var reader = XmlReader.Create(stringReader, GetXmlReaderSettings()))
{
- using (var reader = XmlReader.Create(stringReader, GetXmlReaderSettings()))
- {
- reader.MoveToContent();
- reader.Read();
+ reader.MoveToContent();
+ reader.Read();
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "name":
- movie.CollectionName = reader.ReadElementContentAsString();
- break;
- default:
- reader.Skip();
- break;
- }
- }
- else
+ switch (reader.Name)
{
- reader.Read();
+ case "name":
+ movie.CollectionName = reader.ReadElementContentAsString();
+ break;
+ default:
+ reader.Skip();
+ break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
- catch (XmlException)
- {
-
- }
}
- }
+ catch (XmlException)
+ {
- public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
- {
+ }
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
index 882f3a9d3..d6c06f982 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
@@ -9,11 +9,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
public class SeasonNfoParser : BaseNfoParser<Season>
{
- /// <summary>
- /// Fetches the data from XML node.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="itemResult">The item result.</param>
+ public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, config, providerManager)
+ {
+ }
+
+ /// <inheritdoc />
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Season> itemResult)
{
var item = itemResult.Item;
@@ -39,10 +40,5 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
-
- public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
- {
- }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index b0f25ae64..278858b4a 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -11,15 +11,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
public class SeriesNfoParser : BaseNfoParser<Series>
{
+ public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ : base(logger, config, providerManager)
+ {
+ }
+
+ /// <inheritdoc />
protected override bool SupportsUrlAfterClosingXmlTag => true;
+ /// <inheritdoc />
protected override string MovieDbParserSearchString => "themoviedb.org/tv/";
- /// <summary>
- /// Fetches the data from XML node.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="itemResult">The item result.</param>
+ /// <inheritdoc />
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Series> itemResult)
{
var item = itemResult.Item;
@@ -91,10 +94,5 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
-
- public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
- {
- }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
index 6e6a22794..3517bc32c 100644
--- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
@@ -23,14 +23,14 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
{
new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
- {
- return directoryService.GetFile(Path.Combine(info.Path, "album.nfo"));
- }
+ => directoryService.GetFile(Path.Combine(info.Path, "album.nfo"));
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
index 20abfc7f3..03d8dbc7e 100644
--- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
@@ -23,14 +23,14 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
{
new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
- {
- return directoryService.GetFile(Path.Combine(info.Path, "artist.nfo"));
- }
+ => directoryService.GetFile(Path.Combine(info.Path, "artist.nfo"));
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
index 0a47ac8e1..ff3368bb9 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
@@ -11,9 +11,16 @@ namespace MediaBrowser.XbmcMetadata.Providers
public abstract class BaseNfoProvider<T> : ILocalMetadataProvider<T>, IHasItemChangeMonitor
where T : BaseItem, new()
{
- protected IFileSystem FileSystem;
+ private IFileSystem _fileSystem;
- public Task<MetadataResult<T>> GetMetadata(ItemInfo info,
+ protected BaseNfoProvider(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
+ /// <inheritdoc />
+ public Task<MetadataResult<T>> GetMetadata(
+ ItemInfo info,
IDirectoryService directoryService,
CancellationToken cancellationToken)
{
@@ -47,15 +54,13 @@ namespace MediaBrowser.XbmcMetadata.Providers
return Task.FromResult(result);
}
+ /// <inheritdoc />
protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken);
- protected BaseNfoProvider(IFileSystem fileSystem)
- {
- FileSystem = fileSystem;
- }
-
+ /// <inheritdoc />
protected abstract FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService);
+ /// <inheritdoc />
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
var file = GetXmlFile(new ItemInfo(item), directoryService);
@@ -65,7 +70,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
return false;
}
- return file.Exists && FileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved;
+ return file.Exists && _fileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved;
}
public string Name => BaseNfoSaver.SaverName;
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index 28a0514d5..7410b97e0 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -25,6 +25,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken)
{
var tmpItem = new MetadataResult<Video>
@@ -42,9 +43,10 @@ namespace MediaBrowser.XbmcMetadata.Providers
}
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
- return MovieNfoSaver.GetMovieSavePaths(info, FileSystem)
+ return MovieNfoSaver.GetMovieSavePaths(info)
.Select(directoryService.GetFile)
.FirstOrDefault(i => i != null);
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
index f90f283cf..b2278ba4a 100644
--- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
@@ -24,15 +23,13 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
{
- var images = new List<LocalImageInfo>();
-
- new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, images, path, cancellationToken);
-
- result.Images = images;
+ new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
{
var path = Path.ChangeExtension(info.Path, ".nfo");
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
index 0ebc30293..2cf542054 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
@@ -23,11 +23,13 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken)
{
new SeasonNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
=> directoryService.GetFile(Path.Combine(info.Path, "season.nfo"));
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
index 19ac3dc97..25c8e0faf 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
@@ -23,11 +23,13 @@ namespace MediaBrowser.XbmcMetadata.Providers
_providerManager = providerManager;
}
+ /// <inheritdoc />
protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
{
new SeriesNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
}
+ /// <inheritdoc />
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
=> directoryService.GetFile(Path.Combine(info.Path, "tvshow.nfo"));
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
index a1905bf26..233b3cb89 100644
--- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
@@ -28,26 +28,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// <inheritdoc />
protected override string GetLocalSavePath(BaseItem item)
- {
- return Path.Combine(item.Path, "album.nfo");
- }
+ => Path.Combine(item.Path, "album.nfo");
/// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return "album";
- }
+ => "album";
/// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is MusicAlbum && updateType >= MinimumUpdateType;
- }
+ => item.SupportsLocalMetadata && item is MusicAlbum && updateType >= MinimumUpdateType;
/// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
index 0876db5c1..04565ff7e 100644
--- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
@@ -14,26 +14,24 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class ArtistNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(BaseItem item)
+ public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
- return Path.Combine(item.Path, "artist.nfo");
}
+ /// <inheritdoc />
+ protected override string GetLocalSavePath(BaseItem item)
+ => Path.Combine(item.Path, "artist.nfo");
+
+ /// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return "artist";
- }
+ => "artist";
+ /// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is MusicArtist && updateType >= MinimumUpdateType;
- }
+ => item.SupportsLocalMetadata && item is MusicArtist && updateType >= MinimumUpdateType;
+ /// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var artist = (MusicArtist)item;
@@ -51,8 +49,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
AddAlbums(albums, writer);
}
- private readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
private void AddAlbums(IList<BaseItem> albums, XmlWriter writer)
{
foreach (var album in albums)
@@ -66,13 +62,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (album.ProductionYear.HasValue)
{
- writer.WriteElementString("year", album.ProductionYear.Value.ToString(UsCulture));
+ writer.WriteElementString("year", album.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
}
writer.WriteEndElement();
}
}
+ /// <inheritdoc />
protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
@@ -81,12 +78,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
"album",
"disbanded"
});
- return list;
- }
- public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
- : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
+ return list;
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 3ae72c472..d84bc2abb 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -25,76 +25,78 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public abstract class BaseNfoSaver : IMetadataFileSaver
{
- public static readonly string YouTubeWatchUrl = "https://www.youtube.com/watch?v=";
-
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- private static readonly Dictionary<string, string> CommonTags = new[] {
-
- "plot",
- "customrating",
- "lockdata",
- "dateadded",
- "title",
- "rating",
- "year",
- "sorttitle",
- "mpaa",
- "aspectratio",
- "collectionnumber",
- "tmdbid",
- "rottentomatoesid",
- "language",
- "tvcomid",
- "tagline",
- "studio",
- "genre",
- "tag",
- "runtime",
- "actor",
- "criticrating",
- "fileinfo",
- "director",
- "writer",
- "trailer",
- "premiered",
- "releasedate",
- "outline",
- "id",
- "credits",
- "originaltitle",
- "watched",
- "playcount",
- "lastplayed",
- "art",
- "resume",
- "biography",
- "formed",
- "review",
- "style",
- "imdbid",
- "imdb_id",
- "country",
- "audiodbalbumid",
- "audiodbartistid",
- "enddate",
- "lockedfields",
- "zap2itid",
- "tvrageid",
-
- "musicbrainzartistid",
- "musicbrainzalbumartistid",
- "musicbrainzalbumid",
- "musicbrainzreleasegroupid",
- "tvdbid",
- "collectionitem",
-
- "isuserfavorite",
- "userrating",
+ public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
+
+ public const string YouTubeWatchUrl = "https://www.youtube.com/watch?v=";
- "countrycode"
+ private static readonly HashSet<string> _commonTags = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "plot",
+ "customrating",
+ "lockdata",
+ "dateadded",
+ "title",
+ "rating",
+ "year",
+ "sorttitle",
+ "mpaa",
+ "aspectratio",
+ "collectionnumber",
+ "tmdbid",
+ "rottentomatoesid",
+ "language",
+ "tvcomid",
+ "tagline",
+ "studio",
+ "genre",
+ "tag",
+ "runtime",
+ "actor",
+ "criticrating",
+ "fileinfo",
+ "director",
+ "writer",
+ "trailer",
+ "premiered",
+ "releasedate",
+ "outline",
+ "id",
+ "credits",
+ "originaltitle",
+ "watched",
+ "playcount",
+ "lastplayed",
+ "art",
+ "resume",
+ "biography",
+ "formed",
+ "review",
+ "style",
+ "imdbid",
+ "imdb_id",
+ "country",
+ "audiodbalbumid",
+ "audiodbartistid",
+ "enddate",
+ "lockedfields",
+ "zap2itid",
+ "tvrageid",
+
+ "musicbrainzartistid",
+ "musicbrainzalbumartistid",
+ "musicbrainzalbumid",
+ "musicbrainzreleasegroupid",
+ "tvdbid",
+ "collectionitem",
+
+ "isuserfavorite",
+ "userrating",
+
+ "countrycode"
+ };
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+ // filters control characters but allows only properly-formed surrogate sequences
+ private const string _invalidXMLCharsRegex = @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]";
protected BaseNfoSaver(
IFileSystem fileSystem,
@@ -112,12 +114,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
FileSystem = fileSystem;
}
- protected IFileSystem FileSystem { get; private set; }
- protected IServerConfigurationManager ConfigurationManager { get; private set; }
- protected ILibraryManager LibraryManager { get; private set; }
- protected IUserManager UserManager { get; private set; }
- protected IUserDataManager UserDataManager { get; private set; }
- protected ILogger Logger { get; private set; }
+ protected IFileSystem FileSystem { get; }
+
+ protected IServerConfigurationManager ConfigurationManager { get; }
+
+ protected ILibraryManager LibraryManager { get; }
+
+ protected IUserManager UserManager { get; }
+
+ protected IUserDataManager UserDataManager { get; }
+
+ protected ILogger Logger { get; }
protected ItemUpdateType MinimumUpdateType
{
@@ -132,35 +139,30 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
+ /// <inheritdoc />
public string Name => SaverName;
public static string SaverName => "Nfo";
+ /// <inheritdoc />
public string GetSavePath(BaseItem item)
- {
- return GetLocalSavePath(item);
- }
+ => GetLocalSavePath(item);
/// <summary>
/// Gets the save path.
/// </summary>
/// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
+ /// <returns><see cref="string" />.</returns>
protected abstract string GetLocalSavePath(BaseItem item);
/// <summary>
/// Gets the name of the root element.
/// </summary>
/// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
+ /// <returns><see cref="string" />.</returns>
protected abstract string GetRootElementName(BaseItem item);
- /// <summary>
- /// Determines whether [is enabled for] [the specified item].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="updateType">Type of the update.</param>
- /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
+ /// <inheritdoc />
public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType);
protected virtual List<string> GetTagsUsed(BaseItem item)
@@ -169,14 +171,16 @@ namespace MediaBrowser.XbmcMetadata.Savers
foreach (var providerKey in item.ProviderIds.Keys)
{
var providerIdTagName = GetTagForProviderKey(providerKey);
- if (!CommonTags.ContainsKey(providerIdTagName))
+ if (!_commonTags.Contains(providerIdTagName))
{
list.Add(providerIdTagName);
}
}
+
return list;
}
+ /// <inheritdoc />
public void Save(BaseItem item, CancellationToken cancellationToken)
{
var path = GetSavePath(item);
@@ -196,10 +200,11 @@ namespace MediaBrowser.XbmcMetadata.Savers
private void SaveToFile(Stream stream, string path)
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
+
// On Windows, savint the file will fail if the file is hidden or readonly
FileSystem.SetAttributes(path, false, false);
- using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
stream.CopyTo(filestream);
}
@@ -216,9 +221,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
FileSystem.SetHidden(path, hidden);
}
- catch (Exception ex)
+ catch (IOException ex)
{
- Logger.LogError(ex, "Error setting hidden attribute on {path}", path);
+ Logger.LogError(ex, "Error setting hidden attribute on {Path}", path);
}
}
@@ -248,9 +253,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
WriteCustomElements(item, writer);
- var hasMediaSources = baseItem as IHasMediaSources;
-
- if (hasMediaSources != null)
+ if (baseItem is IHasMediaSources hasMediaSources)
{
AddMediaInfo(hasMediaSources, writer);
}
@@ -259,7 +262,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
try
{
- AddCustomTags(xmlPath, tagsUsed, writer, Logger, FileSystem);
+ AddCustomTags(xmlPath, tagsUsed, writer, Logger);
}
catch (FileNotFoundException)
{
@@ -283,7 +286,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
protected abstract void WriteCustomElements(BaseItem item, XmlWriter writer);
public static void AddMediaInfo<T>(T item, XmlWriter writer)
- where T : IHasMediaSources
+ where T : IHasMediaSources
{
writer.WriteStartElement("fileinfo");
writer.WriteStartElement("streamdetails");
@@ -313,17 +316,17 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (stream.BitRate.HasValue)
{
- writer.WriteElementString("bitrate", stream.BitRate.Value.ToString(UsCulture));
+ writer.WriteElementString("bitrate", stream.BitRate.Value.ToString(CultureInfo.InvariantCulture));
}
if (stream.Width.HasValue)
{
- writer.WriteElementString("width", stream.Width.Value.ToString(UsCulture));
+ writer.WriteElementString("width", stream.Width.Value.ToString(CultureInfo.InvariantCulture));
}
if (stream.Height.HasValue)
{
- writer.WriteElementString("height", stream.Height.Value.ToString(UsCulture));
+ writer.WriteElementString("height", stream.Height.Value.ToString(CultureInfo.InvariantCulture));
}
if (!string.IsNullOrEmpty(stream.AspectRatio))
@@ -336,14 +339,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (framerate.HasValue)
{
- writer.WriteElementString("framerate", framerate.Value.ToString(UsCulture));
+ writer.WriteElementString("framerate", framerate.Value.ToString(CultureInfo.InvariantCulture));
}
if (!string.IsNullOrEmpty(stream.Language))
{
// http://web.archive.org/web/20181230211547/https://emby.media/community/index.php?/topic/49071-nfo-not-generated-on-actualize-or-rescan-or-identify
// Web Archive version of link since it's not really explained in the thread.
- writer.WriteElementString("language", RemoveInvalidXMLChars(stream.Language));
+ writer.WriteElementString("language", Regex.Replace(stream.Language, _invalidXMLCharsRegex, string.Empty));
}
var scanType = stream.IsInterlaced ? "interlaced" : "progressive";
@@ -354,12 +357,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (stream.Channels.HasValue)
{
- writer.WriteElementString("channels", stream.Channels.Value.ToString(UsCulture));
+ writer.WriteElementString("channels", stream.Channels.Value.ToString(CultureInfo.InvariantCulture));
}
if (stream.SampleRate.HasValue)
{
- writer.WriteElementString("samplingrate", stream.SampleRate.Value.ToString(UsCulture));
+ writer.WriteElementString("samplingrate", stream.SampleRate.Value.ToString(CultureInfo.InvariantCulture));
}
writer.WriteElementString("default", stream.IsDefault.ToString());
@@ -372,13 +375,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var timespan = TimeSpan.FromTicks(runtimeTicks.Value);
- writer.WriteElementString("duration", Math.Floor(timespan.TotalMinutes).ToString(UsCulture));
- writer.WriteElementString("durationinseconds", Math.Floor(timespan.TotalSeconds).ToString(UsCulture));
+ writer.WriteElementString(
+ "duration",
+ Math.Floor(timespan.TotalMinutes).ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString(
+ "durationinseconds",
+ Math.Floor(timespan.TotalSeconds).ToString(CultureInfo.InvariantCulture));
}
- var video = item as Video;
-
- if (video != null)
+ if (item is Video video)
{
//AddChapters(video, builder, itemRepository);
@@ -413,26 +418,18 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteEndElement();
}
- // filters control characters but allows only properly-formed surrogate sequences
- private static Regex _invalidXMLChars = new Regex(
- @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]");
-
- /// <summary>
- /// removes any unusual unicode characters that can't be encoded into XML
- /// </summary>
- public static string RemoveInvalidXMLChars(string text)
- {
- if (string.IsNullOrEmpty(text)) return string.Empty;
- return _invalidXMLChars.Replace(text, string.Empty);
- }
-
- public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
-
/// <summary>
/// Adds the common nodes.
/// </summary>
/// <returns>Task.</returns>
- private void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
+ private void AddCommonNodes(
+ BaseItem item,
+ XmlWriter writer,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataRepo,
+ IFileSystem fileSystem,
+ IServerConfigurationManager config)
{
var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
@@ -524,12 +521,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (item.CommunityRating.HasValue)
{
- writer.WriteElementString("rating", item.CommunityRating.Value.ToString(UsCulture));
+ writer.WriteElementString("rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
}
if (item.ProductionYear.HasValue)
{
- writer.WriteElementString("year", item.ProductionYear.Value.ToString(UsCulture));
+ writer.WriteElementString("year", item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
}
var forcedSortName = item.ForcedSortName;
@@ -543,13 +540,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("mpaa", item.OfficialRating);
}
- var hasAspectRatio = item as IHasAspectRatio;
- if (hasAspectRatio != null)
+ if (item is IHasAspectRatio hasAspectRatio
+ && !string.IsNullOrEmpty(hasAspectRatio.AspectRatio))
{
- if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio))
- {
- writer.WriteElementString("aspectratio", hasAspectRatio.AspectRatio);
- }
+ writer.WriteElementString("aspectratio", hasAspectRatio.AspectRatio);
}
var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
@@ -571,6 +565,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString("imdbid", imdb);
}
+
writtenProviderIds.Add(MetadataProviders.Imdb.ToString());
}
@@ -607,12 +602,18 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (item is MusicArtist)
{
- writer.WriteElementString("formed", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "formed",
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString));
}
else
{
- writer.WriteElementString("premiered", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
- writer.WriteElementString("releasedate", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "premiered",
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "releasedate",
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString));
}
}
@@ -622,18 +623,20 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString("enddate", item.EndDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString(
+ "enddate",
+ item.EndDate.Value.ToLocalTime().ToString(formatString));
}
}
if (item.CriticRating.HasValue)
{
- writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(UsCulture));
+ writer.WriteElementString(
+ "criticrating",
+ item.CriticRating.Value.ToString(CultureInfo.InvariantCulture));
}
- var hasDisplayOrder = item as IHasDisplayOrder;
-
- if (hasDisplayOrder != null)
+ if (item is IHasDisplayOrder hasDisplayOrder)
{
if (!string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder))
{
@@ -648,7 +651,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
- writer.WriteElementString("runtime", Convert.ToInt64(timespan.TotalMinutes).ToString(UsCulture));
+ writer.WriteElementString(
+ "runtime",
+ Convert.ToInt64(timespan.TotalMinutes).ToString(CultureInfo.InvariantCulture));
}
if (!string.IsNullOrWhiteSpace(item.Tagline))
@@ -756,9 +761,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
try
{
var tagName = GetTagForProviderKey(providerKey);
- //logger.LogDebug("Verifying custom provider tagname {0}", tagName);
+ Logger.LogDebug("Verifying custom provider tagname {0}", tagName);
XmlConvert.VerifyName(tagName);
- //logger.LogDebug("Saving custom provider tagname {0}", tagName);
+ Logger.LogDebug("Saving custom provider tagname {0}", tagName);
writer.WriteElementString(GetTagForProviderKey(providerKey), providerId);
}
@@ -783,8 +788,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
AddActors(people, writer, libraryManager, fileSystem, config, options.SaveImagePathsInNfo);
- var folder = item as BoxSet;
- if (folder != null)
+ if (item is BoxSet folder)
{
AddCollectionItems(folder, writer);
}
@@ -866,29 +870,43 @@ namespace MediaBrowser.XbmcMetadata.Savers
var userdata = userDataRepo.GetUserData(user, item);
- writer.WriteElementString("isuserfavorite", userdata.IsFavorite.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ writer.WriteElementString(
+ "isuserfavorite",
+ userdata.IsFavorite.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
if (userdata.Rating.HasValue)
{
- writer.WriteElementString("userrating", userdata.Rating.Value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ writer.WriteElementString(
+ "userrating",
+ userdata.Rating.Value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
}
if (!item.IsFolder)
{
- writer.WriteElementString("playcount", userdata.PlayCount.ToString(UsCulture));
- writer.WriteElementString("watched", userdata.Played.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ writer.WriteElementString(
+ "playcount",
+ userdata.PlayCount.ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString(
+ "watched",
+ userdata.Played.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
if (userdata.LastPlayedDate.HasValue)
{
- writer.WriteElementString("lastplayed", userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss").ToLowerInvariant());
+ writer.WriteElementString(
+ "lastplayed",
+ userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss").ToLowerInvariant());
}
writer.WriteStartElement("resume");
var runTimeTicks = item.RunTimeTicks ?? 0;
- writer.WriteElementString("position", TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(UsCulture));
- writer.WriteElementString("total", TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(UsCulture));
+ writer.WriteElementString(
+ "position",
+ TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString(
+ "total",
+ TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture));
}
writer.WriteEndElement();
@@ -922,24 +940,21 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (person.SortOrder.HasValue)
{
- writer.WriteElementString("sortorder", person.SortOrder.Value.ToString(UsCulture));
+ writer.WriteElementString(
+ "sortorder",
+ person.SortOrder.Value.ToString(CultureInfo.InvariantCulture));
}
if (saveImagePath)
{
- try
- {
- var personEntity = libraryManager.GetPerson(person.Name);
- var image = personEntity.GetImageInfo(ImageType.Primary, 0);
+ var personEntity = libraryManager.GetPerson(person.Name);
+ var image = personEntity.GetImageInfo(ImageType.Primary, 0);
- if (image != null)
- {
- writer.WriteElementString("thumb", GetImagePathToSave(image, libraryManager, config));
- }
- }
- catch (Exception)
+ if (image != null)
{
- // Already logged in core
+ writer.WriteElementString(
+ "thumb",
+ GetImagePathToSave(image, libraryManager, config));
}
}
@@ -958,11 +973,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
private bool IsPersonType(PersonInfo person, string type)
- {
- return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
- }
+ => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
- private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger, IFileSystem fileSystem)
+ private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger)
{
var settings = new XmlReaderSettings()
{
@@ -982,7 +996,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
catch (Exception ex)
{
- logger.LogError(ex, "Error reading existing xml tags from {path}.", path);
+ logger.LogError(ex, "Error reading existing xml tags from {Path}.", path);
return;
}
@@ -995,7 +1009,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
var name = reader.Name;
- if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
+ if (!_commonTags.Contains(name)
+ && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
{
writer.WriteNode(reader, false);
}
@@ -1013,8 +1028,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
private string GetTagForProviderKey(string providerKey)
- {
- return providerKey.ToLowerInvariant() + "id";
- }
+ => providerKey.ToLowerInvariant() + "id";
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
index cf1b6468a..091c1957e 100644
--- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
@@ -14,43 +14,43 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class EpisodeNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(BaseItem item)
+ public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
- return Path.ChangeExtension(item.Path, ".nfo");
}
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ /// <inheritdoc />
+ protected override string GetLocalSavePath(BaseItem item)
+ => Path.ChangeExtension(item.Path, ".nfo");
+
+ /// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return "episodedetails";
- }
+ => "episodedetails";
+ /// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is Episode && updateType >= MinimumUpdateType;
- }
+ => item.SupportsLocalMetadata && item is Episode && updateType >= MinimumUpdateType;
+ /// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var episode = (Episode)item;
if (episode.IndexNumber.HasValue)
{
- writer.WriteElementString("episode", episode.IndexNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("episode", episode.IndexNumber.Value.ToString(_usCulture));
}
if (episode.IndexNumberEnd.HasValue)
{
- writer.WriteElementString("episodenumberend", episode.IndexNumberEnd.Value.ToString(UsCulture));
+ writer.WriteElementString("episodenumberend", episode.IndexNumberEnd.Value.ToString(_usCulture));
}
if (episode.ParentIndexNumber.HasValue)
{
- writer.WriteElementString("season", episode.ParentIndexNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("season", episode.ParentIndexNumber.Value.ToString(_usCulture));
}
if (episode.PremiereDate.HasValue)
@@ -64,32 +64,33 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
if (episode.AirsAfterSeasonNumber.HasValue && episode.AirsAfterSeasonNumber.Value != -1)
{
- writer.WriteElementString("airsafter_season", episode.AirsAfterSeasonNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("airsafter_season", episode.AirsAfterSeasonNumber.Value.ToString(_usCulture));
}
+
if (episode.AirsBeforeEpisodeNumber.HasValue && episode.AirsBeforeEpisodeNumber.Value != -1)
{
- writer.WriteElementString("airsbefore_episode", episode.AirsBeforeEpisodeNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("airsbefore_episode", episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture));
}
+
if (episode.AirsBeforeSeasonNumber.HasValue && episode.AirsBeforeSeasonNumber.Value != -1)
{
- writer.WriteElementString("airsbefore_season", episode.AirsBeforeSeasonNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("airsbefore_season", episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture));
}
if (episode.AirsBeforeEpisodeNumber.HasValue && episode.AirsBeforeEpisodeNumber.Value != -1)
{
- writer.WriteElementString("displayepisode", episode.AirsBeforeEpisodeNumber.Value.ToString(UsCulture));
+ writer.WriteElementString("displayepisode", episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture));
}
var specialSeason = episode.AiredSeasonNumber;
if (specialSeason.HasValue && specialSeason.Value != -1)
{
- writer.WriteElementString("displayseason", specialSeason.Value.ToString(UsCulture));
+ writer.WriteElementString("displayseason", specialSeason.Value.ToString(_usCulture));
}
}
}
- private readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
+ /// <inheritdoc />
protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
@@ -105,12 +106,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
"displayseason",
"displayepisode"
});
- return list;
- }
- public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
- : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
+ return list;
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index 5e0eff029..08a752e33 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Xml;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -15,28 +16,29 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class MovieNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(BaseItem item)
+ public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
- var paths = GetMovieSavePaths(new ItemInfo(item), FileSystem);
- return paths.Count == 0 ? null : paths[0];
}
- public static List<string> GetMovieSavePaths(ItemInfo item, IFileSystem fileSystem)
- {
- var list = new List<string>();
+ /// <inheritdoc />
+ protected override string GetLocalSavePath(BaseItem item)
+ => GetMovieSavePaths(new ItemInfo(item)).FirstOrDefault();
+ public static IEnumerable<string> GetMovieSavePaths(ItemInfo item)
+ {
if (item.VideoType == VideoType.Dvd && !item.IsPlaceHolder)
{
var path = item.ContainingFolderPath;
- list.Add(Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo"));
+ yield return Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo");
}
if (!item.IsPlaceHolder && (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay))
{
var path = item.ContainingFolderPath;
- list.Add(Path.Combine(path, Path.GetFileName(path) + ".nfo"));
+ yield return Path.Combine(path, Path.GetFileName(path) + ".nfo");
}
else
{
@@ -47,22 +49,20 @@ namespace MediaBrowser.XbmcMetadata.Savers
// list.Add(Path.Combine(item.ContainingFolderPath, "movie.nfo"));
//}
- list.Add(Path.ChangeExtension(item.Path, ".nfo"));
+ yield return Path.ChangeExtension(item.Path, ".nfo");
if (!item.IsInMixedFolder)
{
- list.Add(Path.Combine(item.ContainingFolderPath, "movie.nfo"));
+ yield return Path.Combine(item.ContainingFolderPath, "movie.nfo");
}
}
-
- return list;
}
+ /// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return item is MusicVideo ? "musicvideo" : "movie";
- }
+ => item is MusicVideo ? "musicvideo" : "movie";
+ /// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
@@ -70,10 +70,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
return false;
}
- var video = item as Video;
-
// Check parent for null to avoid running this against things like video backdrops
- if (video != null && !(item is Episode) && !video.ExtraType.HasValue)
+ if (item is Video video && !(item is Episode) && !video.ExtraType.HasValue)
{
return updateType >= MinimumUpdateType;
}
@@ -81,6 +79,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return false;
}
+ /// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var imdb = item.GetProviderId(MetadataProviders.Imdb);
@@ -90,9 +89,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("id", imdb);
}
- var musicVideo = item as MusicVideo;
-
- if (musicVideo != null)
+ if (item is MusicVideo musicVideo)
{
foreach (var artist in musicVideo.Artists)
{
@@ -104,9 +101,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- var movie = item as Movie;
-
- if (movie != null)
+ if (item is Movie movie)
{
if (!string.IsNullOrEmpty(movie.CollectionName))
{
@@ -115,6 +110,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
+ /// <inheritdoc />
protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
@@ -125,12 +121,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
"set",
"id"
});
- return list;
- }
- public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
- : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
+ return list;
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
index aa8d3e96c..25695121d 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
@@ -13,16 +13,26 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeasonNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(BaseItem item)
+ public SeasonNfoSaver(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager,
+ ILogger logger)
+ : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
- return Path.Combine(item.Path, "season.nfo");
}
+ /// <inheritdoc />
+ protected override string GetLocalSavePath(BaseItem item)
+ => Path.Combine(item.Path, "season.nfo");
+
+ /// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return "season";
- }
+ => "season";
+ /// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
@@ -38,6 +48,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && File.Exists(GetSavePath(item)));
}
+ /// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var season = (Season)item;
@@ -48,6 +59,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
+ /// <inheritdoc />
protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
@@ -58,16 +70,5 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
-
- public SeasonNfoSaver(
- IFileSystem fileSystem,
- IServerConfigurationManager configurationManager,
- ILibraryManager libraryManager,
- IUserManager userManager,
- IUserDataManager userDataManager,
- ILogger logger)
- : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index b0fc8c368..8d7faece7 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Xml;
using MediaBrowser.Controller.Configuration;
@@ -13,26 +14,30 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeriesNfoSaver : BaseNfoSaver
{
- protected override string GetLocalSavePath(BaseItem item)
+ public SeriesNfoSaver(
+ IFileSystem fileSystem,
+ IServerConfigurationManager configurationManager,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IUserDataManager userDataManager,
+ ILogger logger)
+ : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
{
- return Path.Combine(item.Path, "tvshow.nfo");
}
+ /// <inheritdoc />
+ protected override string GetLocalSavePath(BaseItem item)
+ => Path.Combine(item.Path, "tvshow.nfo");
+
+ /// <inheritdoc />
protected override string GetRootElementName(BaseItem item)
- {
- return "tvshow";
- }
+ => "tvshow";
+ /// <inheritdoc />
public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is Series && updateType >= MinimumUpdateType;
- }
+ => item.SupportsLocalMetadata && item is Series && updateType >= MinimumUpdateType;
+ /// <inheritdoc />
protected override void WriteCustomElements(BaseItem item, XmlWriter writer)
{
var series = (Series)item;
@@ -52,7 +57,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteStartElement("url");
writer.WriteAttributeString("cache", string.Format("{0}.xml", tvdb));
- writer.WriteString(string.Format("http://www.thetvdb.com/api/1D62F2F90030C444/series/{0}/all/{1}.zip", tvdb, language));
+ writer.WriteString(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "http://www.thetvdb.com/api/1D62F2F90030C444/series/{0}/all/{1}.zip",
+ tvdb,
+ language));
writer.WriteEndElement();
writer.WriteEndElement();
@@ -67,6 +77,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
+ /// <inheritdoc />
protected override List<string> GetTagsUsed(BaseItem item)
{
var list = base.GetTagsUsed(item);
@@ -79,12 +90,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
"status",
"displayorder"
});
- return list;
- }
- public SeriesNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
- : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
+ return list;
}
}
}
diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs
index 7f3e56394..53b740052 100644
--- a/RSSDP/SsdpDevicePublisher.cs
+++ b/RSSDP/SsdpDevicePublisher.cs
@@ -86,7 +86,6 @@ namespace Rssdp.Infrastructure
ThrowIfDisposed();
- var minCacheTime = TimeSpan.Zero;
bool wasAdded = false;
lock (_Devices)
{
@@ -94,7 +93,6 @@ namespace Rssdp.Infrastructure
{
_Devices.Add(device);
wasAdded = true;
- minCacheTime = GetMinimumNonZeroCacheLifetime();
}
}
@@ -120,14 +118,12 @@ namespace Rssdp.Infrastructure
if (device == null) throw new ArgumentNullException(nameof(device));
bool wasRemoved = false;
- var minCacheTime = TimeSpan.Zero;
lock (_Devices)
{
if (_Devices.Contains(device))
{
_Devices.Remove(device);
wasRemoved = true;
- minCacheTime = GetMinimumNonZeroCacheLifetime();
}
}
diff --git a/build b/build
index fb4ff1984..95d5d5c49 100755
--- a/build
+++ b/build
@@ -164,40 +164,6 @@ for target_platform in ${platform[@]}; do
fi
done
-if [[ ${web_branch} != 'local' ]]; then
- # Initialize submodules
- git submodule update --init --recursive
-
- # configure branch
- pushd MediaBrowser.WebDashboard/jellyfin-web
-
- if ! git diff-index --quiet HEAD --; then
- popd
- echo
- echo "ERROR: Your 'jellyfin-web' submodule working directory is not clean!"
- echo "This script will overwrite your unstaged and unpushed changes."
- echo "Please do development on 'jellyfin-web' outside of the submodule."
- exit 1
- fi
-
- git fetch --all
- # If this is an official branch name, fetch it from origin
- official_branches_regex="^master$|^dev$|^release-.*$|^hotfix-.*$"
- if [[ ${web_branch} =~ ${official_branches_regex} ]]; then
- git checkout origin/${web_branch} || {
- echo "ERROR: 'jellyfin-web' branch 'origin/${web_branch}' is invalid."
- exit 1
- }
- # Otherwise, just check out the local branch (for testing, etc.)
- else
- git checkout ${web_branch} || {
- echo "ERROR: 'jellyfin-web' branch '${web_branch}' is invalid."
- exit 1
- }
- fi
- popd
-fi
-
# Execute each platform and action in order, if said action is enabled
pushd deployment/
for target_platform in ${platform[@]}; do
@@ -214,7 +180,7 @@ for target_platform in ${platform[@]}; do
for target_action in ${action[@]}; do
echo -e ">> Processing action ${target_action}"
if [[ -f ${target_action}.sh && -x ${target_action}.sh ]]; then
- ./${target_action}.sh
+ ./${target_action}.sh web_branch=${web_branch}
fi
done
if [[ -d pkg-dist/ ]]; then
diff --git a/deployment/README.md b/deployment/README.md
index a00cd3e6c..a805f59ca 100644
--- a/deployment/README.md
+++ b/deployment/README.md
@@ -11,10 +11,8 @@ This directory contains the packaging configuration of Jellyfin for multiple pla
### Portable Builds (archives)
-* `debian-x64`: Portable binary archive for Debian amd64 systems.
-* `ubuntu-x64`: Portable binary archive for Ubuntu amd64 systems.
* `linux-x64`: Portable binary archive for generic Linux amd64 systems.
-* `osx-x64`: Portable binary archive for MacOS amd64 systems.
+* `macos`: Portable binary archive for MacOS amd64 systems.
* `win-x64`: Portable binary archive for Windows amd64 systems.
* `win-x86`: Portable binary archive for Windows i386 systems.
@@ -22,10 +20,10 @@ This directory contains the packaging configuration of Jellyfin for multiple pla
These builds are not necessarily run from the `build` script, but are present for other platforms.
-* `framework`: Compiled `.dll` for use with .NET Core runtime on any system.
+* `portable`: Compiled `.dll` for use with .NET Core runtime on any system.
* `docker`: Docker manifests for auto-publishing.
* `unraid`: unRaid Docker template; not built by `build` but imported into unRaid directly.
-* `win-generic`: Portable binary for generic Windows systems.
+* `windows`: Support files and scripts for Windows CI build.
## Package Specification
@@ -62,52 +60,3 @@ These builds are not necessarily run from the `build` script, but are present fo
* Upon completion of the defined actions, at least one output file must be created in the `<platform>/pkg-dist` directory.
* Output files will be moved to the directory `jellyfin-build/<platform>` one directory above the repository root upon completion.
-
-### Common Functions
-
-* A number of common functions are defined in `deployment/common.build.sh` for use by platform scripts.
-
-* Each action script should import the common functions to define a number of standard variables.
-
-* The common variables are:
-
- * `ROOT`: The Jellyfin repostiory root, usually `../..`.
- * `CONFIG`: The .NET config, usually `Release`.
- * `DOTNETRUNTIME`: The .NET `--runtime` value, platform-dependent.
- * `OUTPUT_DIR`: The intermediate output dir, usually `./dist/jellyfin_${VERSION}`.
- * `BUILD_CONTEXT`: The Docker build context, usually `../..`.
- * `DOCKERFILE`: The Dockerfile, usually `Dockerfile` in the platform directory.
- * `IMAGE_TAG`: A tag for the built Docker image.
- * `PKG_DIR`: The final binary output directory for collection, invariably `pkg-dist`.
- * `ARCHIVE_CMD`: The compression/archive command for release archives, usually `tar -xvzf` or `zip`.
-
-#### `get_version`
-
-Reads the version information from `SharedVersion.cs`.
-
-**Arguments:** `ROOT`
-
-#### `build_jellyfin`
-
-Build a standard self-contained binary in the current OS context.
-
-**Arguments:** `ROOT` `CONFIG` `DOTNETRUNTIME` `OUTPUT_DIR`
-
-#### `build_jellyfin_docker`
-
-Build a standard self-contained binary in a Docker image.
-
-**Arguments:** `BUILD_CONTEXT` `DOCKERFILE` `IMAGE_TAG`
-
-#### `clean_jellyfin`
-
-Clean up a build for housekeeping.
-
-**Arguments:** `ROOT` `CONFIG` `OUTPUT_DIR` `PKG_DIR`
-
-#### `package_portable`
-
-Produce a compressed archive.
-
-**Arguments:** `ROOT` `OUTPUT_DIR` `PKG_DIR` `ARCHIVE_CMD`
-
diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/centos-package-x64/Dockerfile
index 38853f173..99f538bc2 100644
--- a/deployment/centos-package-x64/Dockerfile
+++ b/deployment/centos-package-x64/Dockerfile
@@ -8,13 +8,24 @@ ARG SDK_VERSION=2.2
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
-# Prepare CentOS build environment
+# Prepare CentOS environment
RUN yum update -y \
- && yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel \
- && rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \
+ && yum install -y epel-release
+
+# Install build dependencies
+RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs wget git
+
+# Install DotNET SDK
+RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \
&& rpmdev-setuptree \
- && yum install -y dotnet-sdk-${SDK_VERSION} \
- && ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
+ && yum install -y dotnet-sdk-${SDK_VERSION}
+
+# Install yarn package manager
+RUN wget -q -O /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
+ && yum install -y yarn
+
+# Create symlinks and directories
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
&& mkdir -p ${SOURCE_DIR}/SPECS \
&& ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
&& mkdir -p ${SOURCE_DIR}/SOURCES \
diff --git a/deployment/centos-package-x64/clean.sh b/deployment/centos-package-x64/clean.sh
index 7278372e1..31455de0d 100755
--- a/deployment/centos-package-x64/clean.sh
+++ b/deployment/centos-package-x64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/centos-package-x64/docker-build.sh b/deployment/centos-package-x64/docker-build.sh
index cefb1652e..014f582f0 100755
--- a/deployment/centos-package-x64/docker-build.sh
+++ b/deployment/centos-package-x64/docker-build.sh
@@ -8,7 +8,69 @@ set -o xtrace
# Move to source directory
pushd ${SOURCE_DIR}
-ls -al SOURCES/pkg-src/
+VERSION="$( grep '^Version:' ${SOURCE_DIR}/SOURCES/pkg-src/jellyfin.spec | awk '{ print $NF }' )"
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Create RPM source archive
+GNU_TAR=1
+echo "Bundling all sources for RPM build."
+tar \
+--transform "s,^\.,jellyfin-${VERSION}," \
+--exclude='.git*' \
+--exclude='**/.git' \
+--exclude='**/.hg' \
+--exclude='**/.vs' \
+--exclude='**/.vscode' \
+--exclude='deployment' \
+--exclude='**/bin' \
+--exclude='**/obj' \
+--exclude='**/.nuget' \
+--exclude='*.deb' \
+--exclude='*.rpm' \
+-czf "${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-${VERSION}.tar.gz" \
+-C ${SOURCE_DIR} ./ || GNU_TAR=0
+
+if [ $GNU_TAR -eq 0 ]; then
+ echo "The installed tar binary did not support --transform. Using workaround."
+ package_temporary_dir="$( mktemp -d )"
+ mkdir -p "${package_temporary_dir}/jellyfin"
+ # Not GNU tar
+ tar \
+ --exclude='.git*' \
+ --exclude='**/.git' \
+ --exclude='**/.hg' \
+ --exclude='**/.vs' \
+ --exclude='**/.vscode' \
+ --exclude='deployment' \
+ --exclude='**/bin' \
+ --exclude='**/obj' \
+ --exclude='**/.nuget' \
+ --exclude='*.deb' \
+ --exclude='*.rpm' \
+ -czf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
+ -C ${SOURCE_DIR} ./
+ echo "Extracting filtered package."
+ mkdir -p "${package_temporary_dir}/jellyfin-${VERSION}"
+ tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
+ echo "Removing filtered package."
+ rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
+ echo "Repackaging package into final tarball."
+ tar -czf "${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
+ rm -rf ${package_temporary_dir}
+fi
# Build RPM
spectool -g -R SPECS/jellyfin.spec
diff --git a/deployment/centos-package-x64/package.sh b/deployment/centos-package-x64/package.sh
index df5a66580..1b983f49d 100755
--- a/deployment/centos-package-x64/package.sh
+++ b/deployment/centos-package-x64/package.sh
@@ -1,13 +1,15 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
WORKDIR="$( pwd )"
-VERSION="$( grep '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )"
package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
output_dir="${WORKDIR}/pkg-dist"
-pkg_src_dir="${WORKDIR}/pkg-src"
current_user="$( whoami )"
image_name="jellyfin-centos-build"
@@ -21,57 +23,12 @@ else
docker_sudo=""
fi
-# Create RPM source archive
-GNU_TAR=1
+# Prepare temporary package dir
mkdir -p "${package_temporary_dir}"
-echo "Bundling all sources for RPM build."
-tar \
---transform "s,^\.,jellyfin-${VERSION}," \
---exclude='.git*' \
---exclude='**/.git' \
---exclude='**/.hg' \
---exclude='**/.vs' \
---exclude='**/.vscode' \
---exclude='deployment' \
---exclude='**/bin' \
---exclude='**/obj' \
---exclude='**/.nuget' \
---exclude='*.deb' \
---exclude='*.rpm' \
--czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" \
--C "../.." ./ || GNU_TAR=0
-
-if [ $GNU_TAR -eq 0 ]; then
- echo "The installed tar binary did not support --transform. Using workaround."
- mkdir -p "${package_temporary_dir}/jellyfin"
- # Not GNU tar
- tar \
- --exclude='.git*' \
- --exclude='**/.git' \
- --exclude='**/.hg' \
- --exclude='**/.vs' \
- --exclude='**/.vscode' \
- --exclude='deployment' \
- --exclude='**/bin' \
- --exclude='**/obj' \
- --exclude='**/.nuget' \
- --exclude='*.deb' \
- --exclude='*.rpm' \
- -zcf \
- "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
- -C "../.." ./
- echo "Extracting filtered package."
- tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
- echo "Removing filtered package."
- rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
- echo "Repackaging package into final tarball."
- tar -czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
-fi
-
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
# Build the RPMs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the RPMs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
diff --git a/deployment/common.build.sh b/deployment/common.build.sh
deleted file mode 100755
index 000872ea9..000000000
--- a/deployment/common.build.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env bash
-
-set -o errexit
-set -o nounset
-
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-CYAN='\033[0;36m'
-NC='\033[0m' # No Color
-
-DEFAULT_BUILD_CONTEXT="../.."
-DEFAULT_ROOT="."
-DEFAULT_DOTNETRUNTIME="framework"
-DEFAULT_CONFIG="Release"
-DEFAULT_OUTPUT_DIR="dist/jellyfin-git"
-DEFAULT_PKG_DIR="pkg-dist"
-DEFAULT_DOCKERFILE="Dockerfile"
-DEFAULT_ARCHIVE_CMD="tar -xvzf"
-
-# Parse the version from the build.yaml version
-get_version()
-(
- local ROOT=${1-$DEFAULT_ROOT}
- grep "version:" ${ROOT}/build.yaml \
- | sed -E 's/version: "([0-9\.]+.*)"/\1/'
-)
-
-# Run a build
-build_jellyfin()
-(
- ROOT=${1-$DEFAULT_ROOT}
- CONFIG=${2-$DEFAULT_CONFIG}
- DOTNETRUNTIME=${3-$DEFAULT_DOTNETRUNTIME}
- OUTPUT_DIR=${4-$DEFAULT_OUTPUT_DIR}
-
- echo -e "${CYAN}Building jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}"
- if [[ $DOTNETRUNTIME == 'framework' ]]; then
- dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}" "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
- else
- dotnet publish "${ROOT}" --configuration "${CONFIG}" --output="${OUTPUT_DIR}" --self-contained --runtime ${DOTNETRUNTIME} "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
- fi
- EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Build jellyfin in '${ROOT}' for ${DOTNETRUNTIME} with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' FAILED.${NC}"
- fi
-)
-
-# Run a docker
-build_jellyfin_docker()
-(
- BUILD_CONTEXT=${1-$DEFAULT_BUILD_CONTEXT}
- DOCKERFILE=${2-$DEFAULT_DOCKERFILE}
- IMAGE_TAG=${3-"jellyfin:$(git rev-parse --abbrev-ref HEAD)"}
-
- echo -e "${CYAN}Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}'.${NC}"
- docker build -t ${IMAGE_TAG} -f ${DOCKERFILE} ${BUILD_CONTEXT}
- EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Building jellyfin docker image in '${BUILD_CONTEXT}' with Dockerfile '${DOCKERFILE}' and tag '${IMAGE_TAG}' FAILED.${NC}"
- fi
-)
-
-# Clean a build
-clean_jellyfin()
-(
- local ROOT=${1-$DEFAULT_ROOT}
- local CONFIG=${2-$DEFAULT_CONFIG}
- local OUTPUT_DIR=${3-$DEFAULT_OUTPUT_DIR}
- local PKG_DIR=${4-$DEFAULT_PKG_DIR}
- echo -e "${CYAN}Cleaning jellyfin in '${ROOT}'' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}'.${NC}"
- echo -e "${CYAN}Deleting '${OUTPUT_DIR}'${NC}"
- rm -rf "$OUTPUT_DIR"
- echo -e "${CYAN}Deleting '${PKG_DIR}'${NC}"
- rm -rf "$PKG_DIR"
- dotnet clean "${ROOT}" -maxcpucount:1 --configuration ${CONFIG}
- local EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Clean jellyfin in '${ROOT}' with configuration ${CONFIG} and output directory '${OUTPUT_DIR}' failed.${NC}"
- fi
-)
-
-# Packages the output folder into an archive.
-package_portable()
-(
- local ROOT=${1-$DEFAULT_ROOT}
- local OUTPUT_DIR=${2-$DEFAULT_OUTPUT_DIR}
- local PKG_DIR=${3-$DEFAULT_PKG_DIR}
- local ARCHIVE_CMD=${4-$DEFAULT_ARCHIVE_CMD}
- # Package portable build result
- if [ -d ${OUTPUT_DIR} ]; then
- echo -e "${CYAN}Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}'.${NC}"
- mkdir -p ${PKG_DIR}
- tar -zcvf "${PKG_DIR}/`basename "${OUTPUT_DIR}"`.portable.tar.gz" -C "`dirname "${OUTPUT_DIR}"`" "`basename "${OUTPUT_DIR}"`"
- local EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' FAILED.${NC}"
- fi
- else
- echo -e "${RED}[FAIL] Build artifacts do not exist for ${OUTPUT_DIR}. Run build.sh first.${NC}"
- fi
-)
-
diff --git a/deployment/debian-package-arm64/Dockerfile.amd64 b/deployment/debian-package-arm64/Dockerfile.amd64
index a05581d71..5644c1470 100644
--- a/deployment/debian-package-arm64/Dockerfile.amd64
+++ b/deployment/debian-package-arm64/Dockerfile.amd64
@@ -29,6 +29,12 @@ RUN dpkg --add-architecture arm64 \
&& cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \
&& apt-get install -y gcc-8-source libstdc++-8-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++-8-dev:arm64
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/debian-package-arm64/clean.sh b/deployment/debian-package-arm64/clean.sh
index ac143c3d0..e7bfdf8b4 100755
--- a/deployment/debian-package-arm64/clean.sh
+++ b/deployment/debian-package-arm64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/debian-package-arm64/docker-build.sh b/deployment/debian-package-arm64/docker-build.sh
index 1c75ece8e..7a13bafcb 100755
--- a/deployment/debian-package-arm64/docker-build.sh
+++ b/deployment/debian-package-arm64/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
dpkg-buildpackage -us -uc -aarm64
diff --git a/deployment/debian-package-arm64/package.sh b/deployment/debian-package-arm64/package.sh
index ce02b1af5..209198218 100755
--- a/deployment/debian-package-arm64/package.sh
+++ b/deployment/debian-package-arm64/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
ARCH="$( arch )"
WORKDIR="$( pwd )"
@@ -35,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/debian-package-armhf/Dockerfile.amd64 b/deployment/debian-package-armhf/Dockerfile.amd64
index 04dffd000..b05f10def 100644
--- a/deployment/debian-package-armhf/Dockerfile.amd64
+++ b/deployment/debian-package-armhf/Dockerfile.amd64
@@ -29,6 +29,12 @@ RUN dpkg --add-architecture armhf \
&& cd cross-gcc-packages-amd64/cross-gcc-8-armhf \
&& apt-get install -y gcc-8-source libstdc++-8-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++-8-dev:armhf
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/debian-package-armhf/Dockerfile.armhf b/deployment/debian-package-armhf/Dockerfile.armhf
index ed3199435..6729d8f38 100644
--- a/deployment/debian-package-armhf/Dockerfile.armhf
+++ b/deployment/debian-package-armhf/Dockerfile.armhf
@@ -21,6 +21,12 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/3cb1d917-19cc-4
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/debian-package-armhf/clean.sh b/deployment/debian-package-armhf/clean.sh
index 3898110af..35a3d3e9a 100755
--- a/deployment/debian-package-armhf/clean.sh
+++ b/deployment/debian-package-armhf/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/debian-package-armhf/docker-build.sh b/deployment/debian-package-armhf/docker-build.sh
index df35345bd..c48ccb3fb 100755
--- a/deployment/debian-package-armhf/docker-build.sh
+++ b/deployment/debian-package-armhf/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
dpkg-buildpackage -us -uc -aarmhf
diff --git a/deployment/debian-package-armhf/package.sh b/deployment/debian-package-armhf/package.sh
index 4393fb834..4a27dd828 100755
--- a/deployment/debian-package-armhf/package.sh
+++ b/deployment/debian-package-armhf/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
ARCH="$( arch )"
WORKDIR="$( pwd )"
@@ -35,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/debian-package-x64/Dockerfile b/deployment/debian-package-x64/Dockerfile
index 45befd76b..2f97d3944 100644
--- a/deployment/debian-package-x64/Dockerfile
+++ b/deployment/debian-package-x64/Dockerfile
@@ -21,6 +21,12 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-4
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/debian-package-x64/clean.sh b/deployment/debian-package-x64/clean.sh
index b2960fcb3..4e507bcb2 100755
--- a/deployment/debian-package-x64/clean.sh
+++ b/deployment/debian-package-x64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/debian-package-x64/docker-build.sh b/deployment/debian-package-x64/docker-build.sh
index 9781879f6..97bc45a06 100755
--- a/deployment/debian-package-x64/docker-build.sh
+++ b/deployment/debian-package-x64/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
dpkg-buildpackage -us -uc
diff --git a/deployment/debian-package-x64/package.sh b/deployment/debian-package-x64/package.sh
index 2530e253b..5a416959a 100755
--- a/deployment/debian-package-x64/package.sh
+++ b/deployment/debian-package-x64/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
WORKDIR="$( pwd )"
@@ -24,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index 54d4cffba..3d2cb770f 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -51,297 +51,3 @@ jellyfin (10.3.0-1) unstable; urgency=medium
* New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 19 Apr 2019 14:24:29 -0400
-
-jellyfin (10.2.2-1) unstable; urgency=medium
-
- * jellyfin:
- * PR968 Release 10.2.z copr autobuild
- * PR964 Install the dotnet runtime package in Fedora build
- * PR979 Build Package releases without debug turned on
- * PR990 Fix slow local image validation
- * PR991 Fix the ffmpeg compatibility
- * PR992 Add Debian armhf (Raspberry Pi) build plus crossbuild
- * PR998 Set EnableRaisingEvents to true for processes that require it
- * PR1017 Set ffmpeg+ffprobe paths in Docker container
- * jellyfin-web:
- * PR152 Go back on Media stop
- * PR156 Fix volume slider not working on nowplayingbar
-
- -- Jellyfin Packaging Team <packaging@jellyfin.org> Thu, 28 Feb 2019 15:32:16 -0500
-
-jellyfin (10.2.1-1) unstable; urgency=medium
-
- * jellyfin:
- * PR920 Fix cachedir missing from Docker container
- * PR924 Use the movie name instead of folder name
- * PR933 Semi-revert to prefer old movie grouping behaviour
- * PR948 Revert movie matching (supercedes PR933, PR924, PR739)
- * PR960 Use jellyfin/ffmpeg image
- * jellyfin-web:
- * PR136 Re-add OpenSubtitles configuration page
- * PR137 Replace HeaderEmbyServer with HeaderJellyfinServer on plugincatalog
- * PR138 Remove left-over JS for Customize Home Screen
- * PR141 Exit fullscreen automatically after video playback ends
-
- -- Jellyfin Packaging Team <packaging@jellyfin.org> Wed, 20 Feb 2019 11:36:16 -0500
-
-jellyfin (10.2.0-2) unstable; urgency=medium
-
- * jellyfin:
- * PR452 Use EF Core for Activity database
- * PR535 Clean up streambuilder
- * PR655 Support trying local branches in submodule
- * PR656 Do some logging in MediaInfoService
- * PR657 Remove conditions that are always true/false
- * PR661 Fix NullRef from progress report
- * PR663 Use TagLibSharp Nuget package
- * PR664 Revert "Fix segment_time_delta for ffmpeg 4.1"
- * PR666 Add cross-platform build for arm64
- * PR668 Return Audio objects from MusicAlbum.Tracks
- * PR671 Set EnableRaisingEvents correctly
- * PR672 Remove unconditional caching, modified since header and use ETags
- * PR677 Fix arm32 Docker
- * PR681 Fix Windows build script errors + pin ffmpeg to 4.0
- * PR686 Disable some StyleCop warnings
- * PR687 Fix some analyzer warnings
- * PR689 Fix RPM package build for fedora
- * PR702 Fix debug build on windows
- * PR706 Make another docker layer reusable
- * PR709 Fix always null expressions
- * PR710 Fix a spelling mistake
- * PR711 Remove remnants of system events
- * PR713 Fix empty statement in DidlBuilder.cs
- * PR716 Remove more compile time warnings
- * PR721 Change image dimentions from double to int
- * PR723 Minor improvements to db code
- * PR724 Move Skia back into it's own project
- * PR726 Clean up IFileSystem wrappers around stdlib.
- * PR727 Change default aspect ratio to 2/3 from 0
- * PR728 Use ffmpeg from jrottenberg/ffmpeg
- * PR732 Reworked LocalizationManager to load data async
- * PR733 Remove unused function
- * PR734 Fix more analyzer warnings
- * PR736 Start startup tasks async
- * PR737 Add AssemblyInfo for Jellyfin.Drawing.Skia
- * PR739 Change multi version logic for movies
- * PR740 Remove code for pre-installed plugins & properly check if file exists
- * PR756 Make cache dir configurable
- * PR757 Fix default aspect ratio
- * PR758 Add password field to initial setup
- * PR764 Remove dead code, made some functions properly async
- * PR769 Fix conditions where the ! was swallowed in #726
- * PR774 reimplement support for plugin repository
- * PR782 Remove commented file MediaBrowser.LocalMetadata.Savers.PersonXmlSaver
- * PR783 Update builds to use #749 and #756
- * PR788 Fix more warnings
- * PR794 Remove MoreLINQ
- * PR797 Fix all warnings
- * PR798 Cleanup around the api endpoints
- * PR800 Add CentOS and update rpm spec for the cachedir option
- * PR802 Fix build error
- * PR804 Handle new option parser properly
- * PR805 Add weblate translation status to README
- * PR807 Fix restart script in OS packages
- * PR810 Fix loading of rating files
- * PR812 Fix up the explicit docs links in the README
- * PR819 Some small changes in Device.cs and DidlBuilder.cs
- * PR822 Complete rename ImageSize -> ImageDimensions
- * PR824 Improved Docker pkgbuild
- * PR831 Move some arrays to generics
- * PR833 Add await to GetCountries in LocalizationService
- * PR834 Add donation badge and reorganize badges
- * PR838 Quick style fix
- * PR840 Fix more warnings
- * PR841 Fix OC badge to all and add forum badge
- * PR842 Use VAAPI-enabled ffmpeg
- * PR852 Use SQLitePCL.pretty.netstandard on NuGet
- * PR853 Fix poor handling of cache directories
- * PR864: Add support for ZIP plugin archives
- * PR868: Fix audio streaming via BaseProgressiveStreamingService
- * PR869: Remove DLL support and require all packages/plugins to be zip archives
- * PR872: Fix potential NullReferenceException
- * PR890: Drop ETag and use Last-Modified header
- * PR892: Add jellyfin-ffmpeg and versioning to package deps
- * PR899: DLNA: Fix race condition leading to missing device names
- * PR901: Properly dispose HttpWebResponse when the request failed to avoid 'too many open files'
- * PR909: Fix docker arm builds
- * PR910: Enhance Dockerfiles
- * PR911: Checkout submodules in Docker Hub hook
- * jellyfin-web:
- * PR51 remove more code for sync and camera roll
- * PR56 Use English for fallback translations and clean up language files
- * PR58 Css slider fixes
- * PR62 remove BOM markers
- * PR65 Fix profile image not being shown on profile page
- * PR73 Dev sync
- * PR74 Add download menu option to media items
- * PR75 User profile fixes
- * PR76 Fix syntax error caused by deminification
- * PR79 Remove unused Connect related from the frontend
- * PR80 Remove games
- * PR92 Added frontend support for a password field on setup
- * PR94 Update british strings
- * PR95 add display language option back
- * PR112 Removed seasonal theme support
- * PR116 Consolidate all strings into a single file per language
- * PR117 Fix volume slider behavior
- * PR118 Enable and fix PiP for Safari
- * PR119 Make the toggle track visible on all themes
- * PR121 Fix syntax error in site.js
- * PR127 Change sharedcomponents module to core
- * PR135 Make sure fallback culture is always available
-
- -- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 15 Feb 2019 20:51:25 -0500
-
-jellyfin (10.1.0-1) unstable; urgency=medium
-
- * jellyfin:
- * PR335 Build scripts and build system consolidation.
- * PR424 add jellyfin-web as submodule
- * PR455 Cleanup some small things
- * PR458 Clean up several minor issues and add TODOs
- * PR506 Removing tabs and trailing whitespace
- * PR508 Update internal versioning and user agents.
- * PR516 Remove useless properties from IEnvironmentInfo
- * PR520 Fix potential bug where aspect ratio would be incorrectly calculated
- * PR534 Add linux-arm and linux-arm64 native NuGet dependency.
- * PR540 Update Emby API keys to our own
- * PR541 Change ItemId to Guid in ProviderManager
- * PR556 Fix "Password Reset by PIN" page
- * PR562 Fix error with uppercase photo extension and fix typo in a log line
- * PR563 Update dev from master
- * PR566 Avoid printing stacktrace when bind to port 1900 fails
- * PR567 Shutdown gracefully when recieving a termination signal
- * PR571 Add more NuGet metadata properties
- * PR575 Reformat all C# server code to conform with code standards
- * PR576 Add code analysers for debug builds
- * PR580 Fix Docker build
- * PR582 Replace custom image parser with Skia
- * PR587 Add nuget info to Emby.Naming
- * PR589 Ensure config and log folders exist
- * PR596 Fix indentation for xml files
- * PR598 Remove MediaBrowser.Text for license violations and hackiness
- * PR606 Slim down docker image
- * PR613 Update MediaEncoding
- * PR616 Add Swagger documentation
- * PR619 Really slim down Docker container
- * PR621 Minor improvements to library scan code
- * PR622 Add unified build script and bump_version script
- * PR623 Replaced injections of ILogger with ILoggerFactory
- * PR625 Update taglib-sharp
- * PR626 Fix extra type name in parameter, add out keyword
- * PR627 Use string for ApplicationVersion
- * PR628 Update Product Name (User-Agent)
- * PR629 Fix subtitle converter misinterpreting 0 valued endTimeTicks
- * PR631 Cleanup ImageProcessor and SkiaEncoder
- * PR634 Replace our TVDB key with @drakus72's which is V1
- * PR636 Allow subtitle extraction and conversion in direct streaming
- * PR637 Remove unused font
- * PR638 Removed XmlTv testfiles and nuget install
- * PR646: Fix infinite loop bug on subtitle.m3u8 request
- * PR655: Support trying local branches in submodule
- * PR661: Fix NullRef from progress report
- * PR666: Add cross-platform build for arm64
- * jellyfin-web:
- * PR1: Change webcomponents to non-minified version
- * PR4: Fix user profile regression
- * PR6: Make icon into proper ico and large PNG
- * PR7: Fix firefox failing to set password for users with no password set
- * PR8: Remove premiere stuff and fix crashes caused by earlier removals
- * PR12: Fix return from PIN reset to index.html
- * PR13: Send android clients to select server before login
- * PR14: Reimplement page to add server
- * PR16: Fix spinning circle at the end of config wizard
- * PR17: Fix directorybrower not resetting scroll
- * PR19: Set union merge for CONTRIBUTORS.md
- * PR20: Show album thumbnail and artist image in page itemdetail
- * PR26: Make the card titles clickable
- * PR27: Stop pagination and adding a library from being able to trigger multiple times
- * PR28: Add transparent nav bar to BlueRadiance theme CSS
- * PR29: Clean up imageuploader
- * PR30: Remove iap and simplify registrationservices
- * PR36: Open videos in fullscreen on android devices
- * PR37: Remove broken features from web interface
- * PR38: Fix inconsistent UI coloring around settings drawer
- * PR39: Remove back button from dashboard and metadata manager
- * PR42: Fix Home backdrop not loading
- * PR43: Filter videos by audio stream language
- * PR44: Remove filter from library collection type options
- * PR45: Fix data-backbutton logic
- * PR46: Minor changes to navbar elements
- * PR48: Remove Sync code
- * PR52: Fix progress color
- * PR53: Fix user tabs color
- * PR54: Add back button to server dashboard
-
- -- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 20 Jan 2019 23:19:46 -0500
-
-jellyfin (10.0.2-1) unstable; urgency=medium
-
- * Hotfix release
- * jellyfin/jellyfin-web#23: Update Chromecast app ID [via direct commit]
- * #540: Update Emby API keys to our own
- * #541: Change ItemId to Guid in ProviderManager
- * #566: Avoid printing stacktrace when bind to port 1900 fails
-
- -- Joshua Boniface <joshua@boniface.me> Sat, 19 Jan 2019 01:19:59 -0500
-
-jellyfin (10.0.1-1) unstable; urgency=medium
-
- * Hotfix release, corrects several small bugs from 10.0.0
- * #512: Fix CONTRIBUTORS.md formatting
- * #501: Fix regression in integer divisions in latest movies category
- * #498: Change contributing link in settings to readthedocs.io
- * #493: Remove unused values.txt resource
- * #491: Fix userprofile.js crash
- * #519: Fix the DecodeJfif function to get proper image sizes
- * #486: Add NuGet package info to plugin projects
-
- -- Joshua Boniface <joshua@boniface.me> Tue, 08 Jan 2019 20:06:01 -0500
-
-jellyfin (10.0.0-1) unstable; urgency=medium
-
- * The first Jellyfin release under our new versioning scheme
- * Numerous bugfixes and code readability improvements
- * Updated logging configuration, including flag for it and configdir
- * Updated theming including logo
- * Dozens of other improvements as documented in GitHub pull request #419
-
- -- Joshua Boniface <joshua@boniface.me> Sat, 05 Jan 2019 15:39:25 -0500
-
-jellyfin (3.5.2-5) unstable; urgency=medium
-
- * Fully GPL'd release - remove tainted code from MediaBrowser.Common
- * Several code cleanups and tweaks
-
- -- Joshua Boniface <joshua@boniface.me> Fri, 28 Dec 2018 10:26:30 -0500
-
-jellyfin (3.5.2-4) unstable; urgency=medium
-
- * Correct manifest.json bug and vdpau
-
- -- Joshua Boniface <joshua@boniface.me> Thu, 20 Dec 2018 18:31:43 -0500
-
-jellyfin (3.5.2-3) unstable; urgency=medium
-
- * Correct several bugs in 3.5.2-2 packaging
-
- -- Joshua Boniface <joshua@boniface.me> Sat, 15 Dec 2018 18:17:32 -0500
-
-jellyfin (3.5.2-2) unstable; urgency=medium
-
- * Major code updates related to rebranding and cleanup
-
- -- Joshua Boniface <joshua@boniface.me> Fri, 14 Dec 2018 00:07:46 -0500
-
-jellyfin (3.5.2-1) unstable; urgency=medium
-
- * Add ffmpeg dependency and cleanup work
-
- -- Joshua Boniface <joshua@boniface.me> Tue, 11 Dec 2018 20:55:32 -0500
-
-jellyfin (3.5.2) unstable; urgency=medium
-
- * Rename from emby-server on version 3.5.2
-
- -- Joshua Boniface <joshua@boniface.me> Sun, 9 Dec 2018 15:20:58 -0400
diff --git a/deployment/docker/build.sh b/deployment/docker/build.sh
deleted file mode 100755
index 444208c85..000000000
--- a/deployment/docker/build.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-build_jellyfin_docker ../.. ../../Dockerfile jellyfin:amd64-${VERSION}
-
-build_jellyfin_docker ../.. ../../Dockerfile.arm jellyfin:arm-${VERSION}
-
-#build_jellyfin_docker ../.. ../../Dockerfile.arm64v8 jellyfin:arm64v8-${VERSION}
-#build_jellyfin_docker ../.. ../../Dockerfile.arm32v7 jellyfin:arm32v7-${VERSION}
diff --git a/deployment/docker/package.sh b/deployment/docker/package.sh
deleted file mode 100755
index d74426e2f..000000000
--- a/deployment/docker/package.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-docker manifest create jellyfin:${VERSION} jellyfin:amd64-${VERSION} jellyfin:arm32v7-${VERSION} jellyfin:arm64v8-${VERSION}
-docker manifest annotate jellyfin:amd64-${VERSION} --os linux --arch amd64
-#docker manifest annotate jellyfin:arm32v7-${VERSION} --os linux --arch arm --variant armv7
-#docker manifest annotate jellyfin:arm64v8-${VERSION} --os linux --arch arm64 --variant armv8
-
-#TODO publish.sh - docker manifest push jellyfin:${VERSION}
diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/fedora-package-x64/Dockerfile
index 397c944ea..b8226b173 100644
--- a/deployment/fedora-package-x64/Dockerfile
+++ b/deployment/fedora-package-x64/Dockerfile
@@ -8,13 +8,23 @@ ARG SDK_VERSION=2.2
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
-# Prepare Fedora build environment
-RUN dnf update -y \
- && dnf install -y @buildsys-build rpmdevtools dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel \
- && dnf copr enable -y @dotnet-sig/dotnet \
+# Prepare Fedora environment
+RUN dnf update -y
+
+# Install build dependencies
+RUN dnf install -y @buildsys-build rpmdevtools dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs wget git
+
+# Install DotNET SDK
+RUN dnf copr enable -y @dotnet-sig/dotnet \
&& rpmdev-setuptree \
- && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} \
- && ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
+ && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION}
+
+# Install yarn package manager
+RUN wget -q -O /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
+ && dnf install -y yarn
+
+# Create symlinks and directories
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
&& mkdir -p ${SOURCE_DIR}/SPECS \
&& ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \
&& mkdir -p ${SOURCE_DIR}/SOURCES \
diff --git a/deployment/fedora-package-x64/clean.sh b/deployment/fedora-package-x64/clean.sh
index 408167e49..700c8f1bb 100755
--- a/deployment/fedora-package-x64/clean.sh
+++ b/deployment/fedora-package-x64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/fedora-package-x64/create_tarball.sh b/deployment/fedora-package-x64/create_tarball.sh
deleted file mode 100755
index e8301c989..000000000
--- a/deployment/fedora-package-x64/create_tarball.sh
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env bash
-
-# shellcheck disable=SC1091
-source ../common.build.sh
-
-WORKDIR="$( pwd )"
-VERSION="$( sed -ne '/^Version:/s/.* *//p' "${WORKDIR}"/pkg-src/jellyfin.spec )"
-
-package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
-pkg_src_dir="${WORKDIR}/pkg-src"
-
-GNU_TAR=1
-echo "Bundling all sources for RPM build."
-tar \
---transform "s,^\.,jellyfin-${VERSION}," \
---exclude='.git*' \
---exclude='**/.git' \
---exclude='**/.hg' \
---exclude='**/.vs' \
---exclude='**/.vscode' \
---exclude='deployment' \
---exclude='**/bin' \
---exclude='**/obj' \
---exclude='**/.nuget' \
---exclude='*.deb' \
---exclude='*.rpm' \
--czf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" \
--C "../.." ./ || GNU_TAR=0
-
-if [ $GNU_TAR -eq 0 ]; then
- echo "The installed tar binary did not support --transform. Using workaround."
- mkdir -p "${package_temporary_dir}/jellyfin"{,-"${VERSION}"}
- # Not GNU tar
- tar \
- --exclude='.git*' \
- --exclude='**/.git' \
- --exclude='**/.hg' \
- --exclude='**/.vs' \
- --exclude='**/.vscode' \
- --exclude='deployment' \
- --exclude='**/bin' \
- --exclude='**/obj' \
- --exclude='**/.nuget' \
- --exclude='*.deb' \
- --exclude='*.rpm' \
- -zcf \
- "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
- -C "../.." ./
- echo "Extracting filtered package."
- tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
- echo "Removing filtered package."
- rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
- echo "Repackaging package into final tarball."
- tar -czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
-fi
diff --git a/deployment/fedora-package-x64/docker-build.sh b/deployment/fedora-package-x64/docker-build.sh
index cefb1652e..014f582f0 100755
--- a/deployment/fedora-package-x64/docker-build.sh
+++ b/deployment/fedora-package-x64/docker-build.sh
@@ -8,7 +8,69 @@ set -o xtrace
# Move to source directory
pushd ${SOURCE_DIR}
-ls -al SOURCES/pkg-src/
+VERSION="$( grep '^Version:' ${SOURCE_DIR}/SOURCES/pkg-src/jellyfin.spec | awk '{ print $NF }' )"
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Create RPM source archive
+GNU_TAR=1
+echo "Bundling all sources for RPM build."
+tar \
+--transform "s,^\.,jellyfin-${VERSION}," \
+--exclude='.git*' \
+--exclude='**/.git' \
+--exclude='**/.hg' \
+--exclude='**/.vs' \
+--exclude='**/.vscode' \
+--exclude='deployment' \
+--exclude='**/bin' \
+--exclude='**/obj' \
+--exclude='**/.nuget' \
+--exclude='*.deb' \
+--exclude='*.rpm' \
+-czf "${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-${VERSION}.tar.gz" \
+-C ${SOURCE_DIR} ./ || GNU_TAR=0
+
+if [ $GNU_TAR -eq 0 ]; then
+ echo "The installed tar binary did not support --transform. Using workaround."
+ package_temporary_dir="$( mktemp -d )"
+ mkdir -p "${package_temporary_dir}/jellyfin"
+ # Not GNU tar
+ tar \
+ --exclude='.git*' \
+ --exclude='**/.git' \
+ --exclude='**/.hg' \
+ --exclude='**/.vs' \
+ --exclude='**/.vscode' \
+ --exclude='deployment' \
+ --exclude='**/bin' \
+ --exclude='**/obj' \
+ --exclude='**/.nuget' \
+ --exclude='*.deb' \
+ --exclude='*.rpm' \
+ -czf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
+ -C ${SOURCE_DIR} ./
+ echo "Extracting filtered package."
+ mkdir -p "${package_temporary_dir}/jellyfin-${VERSION}"
+ tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
+ echo "Removing filtered package."
+ rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
+ echo "Repackaging package into final tarball."
+ tar -czf "${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
+ rm -rf ${package_temporary_dir}
+fi
# Build RPM
spectool -g -R SPECS/jellyfin.spec
diff --git a/deployment/fedora-package-x64/package.sh b/deployment/fedora-package-x64/package.sh
index e659ee5e9..ae6962dd1 100755
--- a/deployment/fedora-package-x64/package.sh
+++ b/deployment/fedora-package-x64/package.sh
@@ -1,13 +1,15 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
WORKDIR="$( pwd )"
-VERSION="$( grep '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )"
package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
output_dir="${WORKDIR}/pkg-dist"
-pkg_src_dir="${WORKDIR}/pkg-src"
current_user="$( whoami )"
image_name="jellyfin-fedora-build"
@@ -21,14 +23,12 @@ else
docker_sudo=""
fi
-./create_tarball.sh
-
# Prepare temporary package dir
mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
# Build the RPMs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the RPMs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/rpm/* "${output_dir}"
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index d591a54e1..b4cd5b2be 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -158,213 +158,3 @@ fi
- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1
* Fri Apr 19 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0
-* Thu Feb 28 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
-- jellyfin:
-- PR968 Release 10.2.z copr autobuild
-- PR964 Install the dotnet runtime package in Fedora build
-- PR979 Build Package releases without debug turned on
-- PR990 Fix slow local image validation
-- PR991 Fix the ffmpeg compatibility
-- PR992 Add Debian armhf (Raspberry Pi) build plus crossbuild
-- PR998 Set EnableRaisingEvents to true for processes that require it
-- PR1017 Set ffmpeg+ffprobe paths in Docker container
-- jellyfin-web:
-- PR152 Go back on Media stop
-- PR156 Fix volume slider not working on nowplayingbar
-* Wed Feb 20 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
-- jellyfin:
-- PR920 Fix cachedir missing from Docker container
-- PR924 Use the movie name instead of folder name
-- PR933 Semi-revert to prefer old movie grouping behaviour
-- PR948 Revert movie matching (supercedes PR933, PR924, PR739)
-- PR960 Use jellyfin/ffmpeg image
-- jellyfin-web:
-- PR136 Re-add OpenSubtitles configuration page
-- PR137 Replace HeaderEmbyServer with HeaderJellyfinServer on plugincatalog
-- PR138 Remove left-over JS for Customize Home Screen
-- PR141 Exit fullscreen automatically after video playback ends
-* Fri Feb 15 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
-- jellyfin:
-- PR452 Use EF Core for Activity database
-- PR535 Clean up streambuilder
-- PR655 Support trying local branches in submodule
-- PR656 Do some logging in MediaInfoService
-- PR657 Remove conditions that are always true/false
-- PR661 Fix NullRef from progress report
-- PR663 Use TagLibSharp Nuget package
-- PR664 Revert "Fix segment_time_delta for ffmpeg 4.1"
-- PR666 Add cross-platform build for arm64
-- PR668 Return Audio objects from MusicAlbum.Tracks
-- PR671 Set EnableRaisingEvents correctly
-- PR672 Remove unconditional caching, modified since header and use ETags
-- PR677 Fix arm32 Docker
-- PR681 Fix Windows build script errors + pin ffmpeg to 4.0
-- PR686 Disable some StyleCop warnings
-- PR687 Fix some analyzer warnings
-- PR689 Fix RPM package build for fedora
-- PR702 Fix debug build on windows
-- PR706 Make another docker layer reusable
-- PR709 Fix always null expressions
-- PR710 Fix a spelling mistake
-- PR711 Remove remnants of system events
-- PR713 Fix empty statement in DidlBuilder.cs
-- PR716 Remove more compile time warnings
-- PR721 Change image dimentions from double to int
-- PR723 Minor improvements to db code
-- PR724 Move Skia back into it's own project
-- PR726 Clean up IFileSystem wrappers around stdlib.
-- PR727 Change default aspect ratio to 2/3 from 0
-- PR728 Use ffmpeg from jrottenberg/ffmpeg
-- PR732 Reworked LocalizationManager to load data async
-- PR733 Remove unused function
-- PR734 Fix more analyzer warnings
-- PR736 Start startup tasks async
-- PR737 Add AssemblyInfo for Jellyfin.Drawing.Skia
-- PR739 Change multi version logic for movies
-- PR740 Remove code for pre-installed plugins & properly check if file exists
-- PR756 Make cache dir configurable
-- PR757 Fix default aspect ratio
-- PR758 Add password field to initial setup
-- PR764 Remove dead code, made some functions properly async
-- PR769 Fix conditions where the ! was swallowed in #726
-- PR774 reimplement support for plugin repository
-- PR782 Remove commented file MediaBrowser.LocalMetadata.Savers.PersonXmlSaver
-- PR783 Update builds to use #749 and #756
-- PR788 Fix more warnings
-- PR794 Remove MoreLINQ
-- PR797 Fix all warnings
-- PR798 Cleanup around the api endpoints
-- PR800 Add CentOS and update rpm spec for the cachedir option
-- PR802 Fix build error
-- PR804 Handle new option parser properly
-- PR805 Add weblate translation status to README
-- PR807 Fix restart script in OS packages
-- PR810 Fix loading of rating files
-- PR812 Fix up the explicit docs links in the README
-- PR819 Some small changes in Device.cs and DidlBuilder.cs
-- PR822 Complete rename ImageSize -> ImageDimensions
-- PR824 Improved Docker pkgbuild
-- PR831 Move some arrays to generics
-- PR833 Add await to GetCountries in LocalizationService
-- PR834 Add donation badge and reorganize badges
-- PR838 Quick style fix
-- PR840 Fix more warnings
-- PR841 Fix OC badge to all and add forum badge
-- PR842 Use VAAPI-enabled ffmpeg
-- PR852 Use SQLitePCL.pretty.netstandard on NuGet
-- PR853 Fix poor handling of cache directories
-- PR864 Add support for ZIP plugin archives
-- PR868 Fix audio streaming via BaseProgressiveStreamingService
-- PR869 Remove DLL support and require all packages/plugins to be zip archives
-- PR872 Fix potential NullReferenceException
-- PR899: DLNA: Fix race condition leading to missing device names
-- PR890 Drop ETag and use Last-Modified header
-- PR892: Add jellyfin-ffmpeg and versioning to package deps
-- PR901: Properly dispose HttpWebResponse when the request failed to avoid 'too many open files'
-- PR909: Fix docker arm builds
-- PR910: Enhance Dockerfiles
-- PR911: Checkout submodules in Docker Hub hook
-- jellyfin-web:
-- PR51 remove more code for sync and camera roll
-- PR56 Use English for fallback translations and clean up language files
-- PR58 Css slider fixes
-- PR62 remove BOM markers
-- PR65 Fix profile image not being shown on profile page
-- PR73 Dev sync
-- PR74 Add download menu option to media items
-- PR75 User profile fixes
-- PR76 Fix syntax error caused by deminification
-- PR79 Remove unused Connect related from the frontend
-- PR80 Remove games
-- PR92 Added frontend support for a password field on setup
-- PR94 Update british strings
-- PR95 add display language option back
-- PR112 Removed seasonal theme support
-- PR116 Consolidate all strings into a single file per language
-- PR117 Fix volume slider behavior
-- PR118 Enable and fix PiP for Safari
-- PR119 Make the toggle track visible on all themes
-- PR121 Fix syntax error in site.js
-- PR127 Change sharedcomponents module to core
-- PR135 Make sure fallback culture is always available
-* Sun Jan 20 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
-- jellyfin:
-- PR335 Build scripts and build system consolidation.
-- PR424 add jellyfin-web as submodule
-- PR455 Cleanup some small things
-- PR458 Clean up several minor issues and add TODOs
-- PR506 Removing tabs and trailing whitespace
-- PR508 Update internal versioning and user agents.
-- PR516 Remove useless properties from IEnvironmentInfo
-- PR520 Fix potential bug where aspect ratio would be incorrectly calculated
-- PR534 Add linux-arm and linux-arm64 native NuGet dependency.
-- PR540 Update Emby API keys to our own
-- PR541 Change ItemId to Guid in ProviderManager
-- PR556 Fix "Password Reset by PIN" page
-- PR562 Fix error with uppercase photo extension and fix typo in a log line
-- PR563 Update dev from master
-- PR566 Avoid printing stacktrace when bind to port 1900 fails
-- PR567 Shutdown gracefully when recieving a termination signal
-- PR571 Add more NuGet metadata properties
-- PR575 Reformat all C# server code to conform with code standards
-- PR576 Add code analysers for debug builds
-- PR580 Fix Docker build
-- PR582 Replace custom image parser with Skia
-- PR587 Add nuget info to Emby.Naming
-- PR589 Ensure config and log folders exist
-- PR596 Fix indentation for xml files
-- PR598 Remove MediaBrowser.Text for license violations and hackiness
-- PR606 Slim down docker image
-- PR613 Update MediaEncoding
-- PR616 Add Swagger documentation
-- PR619 Really slim down Docker container
-- PR621 Minor improvements to library scan code
-- PR622 Add unified build script and bump_version script
-- PR623 Replaced injections of ILogger with ILoggerFactory
-- PR625 Update taglib-sharp
-- PR626 Fix extra type name in parameter, add out keyword
-- PR627 Use string for ApplicationVersion
-- PR628 Update Product Name (User-Agent)
-- PR629 Fix subtitle converter misinterpreting 0 valued endTimeTicks
-- PR631 Cleanup ImageProcessor and SkiaEncoder
-- PR634 Replace our TVDB key with @drakus72's which is V1
-- PR636 Allow subtitle extraction and conversion in direct streaming
-- PR637 Remove unused font
-- PR638 Removed XmlTv testfiles and nuget install
-- PR646: Fix infinite loop bug on subtitle.m3u8 request
-- PR655: Support trying local branches in submodule
-- PR661: Fix NullRef from progress report
-- PR666: Add cross-platform build for arm64
-- jellyfin-web:
-- PR1: Change webcomponents to non-minified version
-- PR4: Fix user profile regression
-- PR6: Make icon into proper ico and large PNG
-- PR7: Fix firefox failing to set password for users with no password set
-- PR8: Remove premiere stuff and fix crashes caused by earlier removals
-- PR12: Fix return from PIN reset to index.html
-- PR13: Send android clients to select server before login
-- PR14: Reimplement page to add server
-- PR16: Fix spinning circle at the end of config wizard
-- PR17: Fix directorybrower not resetting scroll
-- PR19: Set union merge for CONTRIBUTORS.md
-- PR20: Show album thumbnail and artist image in page itemdetail
-- PR26: Make the card titles clickable
-- PR27: Stop pagination and adding a library from being able to trigger multiple times
-- PR28: Add transparent nav bar to BlueRadiance theme CSS
-- PR29: Clean up imageuploader
-- PR30: Remove iap and simplify registrationservices
-- PR36: Open videos in fullscreen on android devices
-- PR37: Remove broken features from web interface
-- PR38: Fix inconsistent UI coloring around settings drawer
-- PR39: Remove back button from dashboard and metadata manager
-- PR42: Fix Home backdrop not loading
-- PR43: Filter videos by audio stream language
-- PR44: Remove filter from library collection type options
-- PR45: Fix data-backbutton logic
-- PR46: Minor changes to navbar elements
-- PR48: Remove Sync code
-- PR52: Fix progress color
-- PR53: Fix user tabs color
-- PR54: Add back button to server dashboard
-* Fri Jan 11 2019 Thomas Büttner <thomas@vergesslicher.tech> - 10.0.2-1
-- TODO Changelog for 10.0.2
diff --git a/deployment/linux-x64/Dockerfile b/deployment/linux-x64/Dockerfile
new file mode 100644
index 000000000..d634b55de
--- /dev/null
+++ b/deployment/linux-x64/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:10
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/linux-x64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-45ab-8c88-fa36165701b9/16ce29a06031eeb09058dee94d6f5330/dotnet-sdk-2.2.401-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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/linux-x64/build.sh b/deployment/linux-x64/build.sh
deleted file mode 100755
index 1f0fb62d3..000000000
--- a/deployment/linux-x64/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-build_jellyfin ../../Jellyfin.Server Release linux-x64 `pwd`/dist/jellyfin_${VERSION}
diff --git a/deployment/linux-x64/clean.sh b/deployment/linux-x64/clean.sh
index 3df2d7796..c07501a7b 100755
--- a/deployment/linux-x64/clean.sh
+++ b/deployment/linux-x64/clean.sh
@@ -1,7 +1,27 @@
#!/usr/bin/env bash
-source ../common.build.sh
+keep_artifacts="${1}"
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-linux-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/linux-x64/dependencies.txt b/deployment/linux-x64/dependencies.txt
index 3d25d1bdf..bdb967096 100644
--- a/deployment/linux-x64/dependencies.txt
+++ b/deployment/linux-x64/dependencies.txt
@@ -1 +1 @@
-dotnet
+docker
diff --git a/deployment/linux-x64/docker-build.sh b/deployment/linux-x64/docker-build.sh
new file mode 100755
index 000000000..8860f943c
--- /dev/null
+++ b/deployment/linux-x64/docker-build.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Builds the TAR archive inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Get version
+version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
+
+# Build archives
+dotnet publish --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
+rm -rf /dist/jellyfin_${version}
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/
+mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/
+chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/linux-x64/package.sh b/deployment/linux-x64/package.sh
index 13b943ea8..dfe8a9aa4 100755
--- a/deployment/linux-x64/package.sh
+++ b/deployment/linux-x64/package.sh
@@ -1,7 +1,34 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-linux-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Prepare temporary package dir
+mkdir -p "${package_temporary_dir}"
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/* "${output_dir}"
diff --git a/deployment/macos/Dockerfile b/deployment/macos/Dockerfile
new file mode 100644
index 000000000..406a2d853
--- /dev/null
+++ b/deployment/macos/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:10
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/macos
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-45ab-8c88-fa36165701b9/16ce29a06031eeb09058dee94d6f5330/dotnet-sdk-2.2.401-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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/macos/build.sh b/deployment/macos/build.sh
deleted file mode 100755
index d6bfb9f5e..000000000
--- a/deployment/macos/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-build_jellyfin ../../Jellyfin.Server Release osx-x64 `pwd`/dist/jellyfin_${VERSION}
diff --git a/deployment/macos/clean.sh b/deployment/macos/clean.sh
index 3df2d7796..c07501a7b 100755
--- a/deployment/macos/clean.sh
+++ b/deployment/macos/clean.sh
@@ -1,7 +1,27 @@
#!/usr/bin/env bash
-source ../common.build.sh
+keep_artifacts="${1}"
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-linux-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/macos/dependencies.txt b/deployment/macos/dependencies.txt
index 3d25d1bdf..bdb967096 100644
--- a/deployment/macos/dependencies.txt
+++ b/deployment/macos/dependencies.txt
@@ -1 +1 @@
-dotnet
+docker
diff --git a/deployment/macos/docker-build.sh b/deployment/macos/docker-build.sh
new file mode 100755
index 000000000..1b4a554e6
--- /dev/null
+++ b/deployment/macos/docker-build.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Builds the TAR archive inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Get version
+version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
+
+# Build archives
+dotnet publish --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
+tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
+rm -rf /dist/jellyfin_${version}
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/
+mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/
+chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/macos/package.sh b/deployment/macos/package.sh
index 13b943ea8..464c0d382 100755
--- a/deployment/macos/package.sh
+++ b/deployment/macos/package.sh
@@ -1,7 +1,34 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-macos-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Prepare temporary package dir
+mkdir -p "${package_temporary_dir}"
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/* "${output_dir}"
diff --git a/deployment/portable/Dockerfile b/deployment/portable/Dockerfile
new file mode 100644
index 000000000..bdbf978fe
--- /dev/null
+++ b/deployment/portable/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:10
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/portable
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-45ab-8c88-fa36165701b9/16ce29a06031eeb09058dee94d6f5330/dotnet-sdk-2.2.401-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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/portable/build.sh b/deployment/portable/build.sh
deleted file mode 100755
index 4f2e6363e..000000000
--- a/deployment/portable/build.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-#Magic word framework will create a non self contained build
-build_jellyfin ../../Jellyfin.Server Release framework `pwd`/dist/jellyfin_${VERSION}
diff --git a/deployment/portable/clean.sh b/deployment/portable/clean.sh
index 3df2d7796..c07501a7b 100755
--- a/deployment/portable/clean.sh
+++ b/deployment/portable/clean.sh
@@ -1,7 +1,27 @@
#!/usr/bin/env bash
-source ../common.build.sh
+keep_artifacts="${1}"
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-linux-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/docker/dependencies.txt b/deployment/portable/dependencies.txt
index bdb967096..bdb967096 100644
--- a/deployment/docker/dependencies.txt
+++ b/deployment/portable/dependencies.txt
diff --git a/deployment/portable/docker-build.sh b/deployment/portable/docker-build.sh
new file mode 100755
index 000000000..3645522d1
--- /dev/null
+++ b/deployment/portable/docker-build.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Builds the TAR archive inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Get version
+version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
+
+# Build archives
+dotnet publish --configuration Release --self-contained --runtime framework --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
+tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version}
+rm -rf /dist/jellyfin_${version}
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/
+mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/
+chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/portable/package.sh b/deployment/portable/package.sh
index 13b943ea8..0ceb54dda 100755
--- a/deployment/portable/package.sh
+++ b/deployment/portable/package.sh
@@ -1,7 +1,34 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-package_portable ../.. `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-portable-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Prepare temporary package dir
+mkdir -p "${package_temporary_dir}"
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/* "${output_dir}"
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/ubuntu-package-arm64/Dockerfile.amd64
index 5e51ef0f0..838e70d50 100644
--- a/deployment/ubuntu-package-arm64/Dockerfile.amd64
+++ b/deployment/ubuntu-package-arm64/Dockerfile.amd64
@@ -38,7 +38,13 @@ RUN rm /etc/apt/sources.list \
&& 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 -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev 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
+ && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev 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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/ubuntu-package-arm64/Dockerfile.arm64
index 646679328..789dcc15a 100644
--- a/deployment/ubuntu-package-arm64/Dockerfile.arm64
+++ b/deployment/ubuntu-package-arm64/Dockerfile.arm64
@@ -12,7 +12,7 @@ ENV ARCH=arm64
# Prepare Debian build environment
RUN apt-get update \
- && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
diff --git a/deployment/ubuntu-package-arm64/clean.sh b/deployment/ubuntu-package-arm64/clean.sh
index c92c7fdec..82d427f9e 100755
--- a/deployment/ubuntu-package-arm64/clean.sh
+++ b/deployment/ubuntu-package-arm64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/ubuntu-package-arm64/docker-build.sh b/deployment/ubuntu-package-arm64/docker-build.sh
index 1c75ece8e..7a13bafcb 100755
--- a/deployment/ubuntu-package-arm64/docker-build.sh
+++ b/deployment/ubuntu-package-arm64/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
dpkg-buildpackage -us -uc -aarm64
diff --git a/deployment/ubuntu-package-arm64/package.sh b/deployment/ubuntu-package-arm64/package.sh
index 5a2bf61c8..d1140a727 100755
--- a/deployment/ubuntu-package-arm64/package.sh
+++ b/deployment/ubuntu-package-arm64/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
ARCH="$( arch )"
WORKDIR="$( pwd )"
@@ -35,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/ubuntu-package-armhf/Dockerfile.amd64
index ef0735e42..d1123e0b6 100644
--- a/deployment/ubuntu-package-armhf/Dockerfile.amd64
+++ b/deployment/ubuntu-package-armhf/Dockerfile.amd64
@@ -38,7 +38,13 @@ RUN rm /etc/apt/sources.list \
&& 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 -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev 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
+ && apt-get install -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev 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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/ubuntu-package-armhf/Dockerfile.armhf b/deployment/ubuntu-package-armhf/Dockerfile.armhf
index 72c464724..c9e093e51 100644
--- a/deployment/ubuntu-package-armhf/Dockerfile.armhf
+++ b/deployment/ubuntu-package-armhf/Dockerfile.armhf
@@ -12,7 +12,7 @@ ENV ARCH=armhf
# Prepare Debian build environment
RUN apt-get update \
- && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
@@ -21,6 +21,12 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d9f37b73-df8d-4
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
# Link to docker-build script
RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
diff --git a/deployment/ubuntu-package-armhf/clean.sh b/deployment/ubuntu-package-armhf/clean.sh
index c92c7fdec..82d427f9e 100755
--- a/deployment/ubuntu-package-armhf/clean.sh
+++ b/deployment/ubuntu-package-armhf/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/ubuntu-package-armhf/docker-build.sh b/deployment/ubuntu-package-armhf/docker-build.sh
index df35345bd..c48ccb3fb 100755
--- a/deployment/ubuntu-package-armhf/docker-build.sh
+++ b/deployment/ubuntu-package-armhf/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
dpkg-buildpackage -us -uc -aarmhf
diff --git a/deployment/ubuntu-package-armhf/package.sh b/deployment/ubuntu-package-armhf/package.sh
index 15f55bff2..2ceb3e816 100755
--- a/deployment/ubuntu-package-armhf/package.sh
+++ b/deployment/ubuntu-package-armhf/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
ARCH="$( arch )"
WORKDIR="$( pwd )"
@@ -35,7 +39,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE}
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/ubuntu-package-x64/Dockerfile b/deployment/ubuntu-package-x64/Dockerfile
index 485b6c42c..1749d2ad0 100644
--- a/deployment/ubuntu-package-x64/Dockerfile
+++ b/deployment/ubuntu-package-x64/Dockerfile
@@ -10,10 +10,16 @@ ENV DEB_BUILD_OPTIONS=noddebs
# Prepare Ubuntu build environment
RUN apt-get update \
- && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev \
&& ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \
&& mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
VOLUME ${ARTIFACT_DIR}/
COPY . ${SOURCE_DIR}/
diff --git a/deployment/ubuntu-package-x64/clean.sh b/deployment/ubuntu-package-x64/clean.sh
index c92c7fdec..82d427f9e 100755
--- a/deployment/ubuntu-package-x64/clean.sh
+++ b/deployment/ubuntu-package-x64/clean.sh
@@ -1,7 +1,5 @@
#!/usr/bin/env bash
-source ../common.build.sh
-
keep_artifacts="${1}"
WORKDIR="$( pwd )"
diff --git a/deployment/ubuntu-package-x64/docker-build.sh b/deployment/ubuntu-package-x64/docker-build.sh
index 9781879f6..97bc45a06 100755
--- a/deployment/ubuntu-package-x64/docker-build.sh
+++ b/deployment/ubuntu-package-x64/docker-build.sh
@@ -11,6 +11,20 @@ pushd ${SOURCE_DIR}
# Remove build-dep for dotnet-sdk-2.2, since it's not a package in this image
sed -i '/dotnet-sdk-2.2,/d' debian/control
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
# Build DEB
dpkg-buildpackage -us -uc
diff --git a/deployment/ubuntu-package-x64/package.sh b/deployment/ubuntu-package-x64/package.sh
index 32e6d4fd6..08c003778 100755
--- a/deployment/ubuntu-package-x64/package.sh
+++ b/deployment/ubuntu-package-x64/package.sh
@@ -1,6 +1,10 @@
#!/usr/bin/env bash
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
WORKDIR="$( pwd )"
@@ -24,7 +28,7 @@ mkdir -p "${package_temporary_dir}"
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
# Build the DEBs and copy out to ${package_temporary_dir}
-${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}"
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
# Move the DEBs to the output directory
mkdir -p "${output_dir}"
mv "${package_temporary_dir}"/deb/* "${output_dir}"
diff --git a/deployment/win-x64/Dockerfile b/deployment/win-x64/Dockerfile
new file mode 100644
index 000000000..7f64c7dae
--- /dev/null
+++ b/deployment/win-x64/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:10
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/win-x64
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-45ab-8c88-fa36165701b9/16ce29a06031eeb09058dee94d6f5330/dotnet-sdk-2.2.401-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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/win-x64/build.sh b/deployment/win-x64/build.sh
deleted file mode 100755
index 0b3046203..000000000
--- a/deployment/win-x64/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-build_jellyfin ../../Jellyfin.Server Release win-x64 `pwd`/dist/jellyfin_${VERSION}
diff --git a/deployment/win-x64/clean.sh b/deployment/win-x64/clean.sh
index 3df2d7796..6c183f337 100755
--- a/deployment/win-x64/clean.sh
+++ b/deployment/win-x64/clean.sh
@@ -1,7 +1,27 @@
#!/usr/bin/env bash
-source ../common.build.sh
+keep_artifacts="${1}"
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-windows-x64-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/win-x64/dependencies.txt b/deployment/win-x64/dependencies.txt
index 3d25d1bdf..bdb967096 100644
--- a/deployment/win-x64/dependencies.txt
+++ b/deployment/win-x64/dependencies.txt
@@ -1 +1 @@
-dotnet
+docker
diff --git a/deployment/win-x64/docker-build.sh b/deployment/win-x64/docker-build.sh
new file mode 100755
index 000000000..36d6f77fc
--- /dev/null
+++ b/deployment/win-x64/docker-build.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Builds the ZIP archive inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Version variables
+NSSM_VERSION="nssm-2.24-101-g897c7ad"
+NSSM_URL="https://nssm.cc/ci/${NSSM_VERSION}.zip"
+FFMPEG_VERSION="ffmpeg-4.0.2-win64-static"
+FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip"
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Get version
+version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
+
+# Build binary
+dotnet publish --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;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}/ffmpeg.zip
+unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir}
+cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe
+unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir}
+cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe
+cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe
+rm -rf ${addin_build_dir}
+
+# Prepare scripts
+cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1
+cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat
+
+# Create zip package
+pushd /dist
+zip /jellyfin_${version}.portable.zip jellyfin_${version}
+popd
+rm -rf /dist/jellyfin_${version}
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/
+mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/
+chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/win-x64/package.sh b/deployment/win-x64/package.sh
index b438c28e4..a8ab190fa 100755
--- a/deployment/win-x64/package.sh
+++ b/deployment/win-x64/package.sh
@@ -1,47 +1,34 @@
#!/usr/bin/env bash
-set -x
-package_win64() (
- local NSSM_VERSION="nssm-2.24-101-g897c7ad"
- local NSSM_URL="https://nssm.cc/ci/${NSSM_VERSION}.zip"
- local FFMPEG_VERSION="ffmpeg-4.0.2-win64-static"
- local FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip"
- local ROOT=${1-$DEFAULT_ROOT}
- local OUTPUT_DIR=${2-$DEFAULT_OUTPUT_DIR}
- local PKG_DIR=${3-$DEFAULT_PKG_DIR}
- local ARCHIVE_CMD="zip -r"
- # Package portable build result
- if [ -d ${OUTPUT_DIR} ]; then
- echo -e "${CYAN}Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}'.${NC}"
- local TEMP_DIR="$(mktemp -d)"
- wget ${NSSM_URL} -O ${TEMP_DIR}/nssm.zip
- wget ${FFMPEG_URL} -O ${TEMP_DIR}/ffmpeg.zip
- unzip ${TEMP_DIR}/nssm.zip -d $TEMP_DIR
- cp ${TEMP_DIR}/${NSSM_VERSION}/win64/nssm.exe ${OUTPUT_DIR}/nssm.exe
- unzip ${TEMP_DIR}/ffmpeg.zip -d $TEMP_DIR
- cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${OUTPUT_DIR}/ffmpeg.exe
- cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffprobe.exe ${OUTPUT_DIR}/ffprobe.exe
- rm -r ${TEMP_DIR}
- cp ${ROOT}/deployment/windows/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
- cp ${ROOT}/deployment/windows/install.bat ${OUTPUT_DIR}/install.bat
- mkdir -p ${PKG_DIR}
- pushd ${OUTPUT_DIR}
- ${ARCHIVE_CMD} ${ROOT}/${PKG_DIR}/`basename "${OUTPUT_DIR}"`.zip .
- popd
- local EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' FAILED.${NC}"
- fi
- else
- echo -e "${RED}[FAIL] Build artifacts do not exist for ${OUTPUT_DIR}. Run build.sh first.${NC}"
- fi
-)
-source ../common.build.sh
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-package_win64 ../.. `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-windows-x64-build"
-#TODO setup and maybe change above code to produce the Windows native zip format.
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Prepare temporary package dir
+mkdir -p "${package_temporary_dir}"
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/* "${output_dir}"
diff --git a/deployment/win-x86/Dockerfile b/deployment/win-x86/Dockerfile
new file mode 100644
index 000000000..fb5f5d6b6
--- /dev/null
+++ b/deployment/win-x86/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:10
+# Docker build arguments
+ARG SOURCE_DIR=/jellyfin
+ARG PLATFORM_DIR=/jellyfin/deployment/win-x86
+ARG ARTIFACT_DIR=/dist
+ARG SDK_VERSION=2.2
+# Docker run environment
+ENV SOURCE_DIR=/jellyfin
+ENV ARTIFACT_DIR=/dist
+ENV DEB_BUILD_OPTIONS=noddebs
+ENV ARCH=amd64
+
+# Prepare Debian build environment
+RUN apt-get update \
+ && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip
+
+# Install dotnet repository
+# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
+RUN wget https://download.visualstudio.microsoft.com/download/pr/228832ea-805f-45ab-8c88-fa36165701b9/16ce29a06031eeb09058dee94d6f5330/dotnet-sdk-2.2.401-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
+
+# Install yarn package manager
+RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
+ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
+ && apt update \
+ && apt install -y yarn
+
+# Link to docker-build script
+RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh
+
+VOLUME ${ARTIFACT_DIR}/
+
+COPY . ${SOURCE_DIR}/
+
+ENTRYPOINT ["/docker-build.sh"]
diff --git a/deployment/win-x86/build.sh b/deployment/win-x86/build.sh
deleted file mode 100755
index 610db356a..000000000
--- a/deployment/win-x86/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-source ../common.build.sh
-
-VERSION=`get_version ../..`
-
-build_jellyfin ../../Jellyfin.Server Release win-x86 `pwd`/dist/jellyfin_${VERSION}
diff --git a/deployment/win-x86/clean.sh b/deployment/win-x86/clean.sh
index 3df2d7796..8b78c5e4b 100755
--- a/deployment/win-x86/clean.sh
+++ b/deployment/win-x86/clean.sh
@@ -1,7 +1,27 @@
#!/usr/bin/env bash
-source ../common.build.sh
+keep_artifacts="${1}"
-VERSION=`get_version ../..`
+WORKDIR="$( pwd )"
-clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-windows-x86-build"
+
+rm -rf "${package_temporary_dir}" &>/dev/null \
+ || sudo rm -rf "${package_temporary_dir}" &>/dev/null
+
+rm -rf "${output_dir}" &>/dev/null \
+ || sudo rm -rf "${output_dir}" &>/dev/null
+
+if [[ ${keep_artifacts} == 'n' ]]; then
+ docker_sudo=""
+ if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo=sudo
+ fi
+ ${docker_sudo} docker image rm ${image_name} --force
+fi
diff --git a/deployment/win-x86/dependencies.txt b/deployment/win-x86/dependencies.txt
index 3d25d1bdf..bdb967096 100644
--- a/deployment/win-x86/dependencies.txt
+++ b/deployment/win-x86/dependencies.txt
@@ -1 +1 @@
-dotnet
+docker
diff --git a/deployment/win-x86/docker-build.sh b/deployment/win-x86/docker-build.sh
new file mode 100755
index 000000000..86ede7452
--- /dev/null
+++ b/deployment/win-x86/docker-build.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Builds the ZIP archive inside the Docker container
+
+set -o errexit
+set -o xtrace
+
+# Version variables
+NSSM_VERSION="nssm-2.24-101-g897c7ad"
+NSSM_URL="https://nssm.cc/ci/${NSSM_VERSION}.zip"
+FFMPEG_VERSION="ffmpeg-4.0.2-win32-static"
+FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip"
+
+# Move to source directory
+pushd ${SOURCE_DIR}
+
+# Clone down and build Web frontend
+web_build_dir="$( mktemp -d )"
+web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web"
+git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/
+pushd ${web_build_dir}
+if [[ -n ${web_branch} ]]; then
+ checkout -b origin/${web_branch}
+fi
+yarn install
+mkdir -p ${web_target}
+mv dist/* ${web_target}/
+popd
+rm -rf ${web_build_dir}
+
+# Get version
+version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
+
+# Build binary
+dotnet publish --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;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}/ffmpeg.zip
+unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir}
+cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe
+unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir}
+cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe
+cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe
+rm -rf ${addin_build_dir}
+
+# Prepare scripts
+cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1
+cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat
+
+# Create zip package
+pushd /dist
+zip /jellyfin_${version}.portable.zip jellyfin_${version}
+popd
+rm -rf /dist/jellyfin_${version}
+
+# Move the artifacts out
+mkdir -p ${ARTIFACT_DIR}/
+mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/
+chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
diff --git a/deployment/win-x86/package.sh b/deployment/win-x86/package.sh
index 8752d92a8..65e7e2928 100755
--- a/deployment/win-x86/package.sh
+++ b/deployment/win-x86/package.sh
@@ -1,45 +1,34 @@
#!/usr/bin/env bash
-package_win32() (
- local NSSM_VERSION="nssm-2.24-101-g897c7ad"
- local NSSM_URL="https://nssm.cc/ci/${NSSM_VERSION}.zip"
- local FFMPEG_VERSION="ffmpeg-4.0.2-win32-static"
- local FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip"
- local ROOT=${1-$DEFAULT_ROOT}
- local OUTPUT_DIR=${2-$DEFAULT_OUTPUT_DIR}
- local PKG_DIR=${3-$DEFAULT_PKG_DIR}
- local ARCHIVE_CMD="zip -r"
- # Package portable build result
- if [ -d ${OUTPUT_DIR} ]; then
- echo -e "${CYAN}Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}'.${NC}"
- local TEMP_DIR="$(mktemp -d)"
- wget ${NSSM_URL} -O ${TEMP_DIR}/nssm.zip
- wget ${FFMPEG_URL} -O ${TEMP_DIR}/ffmpeg.zip
- unzip ${TEMP_DIR}/nssm.zip -d $TEMP_DIR
- cp ${TEMP_DIR}/${NSSM_VERSION}/win32/nssm.exe ${OUTPUT_DIR}/nssm.exe
- unzip ${TEMP_DIR}/ffmpeg.zip -d $TEMP_DIR
- cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${OUTPUT_DIR}/ffmpeg.exe
- cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffprobe.exe ${OUTPUT_DIR}/ffprobe.exe
- rm -r ${TEMP_DIR}
- cp ${ROOT}/deployment/windows/install-jellyfin.ps1 ${OUTPUT_DIR}/install-jellyfin.ps1
- cp ${ROOT}/deployment/windows/install.bat ${OUTPUT_DIR}/install.bat
- mkdir -p ${PKG_DIR}
- pushd ${OUTPUT_DIR}
- ${ARCHIVE_CMD} ${ROOT}/${PKG_DIR}/`basename "${OUTPUT_DIR}"`.zip .
- popd
- local EXIT_CODE=$?
- if [ $EXIT_CODE -eq 0 ]; then
- echo -e "${GREEN}[DONE] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' complete.${NC}"
- else
- echo -e "${RED}[FAIL] Packaging build in '${OUTPUT_DIR}' for `basename "${OUTPUT_DIR}"` to '${PKG_DIR}' with root '${ROOT}' FAILED.${NC}"
- fi
- else
- echo -e "${RED}[FAIL] Build artifacts do not exist for ${OUTPUT_DIR}. Run build.sh first.${NC}"
- fi
-)
-source ../common.build.sh
-VERSION=`get_version ../..`
+args="${@}"
+declare -a docker_envvars
+for arg in ${args}; do
+ docker_envvars+=("-e ${arg}")
+done
-package_win32 ../.. `pwd`/dist/jellyfin_${VERSION}
+WORKDIR="$( pwd )"
-#TODO setup and maybe change above code to produce the Windows native zip format.
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+output_dir="${WORKDIR}/pkg-dist"
+current_user="$( whoami )"
+image_name="jellyfin-windows-x86-build"
+
+# Determine if sudo should be used for Docker
+if [[ ! -z $(id -Gn | grep -q 'docker') ]] \
+ && [[ ! ${EUID:-1000} -eq 0 ]] \
+ && [[ ! ${USER} == "root" ]] \
+ && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then
+ docker_sudo="sudo"
+else
+ docker_sudo=""
+fi
+
+# Prepare temporary package dir
+mkdir -p "${package_temporary_dir}"
+# Set up the build environment Docker image
+${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
+# Build the DEBs and copy out to ${package_temporary_dir}
+${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars}
+# Move the DEBs to the output directory
+mkdir -p "${output_dir}"
+mv "${package_temporary_dir}"/* "${output_dir}"
diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi
index 6a647330d..e33efde91 100644
--- a/deployment/windows/jellyfin.nsi
+++ b/deployment/windows/jellyfin.nsi
@@ -185,7 +185,7 @@ Section "Jellyfin Server Service" InstallService
DetailPrint "Jellyfin Server service statuscode, $0"
${If} $0 == 0
InstallRetry:
- ExecWait '"$INSTDIR\nssm.exe" install JellyfinServer "$INSTDIR\jellyfin.exe" --datadir "$_JELLYFINDATADIR_"' $0
+ ExecWait '"$INSTDIR\nssm.exe" install JellyfinServer "$INSTDIR\jellyfin.exe" --datadir \"$_JELLYFINDATADIR_\"' $0
${If} $0 <> 0
!insertmacro ShowError "Could not install the Jellyfin Server service." InstallRetry
${EndIf}
@@ -201,7 +201,7 @@ Section "Jellyfin Server Service" InstallService
DetailPrint "Jellyfin Server Service setting (Application), $0"
ConfigureAppParametersRetry:
- ExecWait '"$INSTDIR\nssm.exe" set JellyfinServer AppParameters --datadir "$_JELLYFINDATADIR_"' $0
+ ExecWait '"$INSTDIR\nssm.exe" set JellyfinServer AppParameters --datadir \"$_JELLYFINDATADIR_\"' $0
${If} $0 <> 0
!insertmacro ShowError "Could not configure the Jellyfin Server service." ConfigureAppParametersRetry
${EndIf}