From e8d72bf6a3c684efe8a4e320d594988e857c68fc Mon Sep 17 00:00:00 2001 From: theguymadmax Date: Mon, 16 Mar 2026 10:32:09 -0400 Subject: [PATCH 1/2] Fix restore backup metadata location --- .../FullSystemBackup/BackupService.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs index 70483c36cc..3a5df4d68d 100644 --- a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs +++ b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs @@ -118,15 +118,21 @@ public class BackupService : IBackupService throw new NotSupportedException($"The loaded archive '{archivePath}' is made for a newer version of Jellyfin ({manifest.ServerVersion}) and cannot be loaded in this version."); } - void CopyDirectory(string source, string target) + void CopyDirectory(string source, string target, string? exclude = null) { var fullSourcePath = NormalizePathSeparator(Path.GetFullPath(source) + Path.DirectorySeparatorChar); var fullTargetRoot = Path.GetFullPath(target) + Path.DirectorySeparatorChar; + var excludePath = exclude is null ? null : $"{source}/{exclude}/"; foreach (var item in zipArchive.Entries) { var sourcePath = NormalizePathSeparator(Path.GetFullPath(item.FullName)); var targetPath = Path.GetFullPath(Path.Combine(target, Path.GetRelativePath(source, item.FullName))); + if (excludePath is not null && item.FullName.StartsWith(excludePath, StringComparison.Ordinal)) + { + continue; + } + if (!sourcePath.StartsWith(fullSourcePath, StringComparison.Ordinal) || !targetPath.StartsWith(fullTargetRoot, StringComparison.Ordinal) || Path.EndsInDirectorySeparator(item.FullName)) @@ -142,8 +148,9 @@ public class BackupService : IBackupService } CopyDirectory("Config", _applicationPaths.ConfigurationDirectoryPath); - CopyDirectory("Data", _applicationPaths.DataPath); + CopyDirectory("Data", _applicationPaths.DataPath, exclude: "metadata"); CopyDirectory("Root", _applicationPaths.RootFolderPath); + CopyDirectory("Data/metadata", _applicationPaths.InternalMetadataPath); if (manifest.Options.Database) { From 61b19688ff76aa55fe5c39d644c263d72b80199c Mon Sep 17 00:00:00 2001 From: theguymadmax Date: Tue, 17 Mar 2026 23:13:20 -0400 Subject: [PATCH 2/2] Backup default metadata location --- .../FullSystemBackup/BackupService.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs index 3a5df4d68d..5eaf319ab3 100644 --- a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs +++ b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs @@ -118,17 +118,17 @@ public class BackupService : IBackupService throw new NotSupportedException($"The loaded archive '{archivePath}' is made for a newer version of Jellyfin ({manifest.ServerVersion}) and cannot be loaded in this version."); } - void CopyDirectory(string source, string target, string? exclude = null) + void CopyDirectory(string source, string target, string[]? exclude = null) { var fullSourcePath = NormalizePathSeparator(Path.GetFullPath(source) + Path.DirectorySeparatorChar); var fullTargetRoot = Path.GetFullPath(target) + Path.DirectorySeparatorChar; - var excludePath = exclude is null ? null : $"{source}/{exclude}/"; + var excludePaths = exclude?.Select(e => $"{source}/{e}/").ToArray(); foreach (var item in zipArchive.Entries) { var sourcePath = NormalizePathSeparator(Path.GetFullPath(item.FullName)); var targetPath = Path.GetFullPath(Path.Combine(target, Path.GetRelativePath(source, item.FullName))); - if (excludePath is not null && item.FullName.StartsWith(excludePath, StringComparison.Ordinal)) + if (excludePaths is not null && excludePaths.Any(e => item.FullName.StartsWith(e, StringComparison.Ordinal))) { continue; } @@ -148,9 +148,10 @@ public class BackupService : IBackupService } CopyDirectory("Config", _applicationPaths.ConfigurationDirectoryPath); - CopyDirectory("Data", _applicationPaths.DataPath, exclude: "metadata"); + CopyDirectory("Data", _applicationPaths.DataPath, exclude: ["metadata", "metadata-default"]); CopyDirectory("Root", _applicationPaths.RootFolderPath); CopyDirectory("Data/metadata", _applicationPaths.InternalMetadataPath); + CopyDirectory("Data/metadata-default", _applicationPaths.DefaultInternalMetadataPath); if (manifest.Options.Database) { @@ -410,6 +411,15 @@ public class BackupService : IBackupService if (backupOptions.Metadata) { CopyDirectory(Path.Combine(_applicationPaths.InternalMetadataPath), Path.Combine("Data", "metadata")); + + // If a custom metadata path is configured, the default location may still contain data. + if (!string.Equals( + Path.GetFullPath(_applicationPaths.DefaultInternalMetadataPath), + Path.GetFullPath(_applicationPaths.InternalMetadataPath), + StringComparison.OrdinalIgnoreCase)) + { + CopyDirectory(Path.Combine(_applicationPaths.DefaultInternalMetadataPath), Path.Combine("Data", "metadata-default")); + } } var manifestStream = zipArchive.CreateEntry(ManifestEntryName).Open();