Skip to content

Commit

Permalink
fix: fix part episodes (take 2)
Browse files Browse the repository at this point in the history
- Fixed it so the paths match _exactly_ for the episode parts except for the part number, since Jellyfin's internal logic didn't like that the file ids were different between the parts. The fix was to add all file ids to the file name and select the correct id for the given part.
  • Loading branch information
revam committed Oct 15, 2024
1 parent b1280fb commit 83c4745
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 6 deletions.
10 changes: 10 additions & 0 deletions Shokofin/API/Models/CrossReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ public class EpisodeCrossReferenceIDs

public int? ReleaseGroup { get; set; }

/// <summary>
/// ED2K hash.
/// </summary>
public string ED2K { get; set; } = string.Empty;

/// <summary>
/// File size.
/// </summary>
public long FileSize { get; set; }

/// <summary>
/// Percentage file is matched to the episode.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions Shokofin/API/ShokoAPIClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ public Task<File> GetFile(string id)
return Get<File>($"/api/v3/File/{id}?include=XRefs&includeDataFrom=AniDB");
}

public Task<File> GetFileByEd2kAndFileSize(string ed2k, long fileSize)
{
return Get<File>($"/api/v3/File/Hash/ED2K?hash={Uri.EscapeDataString(ed2k)}&size={fileSize}&includeDataFrom=AniDB");
}

public Task<List<File>> GetFileByPath(string path)
{
return Get<List<File>>($"/api/v3/File/PathEndsWith?path={Uri.EscapeDataString(path)}&includeDataFrom=AniDB&limit=1");
Expand Down
13 changes: 9 additions & 4 deletions Shokofin/Resolvers/VirtualFileSystemService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ await Task.WhenAll(allFiles.Select(async (tuple) => {
ExtraType.Sample => ["samples"],
_ => ["extras"],
};
var fileIdList = fileId;
var filePartSuffix = "";
if (collectionType is CollectionType.movies || (collectionType is null && isMovieSeason)) {
if (extrasFolders != null) {
Expand Down Expand Up @@ -822,9 +823,13 @@ await Task.WhenAll(allFiles.Select(async (tuple) => {
else {
folders.Add(Path.Join(vfsPath, showFolder, seasonFolder));
episodeName = $"{showName} S{(isSpecial ? 0 : seasonNumber).ToString().PadLeft(2, '0')}E{episodeNumber.ToString().PadLeft(show.EpisodePadding, '0')}";
filePartSuffix = (episodeXref.Percentage?.Group ?? 1) is not 1
? $".pt{episode.Shoko.CrossReferences.Where(xref => xref.ReleaseGroup == episodeXref.ReleaseGroup && xref.Percentage!.Group == episodeXref.Percentage!.Group).ToList().FindIndex(xref => xref.Percentage!.Start == episodeXref.Percentage!.Start && xref.Percentage!.End == episodeXref.Percentage!.End) + 1}"
: "";
if ((episodeXref.Percentage?.Group ?? 1) is not 1) {
var list = episode.Shoko.CrossReferences.Where(xref => xref.ReleaseGroup == episodeXref.ReleaseGroup && xref.Percentage!.Group == episodeXref.Percentage!.Group).ToList();
var files = await Task.WhenAll(list.Select(xref => ApiClient.GetFileByEd2kAndFileSize(xref.ED2K, xref.FileSize)));
var index = list.FindIndex(xref => xref.Percentage!.Start == episodeXref.Percentage!.Start && xref.Percentage!.End == episodeXref.Percentage!.End);
filePartSuffix = $".pt{index + 1}";
fileIdList = files.Select(f => f.Id.ToString()).Join(",");
}
}
}

Expand All @@ -841,7 +846,7 @@ file.Shoko.AniDBData is not null
);
if (config.VFS_AddResolution && !string.IsNullOrEmpty(file.Shoko.Resolution))
extraDetails.Add(file.Shoko.Resolution);
var fileName = $"{episodeName} {(extraDetails.Count is > 0 ? $"[{extraDetails.Select(a => a.ReplaceInvalidPathCharacters()).Join("] [")}] " : "")}[{ShokoSeriesId.Name}={seriesId}] [{ShokoFileId.Name}={fileId}]{filePartSuffix}{Path.GetExtension(sourceLocation)}";
var fileName = $"{episodeName} {(extraDetails.Count is > 0 ? $"[{extraDetails.Select(a => a.ReplaceInvalidPathCharacters()).Join("] [")}] " : "")}[{ShokoSeriesId.Name}={seriesId}] [{ShokoFileId.Name}={fileIdList}]{filePartSuffix}{Path.GetExtension(sourceLocation)}";
var symbolicLinks = folders
.Select(folderPath => Path.Join(folderPath, fileName))
.ToArray();
Expand Down
20 changes: 18 additions & 2 deletions Shokofin/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
using System.Linq;
using System.Text.RegularExpressions;
using MediaBrowser.Common.Providers;
using Shokofin.ExternalIds;

namespace Shokofin;

public static class StringExtensions
public static partial class StringExtensions
{
public static string Replace(this string input, Regex regex, string replacement, int count, int startAt)
=> regex.Replace(input, replacement, count, startAt);
Expand Down Expand Up @@ -152,6 +153,21 @@ public static string ReplaceInvalidPathCharacters(this string path)
return null;
}

[GeneratedRegex(@"\.pt(?<partNumber>\d+)(?:\.[a-z0-9]+)?$", RegexOptions.IgnoreCase)]
private static partial Regex GetPartRegex();

public static bool TryGetAttributeValue(this string text, string attribute, [NotNullWhen(true)] out string? value)
=> !string.IsNullOrEmpty(value = GetAttributeValue(text, attribute));
{
value = GetAttributeValue(text, attribute);

// Select the correct id for the part number in the stringified list of file ids.
if (!string.IsNullOrEmpty(value) && attribute == ShokoFileId.Name && GetPartRegex().Match(text) is { Success: true } regexResult) {
var partNumber = int.Parse(regexResult.Groups["partNumber"].Value);
var index = partNumber - 1;
value = value.Split(',')[index];
}

return !string.IsNullOrEmpty(value);
}

}

0 comments on commit 83c4745

Please sign in to comment.