Release 0.3.4: Jellyfin 12 stable support and audit fixes
Harden proxy permissions and cache behavior, streamline metadata fetching, remove obsolete rules, and validate the stable SDK with expanded regression coverage. Document backup format changes.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using Jellyfin.Plugin.Multilang.Data;
|
||||
|
||||
@@ -18,8 +17,6 @@ public sealed class AssetStorageService
|
||||
|
||||
public async Task<string?> StoreAsync(
|
||||
string itemId,
|
||||
string lang,
|
||||
string kind,
|
||||
string sourceUrl,
|
||||
bool localStorage,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -30,22 +27,17 @@ public sealed class AssetStorageService
|
||||
if (!localStorage)
|
||||
return sourceUrl;
|
||||
|
||||
var relative = BuildRelativePath(itemId, lang, kind, sourceUrl);
|
||||
var relative = BuildRelativePath(itemId, sourceUrl);
|
||||
var expectedUrl = TranslationStore.LocalAssetUrlPrefix + relative.Replace('\\', '/');
|
||||
if (_store.GetAssetPath(itemId, lang, kind) is { } existing &&
|
||||
existing.Equals(expectedUrl, StringComparison.OrdinalIgnoreCase) &&
|
||||
_store.TryNormalizeLocalAssetPath(existing, out var existingPath) &&
|
||||
File.Exists(existingPath))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var destination = Path.GetFullPath(Path.Combine(_store.AssetsDirectory, relative));
|
||||
var root = Path.GetFullPath(_store.AssetsDirectory) + Path.DirectorySeparatorChar;
|
||||
if (!destination.StartsWith(root, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("Resolved asset path escaped the Multilang assets directory.");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destination) ?? _store.AssetsDirectory);
|
||||
if (File.Exists(destination))
|
||||
return expectedUrl;
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destination)!);
|
||||
await DownloadAsync(sourceUrl, destination, cancellationToken).ConfigureAwait(false);
|
||||
return expectedUrl;
|
||||
}
|
||||
@@ -101,11 +93,11 @@ public sealed class AssetStorageService
|
||||
File.Move(temp, destination, overwrite: true);
|
||||
}
|
||||
|
||||
private static string BuildRelativePath(string itemId, string lang, string kind, string sourceUrl)
|
||||
private static string BuildRelativePath(string itemId, string sourceUrl)
|
||||
{
|
||||
var extension = ExtensionFromUrl(sourceUrl);
|
||||
var hash = Convert.ToHexString(SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(sourceUrl))).ToLowerInvariant()[..16];
|
||||
return Path.Combine(SafeSegment(itemId), SafeSegment(lang), SafeSegment(kind) + "-" + hash + extension);
|
||||
return Path.Combine(SafeSegment(itemId), hash + extension);
|
||||
}
|
||||
|
||||
private static string SafeSegment(string value)
|
||||
@@ -133,6 +125,6 @@ public sealed class AssetStorageService
|
||||
".png" => "image/png",
|
||||
".webp" => "image/webp",
|
||||
".gif" => "image/gif",
|
||||
_ => MediaTypeHeaderValue.Parse("application/octet-stream").MediaType ?? "application/octet-stream"
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user