jellyfin/Emby.Naming/AudioBook/AudioBookListResolver.cs

151 lines
6.2 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
2018-09-12 13:26:21 -04:00
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Model.IO;
namespace Emby.Naming.AudioBook
{
2020-11-10 11:11:48 -05:00
/// <summary>
/// Class used to resolve Name, Year, alternative files and extras from stack of files.
/// </summary>
2018-09-12 13:26:21 -04:00
public class AudioBookListResolver
{
private readonly NamingOptions _options;
2021-12-07 09:18:17 -05:00
private readonly AudioBookResolver _audioBookResolver;
2018-09-12 13:26:21 -04:00
2020-11-10 11:11:48 -05:00
/// <summary>
/// Initializes a new instance of the <see cref="AudioBookListResolver"/> class.
/// </summary>
/// <param name="options">Naming options passed along to <see cref="AudioBookResolver"/> and <see cref="AudioBookNameParser"/>.</param>
2018-09-12 13:26:21 -04:00
public AudioBookListResolver(NamingOptions options)
{
_options = options;
2021-12-07 09:18:17 -05:00
_audioBookResolver = new AudioBookResolver(_options);
2018-09-12 13:26:21 -04:00
}
2020-11-10 11:11:48 -05:00
/// <summary>
/// Resolves Name, Year and differentiate alternative files and extras from regular audiobook files.
/// </summary>
/// <param name="files">List of files related to audiobook.</param>
/// <returns>Returns IEnumerable of <see cref="AudioBookInfo"/>.</returns>
2019-05-10 14:37:42 -04:00
public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
2018-09-12 13:26:21 -04:00
{
// File with empty fullname will be sorted out here.
2018-09-12 13:26:21 -04:00
var audiobookFileInfos = files
2021-12-07 09:18:17 -05:00
.Select(i => _audioBookResolver.Resolve(i.FullName))
2022-01-04 11:05:36 -05:00
.OfType<AudioBookFileInfo>();
2018-09-12 13:26:21 -04:00
2021-12-07 09:18:17 -05:00
var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);
2018-09-12 13:26:21 -04:00
2020-01-22 16:18:56 -05:00
foreach (var stack in stackResult)
2018-09-12 13:26:21 -04:00
{
var stackFiles = stack.Files
2021-12-07 09:18:17 -05:00
.Select(i => _audioBookResolver.Resolve(i))
.OfType<AudioBookFileInfo>()
.ToList();
2018-09-12 13:26:21 -04:00
stackFiles.Sort();
var nameParserResult = new AudioBookNameParser(_options).Parse(stack.Name);
FindExtraAndAlternativeFiles(ref stackFiles, out var extras, out var alternativeVersions, nameParserResult);
var info = new AudioBookInfo(
nameParserResult.Name,
nameParserResult.Year,
stackFiles,
extras,
alternativeVersions);
2018-09-12 13:26:21 -04:00
2020-01-22 16:18:56 -05:00
yield return info;
}
2018-09-12 13:26:21 -04:00
}
private void FindExtraAndAlternativeFiles(ref List<AudioBookFileInfo> stackFiles, out List<AudioBookFileInfo> extras, out List<AudioBookFileInfo> alternativeVersions, AudioBookNameParserResult nameParserResult)
{
extras = new List<AudioBookFileInfo>();
alternativeVersions = new List<AudioBookFileInfo>();
2022-12-05 09:01:13 -05:00
var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber is not null || x.PartNumber is not null);
var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
2021-02-12 18:39:18 -05:00
var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
foreach (var group in groupedBy)
{
2022-12-05 09:00:20 -05:00
if (group.Key.ChapterNumber is null && group.Key.PartNumber is null)
{
if (group.Count() > 1 || haveChaptersOrPages)
{
2023-02-28 18:44:57 -05:00
List<AudioBookFileInfo>? ex = null;
List<AudioBookFileInfo>? alt = null;
foreach (var audioFile in group)
{
2023-02-28 18:44:57 -05:00
var name = Path.GetFileNameWithoutExtension(audioFile.Path.AsSpan());
if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase)
|| name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase)
|| name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
{
2023-02-28 18:44:57 -05:00
(alt ??= new()).Add(audioFile);
}
else
{
2023-02-28 18:44:57 -05:00
(ex ??= new()).Add(audioFile);
}
}
2023-02-28 18:44:57 -05:00
if (ex is not null)
{
var extra = ex
.OrderBy(x => x.Container)
2022-10-24 20:40:47 -04:00
.ThenBy(x => x.Path)
.ToList();
stackFiles = stackFiles.Except(extra).ToList();
extras.AddRange(extra);
}
2023-02-28 18:44:57 -05:00
if (alt is not null)
{
var alternatives = alt
.OrderBy(x => x.Container)
.ThenBy(x => x.Path)
.ToList();
var main = FindMainAudioBookFile(alternatives, nameParserResult.Name);
alternatives.Remove(main);
stackFiles = stackFiles.Except(alternatives).ToList();
alternativeVersions.AddRange(alternatives);
}
}
}
else if (group.Count() > 1)
{
var alternatives = group
.OrderBy(x => x.Container)
.ThenBy(x => x.Path)
.Skip(1)
.ToList();
stackFiles = stackFiles.Except(alternatives).ToList();
alternativeVersions.AddRange(alternatives);
}
}
}
private AudioBookFileInfo FindMainAudioBookFile(List<AudioBookFileInfo> files, string name)
{
var main = files.Find(x => Path.GetFileNameWithoutExtension(x.Path).Equals(name, StringComparison.OrdinalIgnoreCase));
main ??= files.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Path).Equals("audiobook", StringComparison.OrdinalIgnoreCase));
main ??= files.OrderBy(x => x.Container)
.ThenBy(x => x.Path)
.First();
return main;
}
2018-09-12 13:26:21 -04:00
}
}