ExtraResolver.cs 4.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. using System;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.IO;
  4. using Emby.Naming.Common;
  5. using Emby.Naming.Video;
  6. using MediaBrowser.Controller.Entities;
  7. using MediaBrowser.Controller.Providers;
  8. using MediaBrowser.Controller.Resolvers;
  9. using MediaBrowser.Model.Entities;
  10. using Microsoft.Extensions.Logging;
  11. using static Emby.Naming.Video.ExtraRuleResolver;
  12. namespace Emby.Server.Implementations.Library.Resolvers
  13. {
  14. /// <summary>
  15. /// Resolves a Path into a Video or Video subclass.
  16. /// </summary>
  17. internal class ExtraResolver
  18. {
  19. private readonly NamingOptions _namingOptions;
  20. private readonly IItemResolver[] _trailerResolvers;
  21. private readonly IItemResolver[] _videoResolvers;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="ExtraResolver"/> class.
  24. /// </summary>
  25. /// <param name="logger">The logger.</param>
  26. /// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
  27. /// <param name="directoryService">The directory service.</param>
  28. public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
  29. {
  30. _namingOptions = namingOptions;
  31. _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directoryService) };
  32. _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions, directoryService) };
  33. }
  34. /// <summary>
  35. /// Gets the resolvers for the extra type.
  36. /// </summary>
  37. /// <param name="extraType">The extra type.</param>
  38. /// <returns>The resolvers for the extra type.</returns>
  39. public IItemResolver[]? GetResolversForExtraType(ExtraType extraType) => extraType switch
  40. {
  41. ExtraType.Trailer => _trailerResolvers,
  42. // For audio we'll have to rely on the AudioResolver, which is a "built-in"
  43. ExtraType.ThemeSong => null,
  44. _ => _videoResolvers
  45. };
  46. public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType)
  47. {
  48. var extraResult = GetExtraInfo(path, _namingOptions);
  49. if (extraResult.ExtraType is null)
  50. {
  51. extraType = null;
  52. return false;
  53. }
  54. var cleanDateTimeResult = CleanDateTimeParser.Clean(Path.GetFileNameWithoutExtension(path), _namingOptions.CleanDateTimeRegexes);
  55. var name = cleanDateTimeResult.Name;
  56. var year = cleanDateTimeResult.Year;
  57. var parentDir = ownerVideoFileInfo.IsDirectory ? ownerVideoFileInfo.Path : Path.GetDirectoryName(ownerVideoFileInfo.Path.AsSpan());
  58. var trimmedFileNameWithoutExtension = TrimFilenameDelimiters(ownerVideoFileInfo.FileNameWithoutExtension, _namingOptions.VideoFlagDelimiters);
  59. var trimmedVideoInfoName = TrimFilenameDelimiters(ownerVideoFileInfo.Name, _namingOptions.VideoFlagDelimiters);
  60. var trimmedExtraFileName = TrimFilenameDelimiters(name, _namingOptions.VideoFlagDelimiters);
  61. // first check filenames
  62. bool isValid = StartsWith(trimmedExtraFileName, trimmedFileNameWithoutExtension)
  63. || (StartsWith(trimmedExtraFileName, trimmedVideoInfoName) && year == ownerVideoFileInfo.Year);
  64. if (!isValid)
  65. {
  66. // When the extra rule type is DirectoryName we must go one level higher to get the "real" dir name
  67. var currentParentDir = extraResult.Rule?.RuleType == ExtraRuleType.DirectoryName
  68. ? Path.GetDirectoryName(Path.GetDirectoryName(path.AsSpan()))
  69. : Path.GetDirectoryName(path.AsSpan());
  70. isValid = !currentParentDir.IsEmpty && !parentDir.IsEmpty && currentParentDir.Equals(parentDir, StringComparison.OrdinalIgnoreCase);
  71. }
  72. extraType = extraResult.ExtraType;
  73. return isValid;
  74. }
  75. private static ReadOnlySpan<char> TrimFilenameDelimiters(ReadOnlySpan<char> name, ReadOnlySpan<char> videoFlagDelimiters)
  76. {
  77. return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd();
  78. }
  79. private static bool StartsWith(ReadOnlySpan<char> fileName, ReadOnlySpan<char> baseName)
  80. {
  81. return !baseName.IsEmpty && fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase);
  82. }
  83. }
  84. }