Merge pull request #17931 from Shadowghost/fix-version-decimal

Fix decimal point handling in version names
This commit is contained in:
Cody Robibero authored and GitHub committed 2026-09-10 19:33:20 -04:00
commit ad1bf20e84
2 files changed
+61 -3

No files matched your search

+32 -3
View File
@@ -1411,7 +1411,8 @@ namespace MediaBrowser.Controller.Entities
/// token shared by the descriptors but separated only by spaces (e.g. a common "2160p ") is
/// kept in the label, falling back to a space only when no structural delimiter is shared. The
/// separators mirror the version delimiters recognised by the naming layer (Emby.Naming
/// VideoFlagDelimiters).
/// VideoFlagDelimiters), except that a dot between digits is a decimal point rather than a
/// delimiter, so numeric version labels stay whole.
/// </summary>
/// <param name="fileNames">The version file names without extension; must contain at least one entry.</param>
/// <returns>The shared prefix retreated to a separator boundary, or an empty string when none is shared.</returns>
@@ -1445,9 +1446,12 @@ namespace MediaBrowser.Controller.Entities
if (!prefixIsWholeName)
{
// Retreat to the last structural delimiter ('-', '_', '.').
// Retreat to the last structural delimiter ('-', '_', '.'), skipping dots that are
// decimal points within a number rather than delimiters (see IsDecimalPoint).
var cut = prefix.Length;
while (cut > 0 && Array.IndexOf(VersionDelimiters, prefix[cut - 1]) < 0)
while (cut > 0
&& (Array.IndexOf(VersionDelimiters, prefix[cut - 1]) < 0
|| IsDecimalPoint(prefix, cut - 1, fileNames)))
{
cut--;
}
@@ -1467,6 +1471,31 @@ namespace MediaBrowser.Controller.Entities
return prefix;
}
private static bool IsDecimalPoint(string prefix, int index, IReadOnlyList<string> fileNames)
{
if (index == 0 || prefix[index] != '.' || !char.IsDigit(prefix[index - 1]))
{
return false;
}
if (index + 1 < prefix.Length)
{
return char.IsDigit(prefix[index + 1]);
}
// The dot ends the prefix, so the character after it is the first one that differs between
// the versions: only a decimal point when every version continues the number.
for (var i = 0; i < fileNames.Count; i++)
{
if (fileNames[i].Length <= index + 1 || !char.IsDigit(fileNames[i][index + 1]))
{
return false;
}
}
return true;
}
public Task RefreshMetadata(CancellationToken cancellationToken)
{
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
@@ -185,6 +185,17 @@ public class BaseItemTests
"Blade Runner (1982) [EE by ADM] [480p HEVC AAC]",
"[Final Cut] [1080p HEVC AAC]",
"[EE by ADM] [480p HEVC AAC]")]
// Numeric version labels: the dot between the digits is a decimal point, not a delimiter, so the
// prefix retreats past it to the '-' instead of leaving "0" / "11".
[InlineData(
"Evangelion 1.0 You Are (Not) Alone (2007) - 1.0",
"Evangelion 1.0 You Are (Not) Alone (2007) - 1.11",
"1.0",
"1.11")]
// Numeric labels with no structural delimiter at all fall back to the space boundary.
[InlineData("Movie (2007) 1.0", "Movie (2007) 1.11", "1.0", "1.11")]
// A dot followed by a non-digit is still a delimiter, even after a digit.
[InlineData("Movie - Part 1.HDR", "Movie - Part 1.SDR", "HDR", "SDR")]
public void GetMediaSourceName_CommonPrefix_Valid(string primaryName, string altName, string expectedPrimary, string expectedAlt)
{
var primaryPath = "/Shows/Demo/Season 01/" + primaryName + ".mkv";
@@ -215,6 +226,24 @@ public class BaseItemTests
Assert.Equal(expectedAlt, videoAlt.GetMediaSourceName(videoAlt, commonPrefix));
}
[Fact]
public void GetCommonVersionPrefix_NumericLabels_KeepsWholeNumber()
{
// Three versions labelled "1.0", "1.01" and "1.11": the common prefix stops inside the version
// number, so it must retreat past the decimal point to the '-' delimiter.
string[] fileNames =
[
"Evangelion 1.0 You Are (Not) Alone (2007) - 1.0",
"Evangelion 1.0 You Are (Not) Alone (2007) - 1.01",
"Evangelion 1.0 You Are (Not) Alone (2007) - 1.11"
];
var prefix = BaseItem.GetCommonVersionPrefix(fileNames);
Assert.Equal("Evangelion 1.0 You Are (Not) Alone (2007) -", prefix);
Assert.Equal(["1.0", "1.01", "1.11"], fileNames.Select(n => n[prefix.Length..].TrimStart(' ')));
}
[Fact]
public void GetAlternateVersion_ReturnsMatchingLocalVersion()
{