ExtraResolver.cs 4.3 KB

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