public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { if (forceQuickMode) { return(false); } if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { string localFsPath = rah.LocalFsResourceAccessor.LocalFileSystemPath; return(ExtractSeriesData(localFsPath, extractedAspectData)); } } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("SeriesMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
private void ExtractLocalImages(IDictionary <Guid, IList <MediaItemAspect> > aspects, Guid?albumMediaItemId, IDictionary <Guid, string> artistMediaItems, string albumTitle) { if (BaseInfo.IsVirtualResource(aspects)) { return; } IResourceLocator mediaItemLocater = GetResourceLocator(aspects); if (mediaItemLocater == null) { return; } ExtractFolderImages(mediaItemLocater, albumMediaItemId, artistMediaItems, albumTitle); using (IResourceAccessor mediaItemAccessor = mediaItemLocater.CreateAccessor()) { using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { ExtractFileImages(rah.LocalFsResourceAccessor, albumMediaItemId, albumTitle); } } } }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode) { try { if (forceQuickMode) { return(false); } if (importOnly) { return(false); } if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) return(ExtractThumbnail(rah.LocalFsResourceAccessor, extractedAspectData)); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Error("VideoThumbnailer: Exception reading resource '{0}' (Text: '{1}')", e, mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { ILocalFsResourceAccessor lfsra = rah.LocalFsResourceAccessor; if (!lfsra.IsFile && lfsra.ResourceExists("BDMV")) { IFileSystemResourceAccessor fsraBDMV = lfsra.GetResource("BDMV"); if (fsraBDMV != null && fsraBDMV.ResourceExists("index.bdmv")) { // This line is important to keep in, if no VideoAspect is created here, the MediaItems is not detected as Video! MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata); MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); mediaAspect.SetAttribute(MediaAspect.ATTR_MIME_TYPE, "video/bluray"); // BluRay disc string bdmvDirectory = lfsra.LocalFileSystemPath; BDInfoExt bdinfo = new BDInfoExt(bdmvDirectory); string title = bdinfo.GetTitle(); mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, title ?? mediaItemAccessor.ResourceName); // Check for BD disc thumbs FileInfo thumbnail = bdinfo.GetBiggestThumb(); if (thumbnail != null) { try { using (FileStream fileStream = new FileStream(thumbnail.FullName, FileMode.Open, FileAccess.Read)) using (MemoryStream resized = (MemoryStream)ImageUtilities.ResizeImage(fileStream, ImageFormat.Jpeg, MAX_COVER_WIDTH, MAX_COVER_HEIGHT)) { MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, resized.ToArray()); } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } return(true); } } } return(false); } catch { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here if (mediaItemAccessor != null) { ServiceRegistration.Get <ILogger>().Info("BluRayMetadataExtractor: Exception reading source '{0}'", mediaItemAccessor.ResourcePathName); } return(false); } }
private void ExtractLocalImages(IDictionary <Guid, IList <MediaItemAspect> > aspects, Guid?episodeMediaItemId, Guid?seriesMediaItemId, Guid?seasonMediaItemId, EpisodeInfo episode, SeriesInfo series, SeasonInfo season, IDictionary <Guid, string> actorMediaItems) { if (BaseInfo.IsVirtualResource(aspects)) { return; } IResourceLocator mediaItemLocater = GetResourceLocator(aspects); if (mediaItemLocater == null) { return; } ExtractFolderImages(mediaItemLocater, episodeMediaItemId, seriesMediaItemId, seasonMediaItemId, episode, series, season, actorMediaItems); using (IResourceAccessor mediaItemAccessor = mediaItemLocater.CreateAccessor()) { using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { ExtractMkvImages(rah.LocalFsResourceAccessor, seriesMediaItemId, series); } } } }
/// <summary> /// Reads all tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="mediaItemLocator"><see cref="IResourceLocator>"/> that points to the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractFanArt(IResourceLocator mediaItemLocator, Guid mediaItemId, string title, IDictionary <Guid, IList <MediaItemAspect> > aspects) { try { //File based access using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { if (MKV_EXTENSIONS.Contains(ResourcePathHelper.GetExtension(mediaItemLocator.NativeResourcePath.FileName))) { await ExtractMkvFanArt(rah.LocalFsResourceAccessor, mediaItemId, title).ConfigureAwait(false); } if (MP4_EXTENSIONS.Contains(ResourcePathHelper.GetExtension(mediaItemLocator.NativeResourcePath.FileName))) { await ExtractTagFanArt(rah.LocalFsResourceAccessor, mediaItemId, title).ConfigureAwait(false); } //Don't create thumbs if they already exist or if it is a movie (they use posters) var thumbs = ServiceRegistration.Get <IFanArtCache>().GetFanArtFiles(mediaItemId, FanArtTypes.Thumbnail); if (!thumbs.Any() && !aspects.ContainsKey(ThumbnailLargeAspect.ASPECT_ID) && !aspects.ContainsKey(MovieAspect.ASPECT_ID)) { await ExtractThumbnailFanArt(rah.LocalFsResourceAccessor, mediaItemId, title, aspects); } } } catch (Exception ex) { Logger.Warn("VideoFanArtHandler: Exception while reading MKV tag images for '{0}'", ex, mediaItemLocator.NativeResourcePath); } }
public async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { if (forceQuickMode) { return(false); } byte[] thumb; // We only want to create missing thumbnails here, so check for existing ones first if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null) { return(false); } if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) return(await ExtractThumbnailAsync(rah.LocalFsResourceAccessor, extractedAspectData).ConfigureAwait(false)); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Error("OCVVideoThumbnailer: Exception reading resource '{0}' (Text: '{1}')", e, mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
private void ExtractLocalImages(IDictionary <Guid, IList <MediaItemAspect> > aspects, Guid?movieMediaItemId, Guid?collectionMediaItemId, string movieName, string collectionName, IDictionary <Guid, string> actorMediaItems) { if (BaseInfo.IsVirtualResource(aspects)) { return; } IResourceLocator mediaItemLocater = GetResourceLocator(aspects); if (mediaItemLocater == null) { return; } ExtractFolderImages(mediaItemLocater, movieMediaItemId, collectionMediaItemId, movieName, collectionName, actorMediaItems); using (IResourceAccessor mediaItemAccessor = mediaItemLocater.CreateAccessor()) { using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { ExtractMkvImages(rah.LocalFsResourceAccessor, movieMediaItemId, movieName); } } } }
/// <summary> /// Adds the TsReader filter to the graph. /// </summary> protected override void AddSourceFilter() { // Render the file _sourceFilter = FilterLoader.LoadFilterFromDll("TsReader.ax", typeof(TsReader).GUID, true); IFileSourceFilter fileSourceFilter = (IFileSourceFilter)_sourceFilter; ITsReader tsReader = (ITsReader)_sourceFilter; tsReader.SetRelaxedMode(1); tsReader.SetTsReaderCallback(this); tsReader.SetRequestAudioChangeCallback(this); _graphBuilder.AddFilter(_sourceFilter, TSREADER_FILTER_NAME); _subtitleRenderer = new SubtitleRenderer(OnTextureInvalidated); _subtitleFilter = _subtitleRenderer.AddSubtitleFilter(_graphBuilder); if (_subtitleFilter != null) { _subtitleRenderer.RenderSubtitles = true; _subtitleRenderer.SetPlayer(this); } if (_resourceLocator.NativeResourcePath.IsNetworkResource) { // _resourceAccessor points to an rtsp:// stream or network file var sourcePathOrUrl = SourcePathOrUrl; if (sourcePathOrUrl == null) { throw new IllegalCallException("The TsVideoPlayer can only play network resources of type INetworkResourceAccessor"); } ServiceRegistration.Get <ILogger>().Debug("{0}: Initializing for stream '{1}'", PlayerTitle, sourcePathOrUrl); int hr = fileSourceFilter.Load(SourcePathOrUrl, null); new HRESULT(hr).Throw(); } else { // _resourceAccessor points to a local or remote mapped .ts file _localFsRaHelper = new LocalFsResourceAccessorHelper(_resourceAccessor); var localFileSystemResourceAccessor = _localFsRaHelper.LocalFsResourceAccessor; if (localFileSystemResourceAccessor == null) { throw new IllegalCallException("The TsVideoPlayer can only play file resources of type ILocalFsResourceAccessor"); } ServiceRegistration.Get <ILogger>().Debug("{0}: Initializing for stream '{1}'", PlayerTitle, localFileSystemResourceAccessor.LocalFileSystemPath); int hr = fileSourceFilter.Load(localFileSystemResourceAccessor.LocalFileSystemPath, null); new HRESULT(hr).Throw(); } // Init GraphRebuilder _graphRebuilder = new GraphRebuilder(_graphBuilder, _sourceFilter, OnAfterGraphRebuild) { PlayerName = PlayerTitle }; }
protected override async Task <List <SubtitleInfo> > SearchSeriesEpisodeSubtitlesAsync(SubtitleInfo subtitleSearch, List <string> languages) { List <SubDbSearchResult> results = new List <SubDbSearchResult>(); var mediaFile = subtitleSearch.MediaFiles.First(); using (IResourceAccessor mediaItemAccessor = mediaFile.CreateAccessor()) using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { using (var stream = await rah.LocalFsResourceAccessor.OpenReadAsync()) { if (stream != null) { results = await _subDbHandler.SearchSubtitlesAsync(stream); } } } if (!results.Any()) { return(new List <SubtitleInfo>()); } var langs = languages.Select(s => new CultureInfo(s).TwoLetterISOLanguageName); if (!results.Any(r => langs.Any(l => l.Equals(r.LanguageCode, System.StringComparison.InvariantCultureIgnoreCase)))) { return(new List <SubtitleInfo>()); } return(results.Where(r => langs.Any(l => l.Equals(r.LanguageCode, System.StringComparison.InvariantCultureIgnoreCase))). Select(s => new SubtitleInfo { DisplayName = Path.GetFileNameWithoutExtension(mediaFile.NativeResourcePath.FileName), Name = Path.GetFileNameWithoutExtension(mediaFile.NativeResourcePath.FileName) + ".srt", TvdbId = subtitleSearch.TvdbId, Language = new CultureInfo(s.LanguageCode).Name, MediaFiles = subtitleSearch.MediaFiles, MediaTitle = subtitleSearch.MediaTitle, Episode = subtitleSearch.Episode, Season = subtitleSearch.Season, SubtitleId = s.DownloadUrl, Year = subtitleSearch.Year, DataProviders = new List <string>() { _name } }).ToList()); }
/// <summary> /// Reads all tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="mediaItemLocator"><see cref="IResourceLocator>"/> that points to the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractTagFanArt(IResourceLocator mediaItemLocator, Guid mediaItemId, string title) { try { //File based access using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) await ExtractTagFanArt(rah.LocalFsResourceAccessor, mediaItemId, title).ConfigureAwait(false); } catch (Exception ex) { Logger.Warn("AudioFanArtHandler: Exception while reading MKV tag images for '{0}'", ex, mediaItemLocator.NativeResourcePath); } }
public async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { IResourceAccessor disposeAccessor = null; try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null) { return(false); } if (!fsra.IsFile) { if (!IsChainedResourceProvider(mediaItemAccessor)) { return(false); } // The importer mounts iso and zip files as virtual directories but we need the accessor // to the original file so we have to create one here and remember to dispose it later. ResourcePath path = new ResourcePath(new[] { mediaItemAccessor.CanonicalLocalResourcePath.BasePathSegment }); if (!path.TryCreateLocalResourceAccessor(out disposeAccessor)) { return(false); } } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(disposeAccessor ?? mediaItemAccessor)) return(await ExtractGameData(rah.LocalFsResourceAccessor, extractedAspectData, forceQuickMode).ConfigureAwait(false)); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. Logger.Info("GamesMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } finally { if (disposeAccessor != null) { disposeAccessor.Dispose(); } } return(false); }
protected virtual async Task <bool> SaveSubtitleAsync(SubtitleInfo subtitle, IDictionary <BaseSubtitleMatch <TId>, byte[]> downloads, bool overwriteExisting) { var mediaFile = subtitle.MediaFiles.First(); var namingTemplate = ResourcePath.GetFileNameWithoutExtension(mediaFile.NativeResourcePath.FileName); var templatePartMatch = _regexMultiPartVideo.Match(namingTemplate); foreach (var subtitleMatch in downloads.Keys) { var subPartMatch = _regexMultiPartVideo.Match(subtitleMatch.ItemName); string subName = namingTemplate; if (subPartMatch.Success && templatePartMatch.Success) { if (!int.TryParse(templatePartMatch.Groups["disc"].Value, out var _) || !int.TryParse(subPartMatch.Groups["disc"].Value, out var partNum)) { continue; } subName = _regexMultiPartVideo.Replace(namingTemplate, "${1}${2}${4}" + partNum + "${3}"); } string lang = new CultureInfo(subtitleMatch.Language).EnglishName; var dir = ResourcePathHelper.GetDirectoryName(mediaFile.NativeResourcePath.Serialize()); var sub = $"{subName}.{lang}{Path.GetExtension(subtitleMatch.ItemName)}"; ResourcePath subtitlePath = ResourcePath.Deserialize(dir); //File based access var resLoc = new ResourceLocator(mediaFile.NativeSystemId, subtitlePath); using (IResourceAccessor mediaItemAccessor = resLoc.CreateAccessor()) using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { using (var stream = rah.LocalFsResourceAccessor.CreateOpenWrite(sub, overwriteExisting)) { var bytes = downloads[subtitleMatch]; if (stream != null) { await stream.WriteAsync(bytes, 0, bytes.Length); } } } } return(true); }
public Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { return(Task.FromResult(false)); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) return(Task.FromResult(ExtractGoodMergeData(rah.LocalFsResourceAccessor.LocalFileSystemPath, extractedAspectData))); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. Logger.Info("GoodMergeMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(Task.FromResult(false)); }
/// <summary> /// Reads all tag images and caches them in the <see cref="IFanArtCache"/> service. /// </summary> /// <param name="mediaItemLocator"><see cref="IResourceLocator>"/> that points to the file.</param> /// <param name="mediaItemId">Id of the media item.</param> /// <param name="title">Title of the media item.</param> /// <returns><see cref="Task"/> that completes when the images have been cached.</returns> protected async Task ExtractFanArt(IResourceLocator mediaItemLocator, Guid mediaItemId, string title) { try { //File based access using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { if (MKV_EXTENSIONS.Contains(ResourcePathHelper.GetExtension(mediaItemLocator.NativeResourcePath.FileName))) { await ExtractMkvFanArt(rah.LocalFsResourceAccessor, mediaItemId, title).ConfigureAwait(false); } if (MP4_EXTENSIONS.Contains(ResourcePathHelper.GetExtension(mediaItemLocator.NativeResourcePath.FileName))) { await ExtractTagFanArt(rah.LocalFsResourceAccessor, mediaItemId, title).ConfigureAwait(false); } } } catch (Exception ex) { Logger.Warn("VideoFanArtHandler: Exception while reading MKV tag images for '{0}'", ex, mediaItemLocator.NativeResourcePath); } }
public async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } if (extractedAspectData.ContainsKey(RecordingAspect.ASPECT_ID)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) return(await ExtractMetadataAsync(rah.LocalFsResourceAccessor, extractedAspectData, forceQuickMode).ConfigureAwait(false)); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("WTVRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
/// <summary> /// Tries to load chapter information from external file. This method checks for ComSkip files (.txt). /// </summary> /// <returns></returns> protected virtual bool EnumerateExternalChapters() { var fsra = _resourceAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { return(false); } try { string filePath = _resourceAccessor.CanonicalLocalResourcePath.ToString(); string metaFilePath = ProviderPathHelper.ChangeExtension(filePath, ".txt"); IResourceAccessor raTextFile; if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out raTextFile)) { return(false); } List <double> positions = new List <double>(); using (LocalFsResourceAccessorHelper lfsra = new LocalFsResourceAccessorHelper(raTextFile)) { if (lfsra.LocalFsResourceAccessor == null) { return(false); } using (var stream = lfsra.LocalFsResourceAccessor.OpenRead()) using (var chaptersReader = new StreamReader(stream)) { string line = chaptersReader.ReadLine(); int fps; if (string.IsNullOrWhiteSpace(line) || !int.TryParse(line.Substring(line.LastIndexOf(' ') + 1), out fps)) { ServiceRegistration.Get <ILogger>().Warn("VideoPlayer: EnumerateExternalChapters() - Invalid ComSkip chapter file"); return(false); } double framesPerSecond = fps / 100.0; while ((line = chaptersReader.ReadLine()) != null) { if (String.IsNullOrEmpty(line)) { continue; } string[] tokens = line.Split('\t'); if (tokens.Length != 2) { continue; } foreach (var token in tokens) { int time; if (int.TryParse(token, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out time)) { positions.Add(time / framesPerSecond); } } } } } // Insert start of video as position if (!positions.Contains(0d)) { positions.Insert(0, 0d); } var chapterNames = new List <string>(); var chapterTimes = new List <double>(); for (int index = 0; index < positions.Count - 1; index++) { var timeFrom = positions[index]; var timeTo = positions[index + 1]; // Filter out segments with less than 2 seconds duration if (timeTo - timeFrom <= 2) { continue; } var chapterName = string.Format("ComSkip {0} [{1} - {2}]", chapterNames.Count + 1, FormatDuration(timeFrom), FormatDuration(timeTo)); chapterNames.Add(chapterName); chapterTimes.Add(timeFrom); } _chapterNames = chapterNames.ToArray(); _chapterTimestamps = chapterTimes.ToArray(); return(_chapterNames.Length > 0); } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Error("VideoPlayer: EnumerateExternalChapters() - Exception while reading ComSkip chapter file", ex); return(false); } }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode) { try { if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } if (extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID)) { return(false); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { if (!rah.LocalFsResourceAccessor.IsFile && rah.LocalFsResourceAccessor.ResourceExists("BDMV")) { using (IFileSystemResourceAccessor fsraBDMV = rah.LocalFsResourceAccessor.GetResource("BDMV")) if (fsraBDMV != null && fsraBDMV.ResourceExists("index.bdmv")) { MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata); // Calling EnsureLocalFileSystemAccess not necessary; only string operation providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/bluray"); // BluRay disc providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, mediaItemAccessor.CanonicalLocalResourcePath.Serialize()); // This line is important to keep in, if no VideoAspect is created here, the MediaItems is not detected as Video! SingleMediaItemAspect videoAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata); videoAspect.SetAttribute(VideoAspect.ATTR_ISDVD, true); MultipleMediaItemAspect videoStreamAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, -1); MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); mediaAspect.SetAttribute(MediaAspect.ATTR_ISVIRTUAL, false); using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { BDInfoExt bdinfo = new BDInfoExt(rah.LocalFsResourceAccessor.LocalFileSystemPath); string title = bdinfo.GetTitle(); mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, title ?? mediaItemAccessor.ResourceName); // Check for BD disc thumbs FileInfo thumbnail = bdinfo.GetBiggestThumb(); if (thumbnail != null) { try { using (FileStream fileStream = new FileStream(thumbnail.FullName, FileMode.Open, FileAccess.Read)) using (MemoryStream resized = (MemoryStream)ImageUtilities.ResizeImage(fileStream, ImageFormat.Jpeg, MAX_COVER_WIDTH, MAX_COVER_HEIGHT)) { MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, resized.ToArray()); } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } } return(true); } } } return(false); } catch { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here if (mediaItemAccessor != null) { ServiceRegistration.Get <ILogger>().Info("BluRayMetadataExtractor: Exception reading source '{0}'", mediaItemAccessor.ResourcePathName); } return(false); } }
public virtual async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null) { return(false); } if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID)) { return(false); } if (forceQuickMode) { return(false); } if (fsra.IsFile) { string filePath = fsra.ResourcePathName; using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { ILocalFsResourceAccessor lfsra = rah.LocalFsResourceAccessor; if (lfsra != null) { int extSubtitles = FindExternalSubtitles(lfsra, extractedAspectData); int existsSubtitles = extractedAspectData.Count(d => d.Key == SubtitleAspect.ASPECT_ID); if (!SkipOnlineSearches && extSubtitles == 0 && existsSubtitles == 0) { SubtitleInfo subtitle = new SubtitleInfo(); subtitle.FromMetadata(extractedAspectData); var sys = ServiceRegistration.Get <ISystemResolver>(); subtitle.MediaFiles.Add(new ResourceLocator(sys.LocalSystemId, mediaItemAccessor.CanonicalLocalResourcePath)); List <SubtitleInfo> matches = new List <SubtitleInfo>(); if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID)) { foreach (var category in subtitle.Categories) { matches.AddRange(await OnlineMatcherService.Instance.FindMatchingMovieSubtitlesAsync(subtitle, ImportLanguageCultures.ToList(), category).ConfigureAwait(false)); } } else if (extractedAspectData.ContainsKey(EpisodeAspect.ASPECT_ID)) { foreach (var category in subtitle.Categories) { matches.AddRange(await OnlineMatcherService.Instance.FindMatchingEpisodeSubtitlesAsync(subtitle, ImportLanguageCultures.ToList(), category).ConfigureAwait(false)); } } if (matches.Count > 0) { //Order by language and ranking var subtitles = matches.OrderBy(s => s.LanguageMatchRank).OrderByDescending(s => s.MatchPercentage); if (subtitles?.Count() > 0) { //Download for each language foreach (var lang in subtitles.Select(s => s.LanguageMatchRank).Distinct().OrderBy(i => i)) { var sub = subtitles.FirstOrDefault(s => s.LanguageMatchRank == lang); if (sub != null) { await OnlineMatcherService.Instance.DownloadSubtitleAsync(subtitle, false); } } } } } return(true); } } } } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("SubtitleMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", e, mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode) { string fileName = mediaItemAccessor.ResourceName; if (!HasImageExtension(fileName)) { return(false); } bool refresh = false; if (extractedAspectData.ContainsKey(ImageAspect.ASPECT_ID)) { refresh = true; } try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (!refresh) { MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true); if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } // Open a stream for media item to detect mimeType. using (Stream mediaStream = fsra.OpenRead()) { string mimeType = MimeTypeDetector.GetMimeType(mediaStream) ?? DEFAULT_MIMETYPE; providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mimeType); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size); } } MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); mediaAspect.SetAttribute(MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect imageAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, ImageAspect.Metadata); if (!refresh) { // Extract EXIF information from media item. using (ExifMetaInfo.ExifMetaInfo exif = new ExifMetaInfo.ExifMetaInfo(fsra)) { mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, ProviderPathHelper.GetFileNameWithoutExtension(fileName)); mediaAspect.SetAttribute(MediaAspect.ATTR_RECORDINGTIME, exif.OriginalDate != DateTime.MinValue ? exif.OriginalDate : fsra.LastChanged); mediaAspect.SetAttribute(MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(exif.ImageDescription)); if (exif.PixXDim.HasValue) { imageAspect.SetAttribute(ImageAspect.ATTR_WIDTH, (int)exif.PixXDim); } if (exif.PixYDim.HasValue) { imageAspect.SetAttribute(ImageAspect.ATTR_HEIGHT, (int)exif.PixYDim); } imageAspect.SetAttribute(ImageAspect.ATTR_MAKE, StringUtils.TrimToNull(exif.EquipMake)); imageAspect.SetAttribute(ImageAspect.ATTR_MODEL, StringUtils.TrimToNull(exif.EquipModel)); if (exif.ExposureBias.HasValue) { imageAspect.SetAttribute(ImageAspect.ATTR_EXPOSURE_BIAS, ((double)exif.ExposureBias).ToString()); } imageAspect.SetAttribute(ImageAspect.ATTR_EXPOSURE_TIME, exif.ExposureTime); imageAspect.SetAttribute(ImageAspect.ATTR_FLASH_MODE, StringUtils.TrimToNull(exif.FlashMode)); if (exif.FNumber.HasValue) { imageAspect.SetAttribute(ImageAspect.ATTR_FNUMBER, string.Format("F {0}", (double)exif.FNumber)); } imageAspect.SetAttribute(ImageAspect.ATTR_ISO_SPEED, StringUtils.TrimToNull(exif.ISOSpeed)); imageAspect.SetAttribute(ImageAspect.ATTR_ORIENTATION, (Int32)(exif.OrientationType ?? 0)); imageAspect.SetAttribute(ImageAspect.ATTR_METERING_MODE, exif.MeteringMode.ToString()); if (exif.Latitude.HasValue && exif.Longitude.HasValue) { imageAspect.SetAttribute(ImageAspect.ATTR_LATITUDE, exif.Latitude); imageAspect.SetAttribute(ImageAspect.ATTR_LONGITUDE, exif.Longitude); } } byte[] thumbData; // We only want to create missing thumbnails here, so check for existing ones first if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumbData) && thumbData != null) { return(true); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { string localFsResourcePath = rah.LocalFsResourceAccessor.LocalFileSystemPath; if (localFsResourcePath != null) { // Thumbnail extraction IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>(); ImageType imageType; if (generator.GetThumbnail(localFsResourcePath, true, out thumbData, out imageType)) { MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, thumbData); } } } return(true); } else { bool updated = false; double?latitude = imageAspect.GetAttributeValue <double?>(ImageAspect.ATTR_LATITUDE); double?longitude = imageAspect.GetAttributeValue <double?>(ImageAspect.ATTR_LONGITUDE); if (IncludeGeoLocationDetails && !importOnly && latitude.HasValue && longitude.HasValue && string.IsNullOrEmpty(imageAspect.GetAttributeValue <string>(ImageAspect.ATTR_COUNTRY))) { CivicAddress locationInfo; if (!forceQuickMode && GeoLocationService.Instance.TryLookup(new GeoCoordinate(latitude.Value, longitude.Value), out locationInfo)) { imageAspect.SetAttribute(ImageAspect.ATTR_CITY, locationInfo.City); imageAspect.SetAttribute(ImageAspect.ATTR_STATE, locationInfo.StateProvince); imageAspect.SetAttribute(ImageAspect.ATTR_COUNTRY, locationInfo.CountryRegion); updated = true; } } byte[] thumbData; // We only want to create missing thumbnails here, so check for existing ones first if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumbData) && thumbData != null) { return(updated); } using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { string localFsResourcePath = rah.LocalFsResourceAccessor.LocalFileSystemPath; if (localFsResourcePath != null) { // In quick mode only allow thumbs taken from cache. bool cachedOnly = forceQuickMode; // Thumbnail extraction IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>(); ImageType imageType; if (generator.GetThumbnail(localFsResourcePath, cachedOnly, out thumbData, out imageType)) { MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, thumbData); updated = true; } } } return(updated); } } catch (Exception e) { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("ImageMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
private async void TranscodeProcessor(object args) { FFMpegTranscodeThreadData data = (FFMpegTranscodeThreadData)args; bool isStream = data.Context.Live && !data.Context.Segmented; bool isFile = true; bool isSlimTv = false; try { if (data.Context.Segmented == true) { await FFMpegPlaylistManifest.CreatePlaylistFilesAsync(data.TranscodeData).ConfigureAwait(false); } int liveChannelId = 0; bool runProcess = true; using (IResourceAccessor mediaAccessor = data.TranscodeData.GetFirstResourceAccessor()) { if (mediaAccessor is ITranscodeLiveAccessor tla) { isSlimTv = true; liveChannelId = tla.ChannelId; } } data.Context.Start(); data.TranscodeData.ClearRuntimeResourcePaths(); int exitCode = -1; string identifier = "Transcode_" + data.TranscodeData.ClientId; if (isSlimTv) { var result = await _slimtTvHandler.StartTuningAsync(identifier, liveChannelId).ConfigureAwait(false); if (!result.Success) { _logger.Error("FFMpegMediaConverter: Transcoder unable to start timeshifting for channel {0}", liveChannelId); runProcess = false; exitCode = 5000; } else { using (var mediaAccessor = await _slimtTvHandler.GetDefaultAccessorAsync(liveChannelId).ConfigureAwait(false)) { if (mediaAccessor is INetworkResourceAccessor) { int mediaStreamIndex = data.TranscodeData.FirstResourceIndex; data.TranscodeData.AddRuntimeResourcePath(ResourcePath.Deserialize(data.TranscodeData.InputMediaFilePaths[mediaStreamIndex]), mediaAccessor.CanonicalLocalResourcePath.Serialize()); } else { _logger.Error("FFMpegMediaConverter: Transcoder unable to start timeshifting for channel {0} because no URL was found", liveChannelId); runProcess = false; exitCode = 5001; } } } } ResourcePath firstPath; using (var mediaAccessor = data.TranscodeData.GetFirstResourceAccessor()) { firstPath = mediaAccessor.CanonicalLocalResourcePath; if (mediaAccessor is INetworkResourceAccessor) { isFile = false; } } if (runProcess) { //Prepare resources List <IDisposable> disposables = new List <IDisposable>(); foreach (var res in data.TranscodeData.GetResourceAccessors()) { if (!(res is INetworkResourceAccessor)) { var rah = new LocalFsResourceAccessorHelper(res); data.TranscodeData.AddRuntimeResourcePath(res.CanonicalLocalResourcePath, rah.LocalFsResourceAccessor.LocalFileSystemPath); var accessToken = rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess(); if (accessToken != null) { disposables.Add(accessToken); } disposables.Add(rah); } disposables.Add(res); } ProcessStartInfo startInfo = new ProcessStartInfo() { FileName = data.TranscodeData.TranscoderBinPath, WorkingDirectory = data.TranscodeData.WorkPath, Arguments = data.TranscodeData.TranscoderArguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; _logger.Debug("FFMpegMediaConverter: Transcoder '{0}' invoked with command line arguments '{1}'", data.TranscodeData.TranscoderBinPath, data.TranscodeData.TranscoderArguments); IntPtr userToken = IntPtr.Zero; try { //TODO: Fix usages of obsolete and deprecated methods when alternative is available #if !TRANSCODE_CONSOLE_TEST using (ServiceRegistration.Get <IImpersonationService>().CheckImpersonationFor(firstPath)) { //Only when the server is running as a service it will have elevation rights using (ImpersonationProcess ffmpeg = new ImpersonationProcess { StartInfo = startInfo }) { if (isFile && !ImpersonationHelper.GetTokenByProcess(out userToken, true)) { return; } #else { using (Process ffmpeg = new Process() { StartInfo = startInfo }) { #endif ffmpeg.EnableRaisingEvents = true; //Enable raising events because Process does not raise events by default. if (isStream == false) { ffmpeg.OutputDataReceived += data.Context.OutputDataReceived; } ffmpeg.ErrorDataReceived += data.Context.ErrorDataReceived; #if !TRANSCODE_CONSOLE_TEST if (isFile) { ffmpeg.StartAsUser(userToken); } else { ffmpeg.Start(); } #else ffmpeg.Start(); #endif ffmpeg.BeginErrorReadLine(); if (isStream == false) { ffmpeg.BeginOutputReadLine(); } else { data.TranscodeData.LiveStream = ffmpeg.StandardOutput.BaseStream; } RunTranscodingProcess(ffmpeg, data, isStream); ffmpeg.WaitForExit(); exitCode = ffmpeg.ExitCode; ffmpeg.Close(); } } } catch (Exception ex) { if (isStream || data.TranscodeData.OutputFilePath == null) { _logger.Error("FFMpegMediaConverter: Transcoder command failed for stream '{0}'", ex, data.TranscodeData.TranscodeId); } else { _logger.Error("FFMpegMediaConverter: Transcoder command failed for file '{0}'", ex, data.TranscodeData.OutputFilePath); } data.Context.Fail(); } finally { #if !TRANSCODE_CONSOLE_TEST if (userToken != IntPtr.Zero) { NativeMethods.CloseHandle(userToken); } #endif data.TranscodeData.LiveStream?.Dispose(); foreach (var disposable in disposables) { disposable?.Dispose(); } } } if (exitCode > 0) { data.Context.Fail(); } else { data.Context.Stop(); } _ffMpegEncoderHandler.EndEncoding(data.TranscodeData.Encoder, data.TranscodeData.TranscodeId); if (isSlimTv) { if (await _slimtTvHandler.EndTuningAsync(identifier).ConfigureAwait(false) == false) { _logger.Error("FFMpegMediaConverter: Transcoder unable to stop timeshifting for channel {0}", liveChannelId); } } string filePath = data.Context.TargetFile; bool isFolder = false; if (string.IsNullOrEmpty(data.Context.SegmentDir) == false) { filePath = data.Context.SegmentDir; isFolder = true; } if (exitCode > 0 || data.Context.Aborted) { if (exitCode > 0) { if (isStream || data.TranscodeData.OutputFilePath == null) { _logger.Debug("FFMpegMediaConverter: Transcoder command failed with error {1} for stream '{0}'", data.TranscodeData.TranscodeId, exitCode); } else { _logger.Debug("FFMpegMediaConverter: Transcoder command failed with error {1} for file '{0}'", data.TranscodeData.OutputFilePath, exitCode); } } if (data.Context.Aborted) { if (isStream || data.TranscodeData.OutputFilePath == null) { _logger.Debug("FFMpegMediaConverter: Transcoder command aborted for stream '{0}'", data.TranscodeData.TranscodeId); } else { _logger.Debug("FFMpegMediaConverter: Transcoder command aborted for file '{0}'", data.TranscodeData.OutputFilePath); } } else { _logger.Debug("FFMpegMediaConverter: FFMpeg error \n {0}", data.Context.ConsoleErrorOutput); } data.Context.DeleteFiles(); } else { //Touch cache files so they will not be cleaned up if (isFolder == false) { TouchFile(filePath); } else { TouchDirectory(filePath); } } } catch (Exception ex) { if (isStream || data.TranscodeData.OutputFilePath == null) { _logger.Error("FFMpegMediaConverter: Transcoder failed processing '{0}'", ex, data.TranscodeData.TranscodeId); } else { _logger.Error("FFMpegMediaConverter: Transcoder failed processing file '{0}'", ex, data.TranscodeData.OutputFilePath); } } finally { await RemoveTranscodeContextAsync(data.TranscodeData.ClientId, data.TranscodeData.TranscodeId, data.Context).ConfigureAwait(false); } }
/// <summary> /// Adds the TsReader filter to the graph. /// </summary> protected override void AddSourceFilter() { var platform = IntPtr.Size > 4 ? "x64" : "x86"; _sourceFilter = FilterLoader.LoadFilterFromDll($"{platform}\\TsReader.ax", typeof(TsReader).GUID, true); var baseFilter = _sourceFilter.GetFilter(); IFileSourceFilter fileSourceFilter = (IFileSourceFilter)baseFilter; _tsReader = (ITsReader)baseFilter; _tsReader.SetRelaxedMode(1); _tsReader.SetTsReaderCallback(this); _tsReader.SetRequestAudioChangeCallback(this); _graphBuilder.AddFilter(baseFilter, TSREADER_FILTER_NAME); if (_resourceLocator.NativeResourcePath.IsNetworkResource) { // _resourceAccessor points to an rtsp:// stream or network file var sourcePathOrUrl = SourcePathOrUrl; if (sourcePathOrUrl == null) { throw new IllegalCallException("The TsVideoPlayer can only play network resources of type INetworkResourceAccessor"); } ServiceRegistration.Get <ILogger>().Debug("{0}: Initializing for stream '{1}'", PlayerTitle, sourcePathOrUrl); IDisposable accessEnsurer = null; if (IsLocalFilesystemResource) { accessEnsurer = ((ILocalFsResourceAccessor)_resourceAccessor).EnsureLocalFileSystemAccess(); } using (accessEnsurer) { int hr = fileSourceFilter.Load(SourcePathOrUrl, null); new HRESULT(hr).Throw(); } } else { // _resourceAccessor points to a local or remote mapped .ts file _localFsRaHelper = new LocalFsResourceAccessorHelper(_resourceAccessor); var localFileSystemResourceAccessor = _localFsRaHelper.LocalFsResourceAccessor; if (localFileSystemResourceAccessor == null) { throw new IllegalCallException("The TsVideoPlayer can only play file resources of type ILocalFsResourceAccessor"); } using (localFileSystemResourceAccessor.EnsureLocalFileSystemAccess()) { ServiceRegistration.Get <ILogger>().Debug("{0}: Initializing for stream '{1}'", PlayerTitle, localFileSystemResourceAccessor.LocalFileSystemPath); int hr = fileSourceFilter.Load(localFileSystemResourceAccessor.LocalFileSystemPath, null); new HRESULT(hr).Throw(); } } // Init GraphRebuilder _graphRebuilder = new GraphRebuilder(_graphBuilder, baseFilter, OnAfterGraphRebuild) { PlayerName = PlayerTitle }; }
public override async Task <MetadataContainer> ParseMediaStreamAsync(IEnumerable <IResourceAccessor> mediaResources) { bool isImage = true; bool isFileSystem = false; bool isNetwork = false; bool isUnsupported = false; //Check all files if (!mediaResources.Any()) { throw new ArgumentException($"FFMpegMediaAnalyzer: Resource list is empty", "mediaResources"); } foreach (var res in mediaResources) { if (res is IFileSystemResourceAccessor fileRes) { isFileSystem = true; if (!fileRes.IsFile) { throw new ArgumentException($"FFMpegMediaAnalyzer: Resource '{res.ResourceName}' is not a file", "mediaResources"); } } else if (res is INetworkResourceAccessor urlRes) { isNetwork = true; } else { isUnsupported = true; } } if (isFileSystem && isNetwork) { throw new ArgumentException($"FFMpegMediaAnalyzer: Resources are of mixed media formats", "mediaResources"); } if (isUnsupported) { throw new ArgumentException($"FFMpegMediaAnalyzer: Resources are of unsupported media formats", "mediaResources"); } ProcessExecutionResult executionResult = null; string logFileName = "?"; if (isFileSystem) { List <LocalFsResourceAccessorHelper> helpers = new List <LocalFsResourceAccessorHelper>(); List <IDisposable> accessors = new List <IDisposable>(); try { MetadataContainer info = null; logFileName = mediaResources.First().ResourceName; //Initialize and check file system resources foreach (var res in mediaResources) { string resName = res.ResourceName; if (!(HasImageExtension(resName) || HasVideoExtension(resName) || HasAudioExtension(resName) || HasOpticalDiscFileExtension(resName))) { throw new ArgumentException($"FFMpegMediaAnalyzer: Resource '{res.ResourceName}' has unsupported file extension", "mediaResources"); } if (!(HasImageExtension(resName))) { isImage = false; } //Ensure availability var rah = new LocalFsResourceAccessorHelper(res); helpers.Add(rah); var accessor = rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess(); if (accessor != null) { accessors.Add(accessor); } } string fileName; if (helpers.Count > 1) //Check if concatenation needed { fileName = $"concat:\"{string.Join("|", helpers.Select(h => h.LocalFsResourceAccessor.LocalFileSystemPath))}\""; } else { fileName = $"\"{helpers.First().LocalFsResourceAccessor.LocalFileSystemPath}\""; } string arguments = ""; if (isImage) { //Default image decoder (image2) fails if file name contains å, ø, ö etc., so force format to image2pipe arguments = string.Format("-threads {0} -f image2pipe -i {1}", _analyzerMaximumThreads, fileName); } else { arguments = string.Format("-threads {0} -i {1}", _analyzerMaximumThreads, fileName); } //Use first file for parsing. The other files are expected to be of same encoding and same location var firstFile = helpers.First().LocalFsResourceAccessor; executionResult = await ParseFileAsync(helpers.First().LocalFsResourceAccessor, arguments); if (executionResult != null && executionResult.Success && executionResult.ExitCode == 0 && !string.IsNullOrEmpty(executionResult.StandardError)) { //_logger.Debug("MediaAnalyzer: Successfully ran FFProbe:\n {0}", executionResult.StandardError); info = new MetadataContainer(); info.AddEdition(Editions.DEFAULT_EDITION); info.Metadata[Editions.DEFAULT_EDITION].Size = helpers.Sum(h => h.LocalFsResourceAccessor.Size); FFMpegParseFFMpegOutput.ParseFFMpegOutput(firstFile, executionResult.StandardError, ref info, _countryCodesMapping); // Special handling for files like OGG which will be falsely identified as videos if (info.Metadata[0].VideoContainerType != VideoContainer.Unknown && info.Video[0].Codec == VideoCodec.Unknown) { info.Metadata[0].VideoContainerType = VideoContainer.Unknown; } if (info.IsImage(Editions.DEFAULT_EDITION) || HasImageExtension(fileName)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetFileMime(firstFile, "image/unknown"); } else if (info.IsVideo(Editions.DEFAULT_EDITION) || HasVideoExtension(fileName)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetFileMime(firstFile, "video/unknown"); await _probeLock.WaitAsync(); try { FFMpegParseH264Info.ParseH264Info(firstFile, info, _h264MaxDpbMbs, H264_TIMEOUT_MS); } finally { _probeLock.Release(); } FFMpegParseMPEG2TSInfo.ParseMPEG2TSInfo(firstFile, info); } else if (info.IsAudio(Editions.DEFAULT_EDITION) || HasAudioExtension(fileName)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetFileMime(firstFile, "audio/unknown"); } else { return(null); } return(info); } } finally { foreach (var accessor in accessors) { accessor?.Dispose(); } foreach (var helper in helpers) { helper?.Dispose(); } } if (executionResult != null) { _logger.Error("FFMpegMediaAnalyzer: Failed to extract media type information for resource '{0}', Result: {1}, ExitCode: {2}, Success: {3}", logFileName, executionResult.StandardError, executionResult.ExitCode, executionResult.Success); } else { _logger.Error("FFMpegMediaAnalyzer: Failed to extract media type information for resource '{0}', Execution result empty", logFileName); } } else if (isNetwork) { //We can only read one network resource so take the first var urlRes = mediaResources.First() as INetworkResourceAccessor; string url = urlRes.URL; string arguments = ""; if (url.StartsWith("rtsp://", StringComparison.InvariantCultureIgnoreCase) == true) { arguments += "-rtsp_transport tcp "; } arguments += "-analyzeduration " + _analyzerStreamTimeout + " "; //Resolve host first because ffprobe can hang when resolving host var resolvedUrl = UrlHelper.ResolveHostToIPv4Url(url); if (string.IsNullOrEmpty(resolvedUrl)) { throw new InvalidOperationException($"FFMpegMediaAnalyzer: Failed to resolve host for resource '{url}'"); } arguments += string.Format("-i \"{0}\"", resolvedUrl); executionResult = await ParseUrlAsync(url, arguments); if (executionResult != null && executionResult.Success && executionResult.ExitCode == 0 && !string.IsNullOrEmpty(executionResult.StandardError)) { //_logger.Debug("MediaAnalyzer: Successfully ran FFProbe:\n {0}", executionResult.StandardError); MetadataContainer info = new MetadataContainer(); info.AddEdition(Editions.DEFAULT_EDITION); info.Metadata[Editions.DEFAULT_EDITION].Size = 0; FFMpegParseFFMpegOutput.ParseFFMpegOutput(urlRes, executionResult.StandardError, ref info, _countryCodesMapping); // Special handling for files like OGG which will be falsely identified as videos if (info.Metadata[Editions.DEFAULT_EDITION].VideoContainerType != VideoContainer.Unknown && info.Video[Editions.DEFAULT_EDITION].Codec == VideoCodec.Unknown) { info.Metadata[Editions.DEFAULT_EDITION].VideoContainerType = VideoContainer.Unknown; } if (info.IsImage(Editions.DEFAULT_EDITION)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetUrlMime(url, "image/unknown"); } else if (info.IsVideo(Editions.DEFAULT_EDITION)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetUrlMime(url, "video/unknown"); await _probeLock.WaitAsync(); try { FFMpegParseH264Info.ParseH264Info(urlRes, info, _h264MaxDpbMbs, H264_TIMEOUT_MS); } finally { _probeLock.Release(); } FFMpegParseMPEG2TSInfo.ParseMPEG2TSInfo(urlRes, info); } else if (info.IsAudio(Editions.DEFAULT_EDITION)) { info.Metadata[Editions.DEFAULT_EDITION].Mime = MimeDetector.GetUrlMime(url, "audio/unknown"); } else { return(null); } return(info); } if (executionResult != null) { _logger.Error("FFMpegMediaAnalyzer: Failed to extract media type information for resource '{0}', Result: {1}, ExitCode: {2}, Success: {3}", url, executionResult.StandardError, executionResult.ExitCode, executionResult.Success); } else { _logger.Error("FFMpegMediaAnalyzer: Failed to extract media type information for resource '{0}', Execution result empty", url); } } return(null); }
protected override async Task <bool> ConvertSubtitleFileAsync(string clientId, VideoTranscoding video, double timeStart, string transcodingFile, SubtitleStream sourceSubtitle, SubtitleStream res) { SubtitleCodec targetCodec = video.TargetSubtitleCodec; if (targetCodec == SubtitleCodec.Unknown) { targetCodec = sourceSubtitle.Codec; } string tempFile = null; FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath) { TranscodeId = video.TranscodeId + "_sub", ClientId = clientId }; if (string.IsNullOrEmpty(video.TranscoderBinPath) == false) { data.TranscoderBinPath = video.TranscoderBinPath; } if (string.IsNullOrEmpty(video.TranscoderArguments) == false) { // TODO: not sure if this is working data.TranscoderArguments = video.TranscoderArguments; data.InputMediaFilePaths.Add(0, res.SourcePath); data.InputArguments.Add(0, new List <string>()); } else { tempFile = transcodingFile + ".tmp"; res = await ConvertSubtitleEncodingAsync(res, tempFile, video.TargetSubtitleCharacterEncoding).ConfigureAwait(false); // TODO: not sure if this is working _ffMpegCommandline.InitTranscodingParameters(false, new Dictionary <int, string> { { 0, res.SourcePath } }, ref data); data.InputArguments[0].Add(string.Format("-f {0}", FFMpegGetSubtitleContainer.GetSubtitleContainer(sourceSubtitle.Codec))); if (timeStart > 0) { data.OutputArguments.Add(string.Format(CultureInfo.InvariantCulture, "-ss {0:0.0}", timeStart)); } res.Codec = targetCodec; string subtitleEncoder = "copy"; if (res.Codec == SubtitleCodec.Unknown) { res.Codec = SubtitleCodec.Ass; } if (sourceSubtitle.Codec != res.Codec) { subtitleEncoder = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec); } string subtitleFormat = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec); data.OutputArguments.Add("-vn"); data.OutputArguments.Add("-an"); data.OutputArguments.Add(string.Format("-c:s {0}", subtitleEncoder)); data.OutputArguments.Add(string.Format("-f {0}", subtitleFormat)); } data.OutputFilePath = transcodingFile; _logger.Debug("FFMpegMediaConverter: Invoking transcoder to transcode subtitle file '{0}' for transcode '{1}'", res.SourcePath, data.TranscodeId); bool success = false; var path = ResourcePath.Deserialize(res.SourcePath); if (path.TryCreateLocalResourceAccessor(out var subRes)) { using (var rah = new LocalFsResourceAccessorHelper(subRes)) using (var access = rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess()) { var result = await FFMpegBinary.FFMpegExecuteWithResourceAccessAsync(rah.LocalFsResourceAccessor, data.TranscoderArguments, ProcessPriorityClass.Normal, _transcoderTimeout).ConfigureAwait(false); success = result.Success; } } if (success && File.Exists(transcodingFile) == true) { if (tempFile != null && File.Exists(tempFile)) { File.Delete(tempFile); } res.SourcePath = LocalFsResourceProviderBase.ToProviderPath(transcodingFile); return(true); } return(false); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { VideoResult result = null; IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null) { return(false); } if (!fsra.IsFile && fsra.ResourceExists("VIDEO_TS")) { IFileSystemResourceAccessor fsraVideoTs = fsra.GetResource("VIDEO_TS"); if (fsraVideoTs != null && fsraVideoTs.ResourceExists("VIDEO_TS.IFO")) { // Video DVD using (MediaInfoWrapper videoTsInfo = ReadMediaInfo(fsraVideoTs.GetResource("VIDEO_TS.IFO"))) { if (!videoTsInfo.IsValid || videoTsInfo.GetVideoCount() == 0) { return(false); // Invalid video_ts.ifo file } result = VideoResult.CreateDVDInfo(fsra.ResourceName, videoTsInfo); } // Iterate over all video files; MediaInfo finds different audio/video metadata for each .ifo file ICollection <IFileSystemResourceAccessor> files = fsraVideoTs.GetFiles(); if (files != null) { foreach (IFileSystemResourceAccessor file in files) { string lowerPath = (file.ResourcePathName ?? string.Empty).ToLowerInvariant(); if (!lowerPath.EndsWith(".ifo") || lowerPath.EndsWith("video_ts.ifo")) { continue; } using (MediaInfoWrapper mediaInfo = ReadMediaInfo(file)) { // Before we start evaluating the file, check if it is a video at all if (mediaInfo.IsValid && mediaInfo.GetVideoCount() == 0) { continue; } result.AddMediaInfo(mediaInfo); } } } } } else if (fsra.IsFile) { string filePath = fsra.ResourcePathName; if (!HasVideoExtension(filePath)) { return(false); } using (MediaInfoWrapper fileInfo = ReadMediaInfo(fsra)) { // Before we start evaluating the file, check if it is a video at all if (!fileInfo.IsValid || (fileInfo.GetVideoCount() == 0 && !IsWorkaroundRequired(filePath))) { return(false); } string mediaTitle = DosPathHelper.GetFileNameWithoutExtension(fsra.ResourceName); result = VideoResult.CreateFileInfo(mediaTitle, fileInfo); } using (Stream stream = fsra.OpenRead()) result.MimeType = MimeTypeDetector.GetMimeType(stream, DEFAULT_MIMETYPE); } if (result != null) { result.UpdateMetadata(extractedAspectData); using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor)) { ILocalFsResourceAccessor lfsra = rah.LocalFsResourceAccessor; if (lfsra != null) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SIZE, lfsra.Size); ExtractMatroskaTags(lfsra, extractedAspectData, forceQuickMode); ExtractMp4Tags(lfsra, extractedAspectData, forceQuickMode); ExtractThumbnailData(lfsra, extractedAspectData, forceQuickMode); } return(true); } } } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("VideoMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }