123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Emby.Naming.Common;
- using Emby.Naming.ExternalFiles;
- using MediaBrowser.Controller.Entities;
- using MediaBrowser.Controller.MediaEncoding;
- using MediaBrowser.Controller.Providers;
- using MediaBrowser.Model.Dlna;
- using MediaBrowser.Model.Dto;
- using MediaBrowser.Model.Entities;
- using MediaBrowser.Model.Globalization;
- using MediaBrowser.Model.IO;
- using MediaBrowser.Model.MediaInfo;
- using Microsoft.Extensions.Logging;
- namespace MediaBrowser.Providers.MediaInfo
- {
- /// <summary>
- /// Resolves external files for <see cref="Video"/>.
- /// </summary>
- public abstract class MediaInfoResolver
- {
- /// <summary>
- /// The <see cref="ExternalPathParser"/> instance.
- /// </summary>
- private readonly ExternalPathParser _externalPathParser;
- /// <summary>
- /// The <see cref="IMediaEncoder"/> instance.
- /// </summary>
- private readonly IMediaEncoder _mediaEncoder;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- /// <summary>
- /// The <see cref="NamingOptions"/> instance.
- /// </summary>
- private readonly NamingOptions _namingOptions;
- /// <summary>
- /// The <see cref="DlnaProfileType"/> of the files this resolver should resolve.
- /// </summary>
- private readonly DlnaProfileType _type;
- /// <summary>
- /// Initializes a new instance of the <see cref="MediaInfoResolver"/> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="localizationManager">The localization manager.</param>
- /// <param name="mediaEncoder">The media encoder.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="namingOptions">The <see cref="NamingOptions"/> object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters.</param>
- /// <param name="type">The <see cref="DlnaProfileType"/> of the parsed file.</param>
- protected MediaInfoResolver(
- ILogger logger,
- ILocalizationManager localizationManager,
- IMediaEncoder mediaEncoder,
- IFileSystem fileSystem,
- NamingOptions namingOptions,
- DlnaProfileType type)
- {
- _logger = logger;
- _mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _namingOptions = namingOptions;
- _type = type;
- _externalPathParser = new ExternalPathParser(namingOptions, localizationManager, _type);
- }
- /// <summary>
- /// Retrieves the external streams for the provided video.
- /// </summary>
- /// <param name="video">The <see cref="Video"/> object to search external streams for.</param>
- /// <param name="startIndex">The stream index to start adding external streams at.</param>
- /// <param name="directoryService">The directory service to search for files.</param>
- /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>The external streams located.</returns>
- public async Task<IReadOnlyList<MediaStream>> GetExternalStreamsAsync(
- Video video,
- int startIndex,
- IDirectoryService directoryService,
- bool clearCache,
- CancellationToken cancellationToken)
- {
- if (!video.IsFileProtocol)
- {
- return Array.Empty<MediaStream>();
- }
- var pathInfos = GetExternalFiles(video, directoryService, clearCache);
- if (!pathInfos.Any())
- {
- return Array.Empty<MediaStream>();
- }
- var mediaStreams = new List<MediaStream>();
- foreach (var pathInfo in pathInfos)
- {
- if (!pathInfo.Path.AsSpan().EndsWith(".strm", StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- var mediaInfo = await GetMediaInfo(pathInfo.Path, _type, cancellationToken).ConfigureAwait(false);
- if (mediaInfo.MediaStreams.Count == 1)
- {
- MediaStream mediaStream = mediaInfo.MediaStreams[0];
- if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio)
- || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle))
- {
- mediaStream.Index = startIndex++;
- mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault;
- mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced;
- mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
- }
- }
- else
- {
- foreach (MediaStream mediaStream in mediaInfo.MediaStreams)
- {
- if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio)
- || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle))
- {
- mediaStream.Index = startIndex++;
- mediaStreams.Add(MergeMetadata(mediaStream, pathInfo));
- }
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting external streams from {Path}", pathInfo.Path);
- continue;
- }
- }
- }
- return mediaStreams.AsReadOnly();
- }
- /// <summary>
- /// Returns the external file infos for the given video.
- /// </summary>
- /// <param name="video">The <see cref="Video"/> object to search external files for.</param>
- /// <param name="directoryService">The directory service to search for files.</param>
- /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
- /// <returns>The external file paths located.</returns>
- public IReadOnlyList<ExternalPathParserResult> GetExternalFiles(
- Video video,
- IDirectoryService directoryService,
- bool clearCache)
- {
- if (!video.IsFileProtocol)
- {
- return Array.Empty<ExternalPathParserResult>();
- }
- // Check if video folder exists
- string folder = video.ContainingFolderPath;
- if (!_fileSystem.DirectoryExists(folder))
- {
- return Array.Empty<ExternalPathParserResult>();
- }
- var files = directoryService.GetFilePaths(folder, clearCache).ToList();
- files.Remove(video.Path);
- var internalMetadataPath = video.GetInternalMetadataPath();
- if (_fileSystem.DirectoryExists(internalMetadataPath))
- {
- files.AddRange(directoryService.GetFilePaths(internalMetadataPath, clearCache));
- }
- if (!files.Any())
- {
- return Array.Empty<ExternalPathParserResult>();
- }
- var externalPathInfos = new List<ExternalPathParserResult>();
- ReadOnlySpan<char> prefix = video.FileNameWithoutExtension;
- foreach (var file in files)
- {
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file.AsSpan());
- if (fileNameWithoutExtension.Length >= prefix.Length
- && prefix.Equals(fileNameWithoutExtension[..prefix.Length], StringComparison.OrdinalIgnoreCase)
- && (fileNameWithoutExtension.Length == prefix.Length || _namingOptions.MediaFlagDelimiters.Contains(fileNameWithoutExtension[prefix.Length])))
- {
- var externalPathInfo = _externalPathParser.ParseFile(file, fileNameWithoutExtension[prefix.Length..].ToString());
- if (externalPathInfo != null)
- {
- externalPathInfos.Add(externalPathInfo);
- }
- }
- }
- return externalPathInfos;
- }
- /// <summary>
- /// Returns the media info of the given file.
- /// </summary>
- /// <param name="path">The path to the file.</param>
- /// <param name="type">The <see cref="DlnaProfileType"/>.</param>
- /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
- /// <returns>The media info for the given file.</returns>
- private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(string path, DlnaProfileType type, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- return _mediaEncoder.GetMediaInfo(
- new MediaInfoRequest
- {
- MediaType = type,
- MediaSource = new MediaSourceInfo
- {
- Path = path,
- Protocol = MediaProtocol.File
- }
- },
- cancellationToken);
- }
- /// <summary>
- /// Merges path metadata into stream metadata.
- /// </summary>
- /// <param name="mediaStream">The <see cref="MediaStream"/> object.</param>
- /// <param name="pathInfo">The <see cref="ExternalPathParserResult"/> object.</param>
- /// <returns>The modified mediaStream.</returns>
- private MediaStream MergeMetadata(MediaStream mediaStream, ExternalPathParserResult pathInfo)
- {
- mediaStream.Path = pathInfo.Path;
- mediaStream.IsExternal = true;
- mediaStream.Title = string.IsNullOrEmpty(mediaStream.Title) ? (string.IsNullOrEmpty(pathInfo.Title) ? null : pathInfo.Title) : mediaStream.Title;
- mediaStream.Language = string.IsNullOrEmpty(mediaStream.Language) ? (string.IsNullOrEmpty(pathInfo.Language) ? null : pathInfo.Language) : mediaStream.Language;
- return mediaStream;
- }
- }
- }
|