2020-11-01 16:10:48 +00:00
|
|
|
using System;
|
2019-01-13 19:54:44 +00:00
|
|
|
using System.Collections.Generic;
|
2020-11-01 16:10:48 +00:00
|
|
|
using System.IO;
|
2018-09-12 17:26:21 +00:00
|
|
|
using System.Linq;
|
|
|
|
using Emby.Naming.Common;
|
|
|
|
using Emby.Naming.Video;
|
|
|
|
using MediaBrowser.Model.IO;
|
|
|
|
|
|
|
|
namespace Emby.Naming.AudioBook
|
|
|
|
{
|
2020-11-10 16:11:48 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Class used to resolve Name, Year, alternative files and extras from stack of files.
|
|
|
|
/// </summary>
|
2018-09-12 17:26:21 +00:00
|
|
|
public class AudioBookListResolver
|
|
|
|
{
|
|
|
|
private readonly NamingOptions _options;
|
2021-12-07 14:18:17 +00:00
|
|
|
private readonly AudioBookResolver _audioBookResolver;
|
2018-09-12 17:26:21 +00:00
|
|
|
|
2020-11-10 16:11:48 +00: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 17:26:21 +00:00
|
|
|
public AudioBookListResolver(NamingOptions options)
|
|
|
|
{
|
|
|
|
_options = options;
|
2021-12-07 14:18:17 +00:00
|
|
|
_audioBookResolver = new AudioBookResolver(_options);
|
2018-09-12 17:26:21 +00:00
|
|
|
}
|
|
|
|
|
2020-11-10 16:11:48 +00: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 18:37:42 +00:00
|
|
|
public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
|
2018-09-12 17:26:21 +00:00
|
|
|
{
|
2020-11-12 11:54:55 +00:00
|
|
|
// File with empty fullname will be sorted out here.
|
2018-09-12 17:26:21 +00:00
|
|
|
var audiobookFileInfos = files
|
2021-12-07 14:18:17 +00:00
|
|
|
.Select(i => _audioBookResolver.Resolve(i.FullName))
|
2022-01-04 16:05:36 +00:00
|
|
|
.OfType<AudioBookFileInfo>();
|
2018-09-12 17:26:21 +00:00
|
|
|
|
2021-12-07 14:18:17 +00:00
|
|
|
var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);
|
2018-09-12 17:26:21 +00:00
|
|
|
|
2020-01-22 21:18:56 +00:00
|
|
|
foreach (var stack in stackResult)
|
2018-09-12 17:26:21 +00:00
|
|
|
{
|
2020-11-01 16:10:48 +00:00
|
|
|
var stackFiles = stack.Files
|
2021-12-07 14:18:17 +00:00
|
|
|
.Select(i => _audioBookResolver.Resolve(i))
|
2020-11-01 16:10:48 +00:00
|
|
|
.OfType<AudioBookFileInfo>()
|
|
|
|
.ToList();
|
|
|
|
|
2018-09-12 17:26:21 +00:00
|
|
|
stackFiles.Sort();
|
2020-11-01 16:10:48 +00:00
|
|
|
|
2020-11-03 15:24:04 +00:00
|
|
|
var nameParserResult = new AudioBookNameParser(_options).Parse(stack.Name);
|
2020-11-02 19:03:12 +00:00
|
|
|
|
2020-11-03 15:24:04 +00:00
|
|
|
FindExtraAndAlternativeFiles(ref stackFiles, out var extras, out var alternativeVersions, nameParserResult);
|
|
|
|
|
|
|
|
var info = new AudioBookInfo(
|
|
|
|
nameParserResult.Name,
|
|
|
|
nameParserResult.Year,
|
|
|
|
stackFiles,
|
|
|
|
extras,
|
|
|
|
alternativeVersions);
|
2018-09-12 17:26:21 +00:00
|
|
|
|
2020-01-22 21:18:56 +00:00
|
|
|
yield return info;
|
|
|
|
}
|
2018-09-12 17:26:21 +00:00
|
|
|
}
|
2020-11-03 15:24:04 +00: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 14:01:13 +00:00
|
|
|
var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber is not null || x.PartNumber is not null);
|
2020-11-03 15:24:04 +00:00
|
|
|
var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
|
2021-02-12 23:39:18 +00:00
|
|
|
var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
|
2020-11-03 15:24:04 +00:00
|
|
|
|
|
|
|
foreach (var group in groupedBy)
|
|
|
|
{
|
2022-12-05 14:00:20 +00:00
|
|
|
if (group.Key.ChapterNumber is null && group.Key.PartNumber is null)
|
2020-11-03 15:24:04 +00:00
|
|
|
{
|
|
|
|
if (group.Count() > 1 || haveChaptersOrPages)
|
|
|
|
{
|
2023-02-28 23:44:57 +00:00
|
|
|
List<AudioBookFileInfo>? ex = null;
|
|
|
|
List<AudioBookFileInfo>? alt = null;
|
2020-11-03 15:24:04 +00:00
|
|
|
|
|
|
|
foreach (var audioFile in group)
|
|
|
|
{
|
2023-02-28 23:44:57 +00: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))
|
2020-11-03 15:24:04 +00:00
|
|
|
{
|
2023-02-28 23:44:57 +00:00
|
|
|
(alt ??= new()).Add(audioFile);
|
2020-11-03 15:24:04 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-02-28 23:44:57 +00:00
|
|
|
(ex ??= new()).Add(audioFile);
|
2020-11-03 15:24:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 23:44:57 +00:00
|
|
|
if (ex is not null)
|
2020-11-03 15:24:04 +00:00
|
|
|
{
|
|
|
|
var extra = ex
|
|
|
|
.OrderBy(x => x.Container)
|
2022-10-25 00:40:47 +00:00
|
|
|
.ThenBy(x => x.Path)
|
|
|
|
.ToList();
|
2020-11-03 15:24:04 +00:00
|
|
|
|
|
|
|
stackFiles = stackFiles.Except(extra).ToList();
|
|
|
|
extras.AddRange(extra);
|
|
|
|
}
|
|
|
|
|
2023-02-28 23:44:57 +00:00
|
|
|
if (alt is not null)
|
2020-11-03 15:24:04 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2020-11-12 15:51:52 +00:00
|
|
|
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));
|
2020-11-03 15:24:04 +00:00
|
|
|
main ??= files.OrderBy(x => x.Container)
|
|
|
|
.ThenBy(x => x.Path)
|
|
|
|
.First();
|
|
|
|
|
|
|
|
return main;
|
|
|
|
}
|
2018-09-12 17:26:21 +00:00
|
|
|
}
|
|
|
|
}
|