/// <summary> /// Creates a new instance of this class which is based on the given <paramref name="baseAccessor"/>. /// </summary> /// <param name="key">Key which is used for this instance.</param> /// <param name="baseAccessor">Resource accessor denoting a filesystem resource.</param> public MountingDataProxy(string key, IResourceAccessor baseAccessor) { _key = key; _baseAccessor = baseAccessor; if (!MountResource()) throw new EnvironmentException("Cannot mount resource '{0}' to local file system", baseAccessor); }
public override bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { IResourceAccessor metaFileAccessor; if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor)) return false; Argus.Recording recording; using (metaFileAccessor) { using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead()) recording = (Argus.Recording)GetTagsXmlSerializer().Deserialize(metaStream); } // Handle series information SeriesInfo seriesInfo = GetSeriesFromTags(recording); if (seriesInfo.IsCompleteMatch) { if (!forceQuickMode) SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo); seriesInfo.SetMetadata(extractedAspectData); } 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("ArgusRecordingSeriesMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return false; }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { // TODO: support different ResourceAccessors for either local files (single seat) or network streams (multi seat). Current implementation always uses // network streams, even in single seat. result = SlimTvResourceAccessor.GetResourceAccessor(path); return result != null; }
/// <summary> /// Constructs a <see cref="ResourceAccessorTextureImageSource"/> for the given data. /// </summary> /// <param name="resourceAccessor">The resource accessor to load the texture data from.</param> /// <param name="rotation">Desired rotation for the given image.</param> public ResourceAccessorTextureImageSource(IResourceAccessor resourceAccessor, RightAngledRotation rotation) { _key = resourceAccessor.CanonicalLocalResourcePath.Serialize(); _resourceAccessor = resourceAccessor; _stream = _resourceAccessor.OpenRead(); _rotation = rotation; }
/// <summary> /// Sets the font manager up with the specified <paramref name="resourcesCollection"/>. /// This method will load the font defaults (family and size) and the font files from the /// resource collection. /// </summary> public static void Load(IResourceAccessor resourcesCollection) { Unload(); string defaultFontFilePath = resourcesCollection.GetResourceFilePath( SkinResources.FONTS_DIRECTORY + Path.DirectorySeparatorChar + DEFAULT_FONT_FILE); XPathDocument doc = new XPathDocument(defaultFontFilePath); XPathNavigator nav = doc.CreateNavigator(); nav.MoveToChild(XPathNodeType.Element); _defaultFontFamily = nav.GetAttribute("FontFamily", string.Empty); string defaultFontSize = nav.GetAttribute("FontSize", string.Empty); _defaultFontSize = int.Parse(defaultFontSize); // Iterate over font family descriptors foreach (string descriptorFilePath in resourcesCollection.GetResourceFilePaths( "^" + SkinResources.FONTS_DIRECTORY + "\\\\.*\\.desc$").Values) { doc = new XPathDocument(descriptorFilePath); nav = doc.CreateNavigator(); nav.MoveToChild(XPathNodeType.Element); string familyName = nav.GetAttribute("Name", string.Empty); if (string.IsNullOrEmpty(familyName)) throw new ArgumentException("FontManager: Failed to parse family name for font descriptor file '{0}'", descriptorFilePath); string ttfFile = nav.GetAttribute("Ttf", string.Empty); if (string.IsNullOrEmpty(ttfFile)) throw new ArgumentException("FontManager: Failed to parse ttf name for font descriptor file '{0}'", descriptorFilePath); string fontFilePath = resourcesCollection.GetResourceFilePath( SkinResources.FONTS_DIRECTORY + Path.DirectorySeparatorChar + ttfFile); FontFamily family = new FontFamily(familyName, fontFilePath); _families[familyName] = family; } }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { result = null; if (!IsResource(path)) return false; result = new RawUrlResourceAccessor(path); return true; }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { if (!IsResource(path)) { result = null; return false; } result = new NetworkNeighborhoodResourceAccessor(this, path); return true; }
public void Dispose() { if (_baseAccessor == null) // Already disposed return; if (!UnmountResource()) // The ownership was transferred to the resource mounting service, so if unmounting was succesful, we must not dispose our base accessor _baseAccessor.Dispose(); _baseAccessor = null; }
public static void AddSourceFilterOverride(Action fallbackAction, IResourceAccessor resourceAccessor, IGraphBuilder graphBuilder) { string sourceFilterName = GetSourceFilterName(resourceAccessor.ResourcePathName); if (string.IsNullOrEmpty(sourceFilterName)) { fallbackAction(); } else { AddStreamSourceFilter(sourceFilterName, resourceAccessor, graphBuilder); } }
public ZipResourceProxy(string key, IResourceAccessor zipFileAccessor) { _key = key; _zipFileResourceAccessor = zipFileAccessor; _zipFileStream = _zipFileResourceAccessor.OpenRead(); // Not sure if the ZipFile closes the stream appropriately, so we keep a reference to it try { _zipFile = new ZipFile(_zipFileStream); } catch { _zipFileStream.Dispose(); throw; } }
public virtual void Dispose() { foreach (FileHandle handle in _fileHandles) handle.Cleanup(); if (_resourceAccessor != null) try { _resourceAccessor.Dispose(); } catch (Exception e) { ServiceRegistration.Get<ILogger>().Warn("Dokan virtual filesystem resource: Error disposing resource accessor '{0}'", e, _resourceAccessor); } _resourceAccessor = null; }
public IsoResourceProxy(string key, IResourceAccessor isoFileResourceAccessor) { _key = key; _isoFileResourceAccessor = isoFileResourceAccessor; _underlayingStream = _isoFileResourceAccessor.OpenRead(); try { _diskFileSystem = GetFileSystem(_underlayingStream); } catch { _underlayingStream.Dispose(); throw; } }
public static bool ConnectFile(string nativeSystemId, ResourcePath nativeResourcePath, out IResourceAccessor result) { IRemoteResourceInformationService rris = ServiceRegistration.Get<IRemoteResourceInformationService>(); result = null; bool isFileSystemResource; bool isFile; string resourcePathName; string resourceName; DateTime lastChanged; long size; if (!rris.GetResourceInformation(nativeSystemId, nativeResourcePath, out isFileSystemResource, out isFile, out resourcePathName, out resourceName, out lastChanged, out size) || !isFile) return false; result = new RemoteFileResourceAccessor(nativeSystemId, nativeResourcePath, resourcePathName, resourceName, lastChanged, size); return true; }
/// <summary> /// Creates a new <see cref="LocalFsResourceAccessor"/> instance. The given <paramref name="mediaItemAccessor"/> will be either directly used or /// given over to the <see cref="StreamedResourceToLocalFsAccessBridge"/>. The caller must call <see cref="Dispose"/> on the created <see cref="LocalFsResourceAccessorHelper"/> /// instance but must not dispose the given <paramref name="mediaItemAccessor"/>. /// </summary> /// <param name="mediaItemAccessor">IResourceAccessor.</param> public LocalFsResourceAccessorHelper(IResourceAccessor mediaItemAccessor) { _localFsra = mediaItemAccessor as ILocalFsResourceAccessor; _disposeLfsra = null; if (_localFsra != null) return; IFileSystemResourceAccessor localFsra = (IFileSystemResourceAccessor) mediaItemAccessor.Clone(); try { _localFsra = StreamedResourceToLocalFsAccessBridge.GetLocalFsResourceAccessor(localFsra); _disposeLfsra = _localFsra; } catch (Exception) { localFsra.Dispose(); throw; } }
public void Dispose() { foreach (string tempFilePath in _tempFilePaths.Values) try { File.Delete(tempFilePath); } catch (Exception e) { ServiceRegistration.Get<ILogger>().Warn("ZipResourceProxy: Unable to delete temp file '{0}'", e, tempFilePath); } _tempFilePaths.Clear(); CloseZipFile(); if (_zipFileResourceAccessor != null) { _zipFileResourceAccessor.Dispose(); _zipFileResourceAccessor= null; } }
public static void AddStreamSourceFilter(string sourceFilterName, IResourceAccessor resourceAccessor, IGraphBuilder graphBuilder) { IBaseFilter sourceFilter = null; try { if (sourceFilterName == Utils.FilterName) { var filterPath = FileUtils.BuildAssemblyRelativePath(@"MPUrlSourceSplitter\MPUrlSourceSplitter.ax"); sourceFilter = FilterLoader.LoadFilterFromDll(filterPath, new Guid(Utils.FilterCLSID)); if (sourceFilter != null) { graphBuilder.AddFilter(sourceFilter, Utils.FilterName); } } else { sourceFilter = FilterGraphTools.AddFilterByName(graphBuilder, FilterCategory.LegacyAmFilterCategory, sourceFilterName); } if (sourceFilter == null) throw new UPnPRendererExceptions(string.Format("Could not create instance of source filter: '{0}'", sourceFilterName)); string url = resourceAccessor.ResourcePathName; var filterStateEx = sourceFilter as OnlineVideos.MPUrlSourceFilter.IFilterStateEx; if (filterStateEx != null) LoadAndWaitForMPUrlSourceFilter(url, filterStateEx); else { var fileSourceFilter = sourceFilter as IFileSourceFilter; if (fileSourceFilter != null) Marshal.ThrowExceptionForHR(fileSourceFilter.Load(resourceAccessor.ResourcePathName, null)); else throw new UPnPRendererExceptions(string.Format("'{0}' does not implement IFileSourceFilter", sourceFilterName)); } FilterGraphTools.RenderOutputPins(graphBuilder, sourceFilter); } finally { FilterGraphTools.TryRelease(ref sourceFilter); } }
/// <summary> /// Creates an <see cref="IInputSource"/> object for a given mediaitem. /// </summary> /// <param name="resourceLocator">Locator instance to the media item to create the input source for.</param> /// <param name="mimeType">Mime type of the media item, if present. May be <c>null</c>.</param> /// <returns>Input source object for the given <paramref name="resourceLocator"/> or <c>null</c>, if no input source /// could be created.</returns> public IInputSource CreateInputSource(IResourceLocator resourceLocator, string mimeType) { if (!CanPlay(resourceLocator, mimeType)) return null; IInputSource result; _accessor = resourceLocator.CreateAccessor(); AudioCDResourceAccessor acdra = _accessor as AudioCDResourceAccessor; if (acdra != null) result = BassCDTrackInputSource.Create(acdra.Drive, acdra.TrackNo); else { string filePath = _accessor.ResourcePathName; // Network streams INetworkResourceAccessor netra = _accessor as INetworkResourceAccessor; if (netra != null) { result = BassWebStreamInputSource.Create(netra.URL); } // CDDA else if (URLUtils.IsCDDA(filePath)) { ILocalFsResourceAccessor lfra = _accessor as ILocalFsResourceAccessor; if (lfra == null) return null; using (lfra.EnsureLocalFileSystemAccess()) result = BassFsCDTrackInputSource.Create(lfra.LocalFileSystemPath); } else { // Filesystem resources IFileSystemResourceAccessor fsra = _accessor as IFileSystemResourceAccessor; if (fsra == null) return null; if (URLUtils.IsMODFile(filePath)) result = BassMODFileInputSource.Create(fsra); else result = BassAudioFileInputSource.Create(fsra); } } Log.Debug("InputSourceFactory: Creating input source for media resource '{0}' of type '{1}'", _accessor, result.GetType()); return result; }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !mediaItemAccessor.IsFile) { return(false); } string filePath = mediaItemAccessor.CanonicalLocalResourcePath.ToString(); string lowerExtension = StringUtils.TrimToEmpty(ProviderPathHelper.GetExtension(filePath)).ToLowerInvariant(); if (lowerExtension != ".ts") { return(false); } string metaFilePath = ProviderPathHelper.ChangeExtension(filePath, ".xml"); IResourceAccessor metaFileAccessor; if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out metaFileAccessor)) { return(false); } Tags tags; using (metaFileAccessor) { using (Stream metaStream = metaFileAccessor.OpenRead()) tags = (Tags)GetTagsXmlSerializer().Deserialize(metaStream); } MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); MediaItemAspect videoAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata); MediaItemAspect recordingAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, RecordingAspect.Metadata); // Handle series information SeriesInfo seriesInfo = GetSeriesFromTags(tags); if (seriesInfo.IsCompleteMatch) { if (!forceQuickMode) { SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo); } seriesInfo.SetMetadata(extractedAspectData); } string value; if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value)) { mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, value); } if (TryGet(tags, TAG_GENRE, out value)) { videoAspect.SetCollectionAttribute(VideoAspect.ATTR_GENRES, new List <String> { value }); } if (TryGet(tags, TAG_PLOT, out value)) { videoAspect.SetAttribute(VideoAspect.ATTR_STORYPLOT, value); Match yearMatch = _yearMatcher.Match(value); int guessedYear; if (int.TryParse(yearMatch.Value, out guessedYear)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1)); } } if (TryGet(tags, TAG_CHANNEL, out value)) { recordingAspect.SetAttribute(RecordingAspect.ATTR_CHANNEL, value); } // Recording date formatted: 2011-11-04 20:55 DateTime recordingStart; DateTime recordingEnd; if (TryGet(tags, TAG_STARTTIME, out value) && DateTime.TryParse(value, out recordingStart)) { recordingAspect.SetAttribute(RecordingAspect.ATTR_STARTTIME, recordingStart); } if (TryGet(tags, TAG_ENDTIME, out value) && DateTime.TryParse(value, out recordingEnd)) { recordingAspect.SetAttribute(RecordingAspect.ATTR_ENDTIME, recordingEnd); } 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("Tve3RecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { if (forceQuickMode) return false; if (!(mediaItemAccessor is IFileSystemResourceAccessor)) return false; using (IFileSystemResourceAccessor fsra = (IFileSystemResourceAccessor) mediaItemAccessor.Clone()) using (ILocalFsResourceAccessor lfsra = StreamedResourceToLocalFsAccessBridge.GetLocalFsResourceAccessor(fsra)) { string localFsPath = lfsra.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; }
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)) 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 TryExtractStubItems(IResourceAccessor mediaItemAccessor, ICollection <IDictionary <Guid, IList <MediaItemAspect> > > extractedStubAspectData) { return(false); }
private async Task <bool> TryExtractStubItemsAsync(IResourceAccessor mediaItemAccessor, ICollection <IDictionary <Guid, IList <MediaItemAspect> > > extractedStubAspectData) { // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier. // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry. var miNumber = Interlocked.Increment(ref _lastMediaItemNumber); try { _debugLogger.Info("[#{0}]: Start extracting stubs for resource '{1}'", miNumber, mediaItemAccessor); if (!IsStubResource(mediaItemAccessor)) { _debugLogger.Info("[#{0}]: Cannot extract stubs; file does not have a supported extension", miNumber); return(false); } // This MetadataExtractor only works for MediaItems accessible by an IFileSystemResourceAccessor. // Otherwise it is not possible to find a stub-file in the MediaItem's directory. if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { _debugLogger.Info("[#{0}]: Cannot extract stubs; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber); return(false); } var fsra = mediaItemAccessor as IFileSystemResourceAccessor; var seriesStubReader = new StubSeriesReader(_debugLogger, miNumber, true, _settings); if (fsra != null && await seriesStubReader.TryReadMetadataAsync(fsra).ConfigureAwait(false)) { SeriesStub series = seriesStubReader.GetSeriesStubs().FirstOrDefault(); if (series != null && series.Episodes != null && series.Episodes.Count > 0) { Dictionary <Guid, IList <MediaItemAspect> > extractedAspectData = new Dictionary <Guid, IList <MediaItemAspect> >(); string title = string.Format("{0} S{1:00}{2}", series.Title, series.Season.Value, string.Join("", series.Episodes.Select(e => "E" + e.ToString("00")))); MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_STUB); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, fsra.CanonicalLocalResourcePath.Serialize()); if (IsVhs(mediaItemAccessor)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/unknown"); MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_SD); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(4.0 / 3.0)); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 720); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 576); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1); MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 2); } else if (IsTv(mediaItemAccessor)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/unknown"); MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0)); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25F); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1); MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 2); } else if (IsDvd(mediaItemAccessor)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/mp2t"); MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_SD); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0)); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25F); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 720); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 576); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "MPEG-2 Video"); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1); MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3"); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L); } else if (IsBluray(mediaItemAccessor)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/mp4"); MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0)); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 24F); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "AVC"); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1); MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3"); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L); } else if (IsHdDvd(mediaItemAccessor)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/wvc1"); MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0)); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 24F); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "VC1"); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1); videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1); MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3"); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6); audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L); } SingleMediaItemAspect videoAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata); videoAspect.SetAttribute(VideoAspect.ATTR_ISDVD, true); SingleMediaItemAspect movieAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, EpisodeAspect.Metadata); movieAspect.SetCollectionAttribute(EpisodeAspect.ATTR_EPISODE, series.Episodes); movieAspect.SetAttribute(EpisodeAspect.ATTR_SEASON, series.Season.Value); movieAspect.SetAttribute(EpisodeAspect.ATTR_EPISODE_NAME, string.Format("{0} {1}", "Episode", string.Join(", ", series.Episodes))); movieAspect.SetAttribute(EpisodeAspect.ATTR_SERIES_NAME, series.Title); movieAspect.SetAttribute(EpisodeAspect.ATTR_SERIES_SEASON, string.Format("{0} S{1:00}", series.Title, series.Season.Value)); SingleMediaItemAspect stubAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, StubAspect.Metadata); stubAspect.SetAttribute(StubAspect.ATTR_DISC_NAME, series.DiscName); stubAspect.SetAttribute(StubAspect.ATTR_MESSAGE, series.Message); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, series.Title); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(series.Title)); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISSTUB, true); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged); extractedStubAspectData.Add(extractedAspectData); } } else { _debugLogger.Warn("[#{0}]: No valid metadata found in movie stub file", miNumber); } _debugLogger.Info("[#{0}]: Successfully finished extracting stubs", miNumber); return(true); } catch (Exception e) { ServiceRegistration.Get <ILogger>().Warn("StubMovieMetadataExtractor: Exception while extracting stubs for resource '{0}'; enable debug logging for more details.", mediaItemAccessor); _debugLogger.Error("[#{0}]: Exception while extracting stubs", e, miNumber); return(false); } }
public Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { return(Task.FromResult(false)); }
/// <summary> /// Asynchronously tries to extract metadata for the given <param name="mediaItemAccessor"></param> /// </summary> /// <param name="mediaItemAccessor">Points to the resource for which we try to extract metadata</param> /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s with the extracted metadata</param> /// <param name="forceQuickMode">If <c>true</c>, nothing is downloaded from the internet</param> /// <returns><c>true</c> if metadata was found and stored into <param name="extractedAspectData"></param>, else <c>false</c></returns> private async Task <bool> TryExtractAudioMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier. // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry. var miNumber = Interlocked.Increment(ref _lastMediaItemNumber); bool isStub = extractedAspectData.ContainsKey(StubAspect.ASPECT_ID); if (!isStub) { _debugLogger.Info("[#{0}]: Ignoring non-stub track", miNumber); return(false); } try { _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}' (forceQuickMode: {2})", miNumber, mediaItemAccessor, forceQuickMode); // We only extract metadata with this MetadataExtractor, if another MetadataExtractor that was applied before // has identified this MediaItem as a video and therefore added a VideoAspect. if (!extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not audio", miNumber); return(false); } // This MetadataExtractor only works for MediaItems accessible by an IFileSystemResourceAccessor. // Otherwise it is not possible to find a nfo-file in the MediaItem's directory. if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { _debugLogger.Info("[#{0}]: Cannot extract metadata; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber); return(false); } // First we try to find an IFileSystemResourceAccessor pointing to the album nfo-file. IFileSystemResourceAccessor albumNfoFsra; if (TryGetAlbumNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out albumNfoFsra)) { // If we found one, we (asynchronously) extract the metadata into a stub object and, if metadata was found, // we store it into the MediaItemAspects. var albumNfoReader = new NfoAlbumReader(_debugLogger, miNumber, forceQuickMode, isStub, _httpClient, _settings); using (albumNfoFsra) { if (await albumNfoReader.TryReadMetadataAsync(albumNfoFsra).ConfigureAwait(false)) { //Check reimport if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID)) { AlbumInfo reimport = new AlbumInfo(); reimport.FromMetadata(extractedAspectData); if (!VerifyAlbumReimport(albumNfoReader, reimport)) { ServiceRegistration.Get <ILogger>().Info("NfoMovieMetadataExtractor: Nfo album metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport); return(false); } } Stubs.AlbumStub album = albumNfoReader.GetAlbumStubs().FirstOrDefault(); if (album != null) { int trackNo = 0; if (album.Tracks != null && album.Tracks.Count > 0 && MediaItemAspect.TryGetAttribute(extractedAspectData, AudioAspect.ATTR_TRACK, out trackNo)) { var track = album.Tracks.FirstOrDefault(t => t.TrackNumber.HasValue && trackNo == t.TrackNumber.Value); if (track != null) { TrackInfo trackInfo = new TrackInfo(); string title; string sortTitle; title = track.Title.Trim(); sortTitle = BaseInfo.GetSortTitle(title); IEnumerable <string> artists; if (track.Artists.Count > 0) { artists = track.Artists; } IList <MultipleMediaItemAspect> providerResourceAspects; if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspects)) { MultipleMediaItemAspect providerResourceAspect = providerResourceAspects.First(pa => pa.GetAttributeValue <int>(ProviderResourceAspect.ATTR_TYPE) == ProviderResourceAspect.TYPE_STUB); string mime = null; if (track.FileInfo != null && track.FileInfo.Count > 0) { mime = MimeTypeDetector.GetMimeTypeFromExtension("file" + track.FileInfo.First().Container); } if (mime != null) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mime); } } trackInfo.TrackName = title; trackInfo.TrackNameSort = sortTitle; trackInfo.Duration = track.Duration.HasValue ? Convert.ToInt64(track.Duration.Value.TotalSeconds) : 0; trackInfo.Album = !string.IsNullOrEmpty(album.Title) ? album.Title.Trim() : null; trackInfo.TrackNum = track.TrackNumber.HasValue ? track.TrackNumber.Value : 0; trackInfo.TotalTracks = album.Tracks.Count; trackInfo.MusicBrainzId = track.MusicBrainzId; trackInfo.IsrcId = track.Isrc; trackInfo.AudioDbId = track.AudioDbId.HasValue ? track.AudioDbId.Value : 0; trackInfo.AlbumMusicBrainzId = album.MusicBrainzAlbumId; trackInfo.AlbumMusicBrainzGroupId = album.MusicBrainzReleaseGroupId; trackInfo.ReleaseDate = album.ReleaseDate; if (track.FileInfo != null && track.FileInfo.Count > 0 && track.FileInfo.First().AudioStreams != null && track.FileInfo.First().AudioStreams.Count > 0) { var audio = track.FileInfo.First().AudioStreams.First(); trackInfo.Encoding = audio.Codec; trackInfo.BitRate = audio.Bitrate != null?Convert.ToInt32(audio.Bitrate / 1000) : 0; trackInfo.Channels = audio.Channels != null ? audio.Channels.Value : 0; } trackInfo.Artists = new List <PersonInfo>(); if (track.Artists != null && track.Artists.Count > 0) { foreach (string artistName in track.Artists) { trackInfo.Artists.Add(new PersonInfo() { Name = artistName.Trim(), Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } } trackInfo.AlbumArtists = new List <PersonInfo>(); if (album.Artists != null && album.Artists.Count > 0) { foreach (string artistName in album.Artists) { trackInfo.AlbumArtists.Add(new PersonInfo() { Name = artistName.Trim(), Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } } if (album.Genres != null && album.Genres.Count > 0) { trackInfo.Genres = album.Genres.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo { Name = s.Trim() }).ToList(); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in trackInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId)) { genre.Id = genreId; } } } if (album.Thumb != null && album.Thumb.Length > 0) { try { using (MemoryStream stream = new MemoryStream(album.Thumb)) { trackInfo.Thumbnail = stream.ToArray(); trackInfo.HasChanged = true; } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } //Determine compilation if (trackInfo.AlbumArtists.Count > 0 && (trackInfo.AlbumArtists[0].Name.IndexOf("Various", StringComparison.InvariantCultureIgnoreCase) >= 0 || trackInfo.AlbumArtists[0].Name.Equals("VA", StringComparison.InvariantCultureIgnoreCase))) { trackInfo.Compilation = true; } else { //Look for itunes compilation folder var mediaItemPath = mediaItemAccessor.CanonicalLocalResourcePath; var artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../"); if (artistMediaItemDirectoryPath.FileName.IndexOf("Compilation", StringComparison.InvariantCultureIgnoreCase) >= 0) { trackInfo.Compilation = true; } } trackInfo.SetMetadata(extractedAspectData); } } } } else { _debugLogger.Warn("[#{0}]: No valid metadata found in album nfo-file", miNumber); } } } _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber); ServiceRegistration.Get <ILogger>().Debug("NfoAudioMetadataExtractor: Assigned nfo audio metadata for resource '{0}'", mediaItemAccessor); return(true); } catch (Exception e) { ServiceRegistration.Get <ILogger>().Warn("NfoAudioMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor); _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber); return(false); } }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { if (!mediaItemAccessor.IsFile) { return(false); } string fileName = mediaItemAccessor.ResourceName; if (!HasAudioExtension(fileName)) { return(false); } MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); MediaItemAspect audioAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, AudioAspect.Metadata); MediaItemAspect thumbnailSmallAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, ThumbnailSmallAspect.Metadata); MediaItemAspect thumbnailLargeAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, ThumbnailLargeAspect.Metadata); try { File tag; try { ByteVector.UseBrokenLatin1Behavior = true; // Otherwise we have problems retrieving non-latin1 chars tag = File.Create(new ResourceProviderFileAbstraction(mediaItemAccessor)); } catch (CorruptFileException) { // 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("AudioMetadataExtractor: Audio file '{0}' seems to be broken", mediaItemAccessor.CanonicalLocalResourcePath); return(false); } // Some file extensions like .mp4 can contain audio and video. Do not handle files with video content here. if (tag.Properties.VideoHeight > 0 && tag.Properties.VideoWidth > 0) { return(false); } fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty; string title; string artist; uint? trackNo; GuessMetadataFromFileName(fileName, out title, out artist, out trackNo); if (!string.IsNullOrEmpty(tag.Tag.Title)) { title = tag.Tag.Title; } IEnumerable <string> artists; if (tag.Tag.Performers.Length > 0) { artists = tag.Tag.Performers; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { artists = PatchID3v23Enumeration(artists); } } else { artists = artist == null ? null : new string[] { artist } }; if (tag.Tag.Track != 0) { trackNo = tag.Tag.Track; } mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, title); // FIXME Albert: tag.MimeType returns taglib/mp3 for an MP3 file. This is not what we want and collides with the // mimetype handling in the BASS player, which expects audio/xxx. //mediaAspect.SetAttribute(MediaAspect.ATTR_MIME_TYPE, tag.MimeType); audioAspect.SetCollectionAttribute(AudioAspect.ATTR_ARTISTS, ApplyAdditionalSeparator(artists)); audioAspect.SetAttribute(AudioAspect.ATTR_ALBUM, StringUtils.TrimToNull(tag.Tag.Album)); IEnumerable <string> albumArtists = tag.Tag.AlbumArtists; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { albumArtists = PatchID3v23Enumeration(albumArtists); } audioAspect.SetCollectionAttribute(AudioAspect.ATTR_ALBUMARTISTS, ApplyAdditionalSeparator(albumArtists)); audioAspect.SetAttribute(AudioAspect.ATTR_BITRATE, tag.Properties.AudioBitrate); mediaAspect.SetAttribute(MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tag.Tag.Comment)); IEnumerable <string> composers = tag.Tag.Composers; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { composers = PatchID3v23Enumeration(composers); } audioAspect.SetCollectionAttribute(AudioAspect.ATTR_COMPOSERS, ApplyAdditionalSeparator(composers)); audioAspect.SetAttribute(AudioAspect.ATTR_DURATION, tag.Properties.Duration.TotalSeconds); if (tag.Tag.Genres.Length > 0) { IEnumerable <string> genres = tag.Tag.Genres; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { genres = PatchID3v23Enumeration(genres); } audioAspect.SetCollectionAttribute(AudioAspect.ATTR_GENRES, ApplyAdditionalSeparator(genres)); } if (trackNo.HasValue) { audioAspect.SetAttribute(AudioAspect.ATTR_TRACK, (int)trackNo.Value); } if (tag.Tag.TrackCount != 0) { audioAspect.SetAttribute(AudioAspect.ATTR_NUMTRACKS, (int)tag.Tag.TrackCount); } int year = (int)tag.Tag.Year; if (year >= 30 && year <= 99) { year += 1900; } if (year >= 1930 && year <= 2030) { mediaAspect.SetAttribute(MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1)); } // The following code gets cover art images from file (embedded) or from windows explorer cache (supports folder.jpg). IPicture[] pics = tag.Tag.Pictures; if (pics.Length > 0) { thumbnailLargeAspect.SetAttribute(ThumbnailLargeAspect.ATTR_THUMBNAIL, pics[0].Data.Data); } else { // In quick mode only allow thumbs taken from cache. bool cachedOnly = forceQuickMode; // Thumbnail extraction fileName = mediaItemAccessor.ResourcePathName; IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>(); byte[] thumbData; ImageType imageType; if (generator.GetThumbnail(fileName, 96, 96, cachedOnly, out thumbData, out imageType)) { thumbnailSmallAspect.SetAttribute(ThumbnailSmallAspect.ATTR_THUMBNAIL, thumbData); } if (generator.GetThumbnail(fileName, 256, 256, cachedOnly, out thumbData, out imageType)) { thumbnailLargeAspect.SetAttribute(ThumbnailLargeAspect.ATTR_THUMBNAIL, thumbData); } } return(true); } catch (UnsupportedFormatException) { ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Unsupported audio file '{0}'", mediaItemAccessor.CanonicalLocalResourcePath); return(false); } 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("AudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(false); }
/// <summary> /// Asynchronously creates an <see cref="NfoSeriesEpisodeReader"/> for the given <param name="mediaItemAccessor"></param> /// </summary> /// <param name="mediaItemAccessor">Points to the resource for which we try to create an NfoSeriesEpisodeReader</param> /// <param name="season">Season number of the episode to create an NfoSeriesEpisodeReader for</param> /// <param name="episode">Episode number of the episode to create an NfoSeriesEpisodeReader for</param> /// <returns>An NfoSeriesEpisodeReader if an nfo file was found, else <c>null</c></returns> protected async Task <NfoSeriesEpisodeReader> TryGetNfoSeriesEpisodeReaderAsync(IResourceAccessor mediaItemAccessor, int?season, int?episode, bool includeFanart) { // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier. // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry. var miNumber = Interlocked.Increment(ref _lastMediaItemNumber); try { _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}'", miNumber, mediaItemAccessor); // This MetadataExtractor only works for MediaItems accessible by an IFileSystemResourceAccessor. // Otherwise it is not possible to find a nfo-file in the MediaItem's directory or parent directory. if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { _debugLogger.Info("[#{0}]: Cannot extract metadata; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber); return(null); } // Here we try to find an IFileSystemResourceAccessor pointing to the episode nfo-file. // If we don't find one, we cannot extract any metadata. IFileSystemResourceAccessor episodeNfoFsra; NfoSeriesEpisodeReader episodeNfoReader = null; bool episodeDetailsFound = false; if (TryGetEpisodeNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out episodeNfoFsra)) { episodeDetailsFound = true; // Now we (asynchronously) extract the metadata into a stub object. // If no metadata was found, nothing can be stored in the MediaItemAspects. episodeNfoReader = new NfoSeriesEpisodeReader(_debugLogger, miNumber, false, false, _httpClient, _settings, includeFanart); using (episodeNfoFsra) { if (!await episodeNfoReader.TryReadMetadataAsync(episodeNfoFsra).ConfigureAwait(false)) { _debugLogger.Warn("[#{0}]: No valid metadata found in episode nfo-file", miNumber); return(null); } } } // Then we try to find an IFileSystemResourceAccessor pointing to the series nfo-file. IFileSystemResourceAccessor seriesNfoFsra; if (TryGetSeriesNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out seriesNfoFsra)) { // If we found one, we (asynchronously) extract the metadata into a stub object and, if metadata was found, // we store it into the episodeNfoReader so that the latter can store metadata from series and episode level into the MediaItemAspects. var seriesNfoReader = new NfoSeriesReader(_debugLogger, miNumber, false, !episodeDetailsFound, false, _httpClient, _settings, includeFanart); using (seriesNfoFsra) { if (await seriesNfoReader.TryReadMetadataAsync(seriesNfoFsra).ConfigureAwait(false)) { Stubs.SeriesStub series = seriesNfoReader.GetSeriesStubs().FirstOrDefault(); if (series != null) { if (!episodeDetailsFound && series.Episodes != null && season.HasValue && episode.HasValue) { Stubs.SeriesEpisodeStub episodeStub = series.Episodes.FirstOrDefault(e => e.Season == season && e.Episodes != null && e.Episodes.Contains(episode.Value)); episodeNfoReader = new NfoSeriesEpisodeReader(_debugLogger, miNumber, false, false, _httpClient, _settings, includeFanart); episodeNfoReader.SetEpisodeStubs(new List <Stubs.SeriesEpisodeStub>(new[] { episodeStub })); } if (episodeNfoReader != null) { episodeNfoReader.SetSeriesStubs(new List <Stubs.SeriesStub> { series }); } } else { _debugLogger.Warn("[#{0}]: No valid metadata found in series nfo-file", miNumber); } } } } if (episodeNfoReader != null) { return(episodeNfoReader); } _debugLogger.Warn("[#{0}]: No valid nfo-file found", miNumber); } catch (Exception e) { ServiceRegistration.Get <ILogger>().Warn("NfoSeriesMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor); _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber); } return(null); }
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); }
/// <summary> /// Sets the data of the new image to be played. /// </summary> /// <param name="locator">Resource locator of the image item.</param> /// <param name="mediaItemTitle">Title of the image item.</param> /// <param name="rotation">Rotation of the image.</param> /// <param name="flipX">Flipping in horizontal direction.</param> /// <param name="flipY">Flipping in vertical direction.</param> public void SetMediaItemData(IResourceLocator locator, string mediaItemTitle, RightAngledRotation rotation, bool flipX, bool flipY) { if (locator == null) { lock (_syncObj) { _currentLocator = null; return; } } using (IResourceAccessor ra = locator.CreateAccessor()) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra == null) { return; } using (Stream stream = fsra.OpenRead()) { string key = fsra.CanonicalLocalResourcePath.Serialize(); _texture = ContentManager.Instance.GetTexture(stream, key, true); if (_texture == null) { return; } if (!_texture.IsAllocated) { _texture.Allocate(); } if (!_texture.IsAllocated) { return; } } } lock (_syncObj) { ReloadSettings(); _state = PlayerState.Active; _currentLocator = locator; _mediaItemTitle = mediaItemTitle; _rotation = rotation; _flipX = flipX; _flipY = flipY; SurfaceDescription desc = _texture.Texture.GetLevelDescription(0); _textureMaxUV = new SizeF(_texture.Width / (float)desc.Width, _texture.Height / (float)desc.Height); // Reset animation _animator.Initialize(); if (_slideShowTimer != null) { _slideShowTimer.Change(_slideShowImageDuration, TS_INFINITE); } else { CheckTimer(); } _playbackStartTime = DateTime.Now; if (_pauseTime.HasValue) { _pauseTime = _playbackStartTime; } } }
public Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { return(Task.FromResult(false)); } if (extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID)) { return(Task.FromResult(false)); //Ignore video recordings } if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { return(Task.FromResult(false)); } try { var extension = DosPathHelper.GetExtension(fsra.ResourceName).ToLowerInvariant(); if (extension != ".ts") { return(Task.FromResult(false)); } using (MediaInfoWrapper mediaInfo = ReadMediaInfo(fsra)) { // Before we start evaluating the file, check if it is not a video file if (mediaInfo.IsValid && (mediaInfo.GetVideoCount() != 0 || mediaInfo.GetAudioCount() == 0)) { return(Task.FromResult(false)); } string fileName = ProviderPathHelper.GetFileNameWithoutExtension(fsra.Path) ?? string.Empty; MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, fileName); MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/radio"); var audioBitrate = mediaInfo.GetAudioBitrate(0); if (audioBitrate.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, (int)(audioBitrate.Value / 1000)); // We store kbit/s; } var audioChannels = mediaInfo.GetAudioChannels(0); if (audioChannels.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_CHANNELS, audioChannels.Value); } var audioSampleRate = mediaInfo.GetAudioSampleRate(0); if (audioSampleRate.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_SAMPLERATE, audioSampleRate.Value); } MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_ENCODING, mediaInfo.GetAudioCodec(0)); // MediaInfo returns milliseconds, we need seconds long?time = mediaInfo.GetPlaytime(0); if (time.HasValue && time > 1000) { MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, time.Value / 1000); } } return(Task.FromResult(true)); } 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("RadioRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message); return(Task.FromResult(false)); } }
internal static void ParseH264Info(IResourceAccessor res, MetadataContainer info, Dictionary <float, long> h264MaxDpbMbs, int transcoderTimeout) { if (info.Video[Editions.DEFAULT_EDITION].Codec == VideoCodec.H264) { if (res is ILocalFsResourceAccessor fileRes && !fileRes.IsFile) { return; } //if (!(res is INetworkResourceAccessor)) // return; //TODO: Remove this debug code when error found string debug = ""; string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".bin"; try { byte[] data = null; //string arguments = string.Format("-i \"{0}\" -frames:v 1 -c:v copy -f h264", info.Metadata.Source); //if (info.Metadata.VideoContainerType != VideoContainer.Mpeg2Ts) //{ // arguments += " -bsf:v h264_mp4toannexb"; //} //arguments += string.Format(" -an \"{0}\"", tempFileName); //debug = arguments; //ProcessExecutionResult result; //lock (FFPROBE_THROTTLE_LOCK) // result = FFMpegBinary.FFMpegExecuteWithResourceAccessAsync((ILocalFsResourceAccessor)info.Metadata.Source, arguments, ProcessPriorityClass.Idle, transcoderTimeout).Result; //debug = result.StandardError; //if (!result.Success || !File.Exists(tempFileName)) //{ // ServiceRegistration.Get<ILogger>().Warn("MediaAnalyzer: Failed to extract h264 annex b header information for resource: '{0}'", info.Metadata.Source); // return; //} //data = File.ReadAllBytes(tempFileName); string arguments = string.Format("-i \"{0}\" -frames:v 1 -c:v copy -f h264", res); if (info.Metadata[Editions.DEFAULT_EDITION].VideoContainerType != VideoContainer.Mpeg2Ts) { arguments += " -bsf:v h264_mp4toannexb"; } arguments += " -an -"; data = ProbeResource(res, arguments, transcoderTimeout); if (data == null) { Logger.Error("MediaAnalyzer: Timed out analyzing H264 information for resource '{0}'", res); return; } debug = "Parse binary dump"; H264Analyzer avcAnalyzer = new H264Analyzer(); if (avcAnalyzer.Parse(data) == true) { switch (avcAnalyzer.HeaderProfile) { case H264Analyzer.H264HeaderProfile.ConstrainedBaseline: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.Baseline; break; case H264Analyzer.H264HeaderProfile.Baseline: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.Baseline; break; case H264Analyzer.H264HeaderProfile.Main: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.Main; break; case H264Analyzer.H264HeaderProfile.Extended: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.Main; break; case H264Analyzer.H264HeaderProfile.High: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.High; break; case H264Analyzer.H264HeaderProfile.High_10: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.High10; break; case H264Analyzer.H264HeaderProfile.High_422: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.High422; break; case H264Analyzer.H264HeaderProfile.High_444: info.Video[Editions.DEFAULT_EDITION].ProfileType = EncodingProfile.High444; break; } info.Video[Editions.DEFAULT_EDITION].HeaderLevel = avcAnalyzer.HeaderLevel; int refFrames = avcAnalyzer.HeaderRefFrames; debug = "File parsed"; if (info.Video[Editions.DEFAULT_EDITION].Width > 0 && info.Video[Editions.DEFAULT_EDITION].Height > 0 && refFrames > 0 && refFrames <= 16) { long dpbMbs = Convert.ToInt64(((float)info.Video[Editions.DEFAULT_EDITION].Width * (float)info.Video[Editions.DEFAULT_EDITION].Height * (float)refFrames) / 256F); foreach (KeyValuePair <float, long> levelDbp in h264MaxDpbMbs) { if (levelDbp.Value > dpbMbs) { info.Video[Editions.DEFAULT_EDITION].RefLevel = levelDbp.Key; break; } } } if (info.Video[Editions.DEFAULT_EDITION].HeaderLevel == 0 && info.Video[Editions.DEFAULT_EDITION].RefLevel == 0) { Logger.Warn("MediaAnalyzer: Couldn't resolve H264 profile/level/reference frames for resource: '{0}'", res); } } Logger.Debug("MediaAnalyzer: Successfully decoded H264 header: H264 profile {0}, level {1}/level {2}", info.Video[Editions.DEFAULT_EDITION].ProfileType, info.Video[Editions.DEFAULT_EDITION].HeaderLevel, info.Video[Editions.DEFAULT_EDITION].RefLevel); } catch (Exception e) { if (Logger != null) { Logger.Error("MediaAnalyzer: Failed to analyze H264 information for resource '{0}':\n{1}", res, e.Message); Logger.Error("MediaAnalyzer: Debug info: {0}", debug); } } try { if (File.Exists(tempFileName)) { File.Delete(tempFileName); } } catch { } } }
internal IsoResourceProxy CreateIsoResourceProxy(string key, IResourceAccessor isoFileResourceAccessor) { IsoResourceProxy result = new IsoResourceProxy(key, isoFileResourceAccessor); result.Orphaned += OnIsoResourceProxyOrphaned; return result; }
public override bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { // If the base AudioMDE already extracted metadata, don't try here again to avoid conflicts. if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { return(false); } ILocalFsResourceAccessor fsra = mediaItemAccessor as ILocalFsResourceAccessor; if (fsra == null) { return(false); } if (!fsra.IsFile) { return(false); } string fileName = fsra.ResourceName; if (!HasAudioExtension(fileName)) { return(false); } try { TAG_INFO tags; using (fsra.EnsureLocalFileSystemAccess()) tags = BassTags.BASS_TAG_GetFromFile(fsra.LocalFileSystemPath); if (tags == null) { return(false); } fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty; string title; string artist; uint? trackNo; GuessMetadataFromFileName(fileName, out title, out artist, out trackNo); if (!string.IsNullOrWhiteSpace(tags.title)) { title = tags.title; } IEnumerable <string> artists; if (!string.IsNullOrWhiteSpace(tags.artist)) { artists = SplitTagEnum(tags.artist); artists = PatchID3v23Enumeration(artists); } else { artists = artist == null ? null : new string[] { artist } }; if (!string.IsNullOrWhiteSpace(tags.track) && tags.track != "0") { int iTrackNo; if (int.TryParse(tags.track, out iTrackNo)) { trackNo = (uint?)iTrackNo; } else { trackNo = null; } } MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SIZE, fsra.Size); // Calling EnsureLocalFileSystemAccess not necessary; only string operation MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_MIME_TYPE, "audio/" + Path.GetExtension(fsra.LocalFileSystemPath).Substring(1)); MediaItemAspect.SetCollectionAttribute(extractedAspectData, AudioAspect.ATTR_ARTISTS, ApplyAdditionalSeparator(artists)); MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_ALBUM, StringUtils.TrimToNull(tags.album)); IEnumerable <string> albumArtists = SplitTagEnum(tags.albumartist); albumArtists = PatchID3v23Enumeration(albumArtists); MediaItemAspect.SetCollectionAttribute(extractedAspectData, AudioAspect.ATTR_ALBUMARTISTS, ApplyAdditionalSeparator(albumArtists)); MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, tags.bitrate); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tags.comment)); IEnumerable <string> composers = SplitTagEnum(tags.composer); composers = PatchID3v23Enumeration(composers); MediaItemAspect.SetCollectionAttribute(extractedAspectData, AudioAspect.ATTR_COMPOSERS, ApplyAdditionalSeparator(composers)); MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, (long)tags.duration); IEnumerable <string> genres = SplitTagEnum(tags.genre); genres = PatchID3v23Enumeration(genres); MediaItemAspect.SetCollectionAttribute(extractedAspectData, AudioAspect.ATTR_GENRES, ApplyAdditionalSeparator(genres)); if (trackNo.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_TRACK, (int)trackNo.Value); } int year; if (int.TryParse(tags.year, out year)) { if (year >= 30 && year <= 99) { year += 1900; } if (year >= 1930 && year <= 2030) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1)); } } // The following code gets cover art images from file (embedded) or from windows explorer cache (supports folder.jpg). if (tags.PictureCount > 0) { try { using (Image cover = tags.PictureGetImage(0)) using (Image resized = ImageUtilities.ResizeImage(cover, MAX_COVER_WIDTH, MAX_COVER_HEIGHT)) using (MemoryStream result = new MemoryStream()) { resized.Save(result, ImageFormat.Jpeg); MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, result.ToArray()); } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } else { // In quick mode only allow thumbs taken from cache. bool cachedOnly = forceQuickMode; // Thumbnail extraction fileName = mediaItemAccessor.ResourcePathName; IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>(); byte[] thumbData; ImageType imageType; if (generator.GetThumbnail(fileName, cachedOnly, out thumbData, out imageType)) { MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, thumbData); } } return(true); } 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("BassAudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message); } return(false); }
public ResourceProviderFileAbstraction(IResourceAccessor resourceAccessor) { _resourceAccessor = resourceAccessor; }
/// <summary> /// Asynchronously tries to extract metadata for the given <param name="mediaItemAccessor"></param> /// </summary> /// <param name="mediaItemAccessor">Points to the resource for which we try to extract metadata</param> /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s with the extracted metadata</param> /// <param name="forceQuickMode">If <c>true</c>, nothing is downloaded from the internet</param> /// <returns><c>true</c> if metadata was found and stored into <param name="extractedAspectData"></param>, else <c>false</c></returns> private async Task<bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier. // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry. var miNumber = Interlocked.Increment(ref _lastMediaItemNumber); try { _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}' (forceQuickMode: {2})", miNumber, mediaItemAccessor, forceQuickMode); // We only extract metadata with this MetadataExtractor, if another MetadataExtractor that was applied before // has identified this MediaItem as a video and therefore added a VideoAspect. if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID)) { _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not a video", miNumber); return false; } // This MetadataExtractor only works for MediaItems accessible by an IFileSystemResourceAccessor. // Otherwise it is not possible to find a nfo-file in the MediaItem's directory. if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { _debugLogger.Info("[#{0}]: Cannot extract metadata; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber); return false; } // Here we try to find an IFileSystemResourceAccessor pointing to the nfo-file. // If we don't find one, we cannot extract any metadata. IFileSystemResourceAccessor nfoFsra; if (!TryGetNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out nfoFsra)) return false; // Now we (asynchronously) extract the metadata into a stub object. // If no metadata was found, nothing can be stored in the MediaItemAspects. var nfoReader = new NfoMovieReader(_debugLogger, miNumber, forceQuickMode, _httpClient, _settings); using (nfoFsra) { if (!await nfoReader.TryReadMetadataAsync(nfoFsra).ConfigureAwait(false)) { _debugLogger.Warn("[#{0}]: No valid metadata found", miNumber); return false; } } // Then we store the found metadata in the MediaItemAspects. If we only found metadata that is // not (yet) supported by our MediaItemAspects, this MetadataExtractor returns false. if (!nfoReader.TryWriteMetadata(extractedAspectData)) { _debugLogger.Warn("[#{0}]: No metadata was written into MediaItemsAspects", miNumber); return false; } _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber); return true; } catch (Exception e) { ServiceRegistration.Get<ILogger>().Warn("NfoMovieMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor); _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber); return false; } }
public VirtualFile(string name, IResourceAccessor resourceAccessor) : base(name, resourceAccessor) { }
protected IResourceAccessor WrapLocalFsResourceAccessor(IResourceAccessor localFsResourceAccessor) { return(new NetworkNeighborhoodResourceAccessor(_parent, localFsResourceAccessor.Path.Substring(1))); }
public bool TryExtractStubItems(IResourceAccessor mediaItemAccessor, ICollection <IDictionary <Guid, IList <MediaItemAspect> > > extractedStubAspectData) { // The following is bad practice as it wastes one ThreadPool thread. // ToDo: Once the IMetadataExtractor interface is updated to support async operations, call TryExtractMetadataAsync directly return(TryExtractStubItemsAsync(mediaItemAccessor, extractedStubAspectData).Result); }
public IDictionary<Guid, MediaItemAspect> ExtractMetadata(IResourceAccessor mediaItemAccessor, IEnumerable<IMetadataExtractor> metadataExtractors, bool forceQuickMode) { IDictionary<Guid, MediaItemAspect> result = new Dictionary<Guid, MediaItemAspect>(); bool success = false; // Execute all metadata extractors in order of their priority foreach (IMetadataExtractor extractor in metadataExtractors.OrderBy(m => m.Metadata.Priority)) { try { if (extractor.TryExtractMetadata(mediaItemAccessor, result, forceQuickMode)) success = true; } catch (Exception e) { MetadataExtractorMetadata mem = extractor.Metadata; ServiceRegistration.Get<ILogger>().Error("MediaAccessor: Error extracting metadata from metadata extractor '{0}' (Id: '{1}')", e, mem.Name, mem.MetadataExtractorId); throw; } } return success ? result : null; }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) return false; string title; if (!MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) || string.IsNullOrEmpty(title)) return false; string filePath = mediaItemAccessor.CanonicalLocalResourcePath.ToString(); string lowerExtension = StringUtils.TrimToEmpty(ProviderPathHelper.GetExtension(filePath)).ToLowerInvariant(); if (lowerExtension != ".ts") return false; string metaFilePath = ProviderPathHelper.ChangeExtension(filePath, ".xml"); IResourceAccessor metaFileAccessor; if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out metaFileAccessor)) return false; Tags tags; using (metaFileAccessor) { using (Stream metaStream = ((IFileSystemResourceAccessor) metaFileAccessor).OpenRead()) tags = (Tags) GetTagsXmlSerializer().Deserialize(metaStream); } // Handle series information SeriesInfo seriesInfo = GetSeriesFromTags(tags); if (seriesInfo.IsCompleteMatch) { if (!forceQuickMode) SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo); seriesInfo.SetMetadata(extractedAspectData); } string value; if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value)) MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value); if (TryGet(tags, TAG_GENRE, out value)) MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_GENRES, new List<String> { value }); if (TryGet(tags, TAG_PLOT, out value)) { MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value); Match yearMatch = _yearMatcher.Match(value); int guessedYear; if (int.TryParse(yearMatch.Value, out guessedYear)) MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1)); } if (TryGet(tags, TAG_CHANNEL, out value)) MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value); // Recording date formatted: 2011-11-04 20:55 DateTime recordingStart; DateTime recordingEnd; if (TryGet(tags, TAG_STARTTIME, out value) && DateTime.TryParse(value, out recordingStart)) MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recordingStart); if (TryGet(tags, TAG_ENDTIME, out value) && DateTime.TryParse(value, out recordingEnd)) MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recordingEnd); 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("Tve3RecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return false; }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { char drive; byte trackNo; if (TryExtract(path, out drive, out trackNo)) { result = new AudioCDResourceAccessor(this, drive, trackNo); return true; } result = null; return false; }
protected static bool IsChainedResourceProvider(IResourceAccessor mediaItemAccessor) { Guid providerId = mediaItemAccessor.ParentProvider.Metadata.ResourceProviderId; return(ServiceRegistration.Get <IMediaAccessor>().LocalChainedResourceProviders.Any(rp => rp.Metadata.ResourceProviderId == providerId)); }
public bool TryChainUp(IResourceAccessor potentialBaseResourceAccessor, string path, out IResourceAccessor resultResourceAccessor) { resultResourceAccessor = null; string resourcePathName = potentialBaseResourceAccessor.ResourcePathName; if (string.IsNullOrEmpty(resourcePathName) || !potentialBaseResourceAccessor.IsFile || !".iso".Equals(DosPathHelper.GetExtension(resourcePathName), StringComparison.OrdinalIgnoreCase)) { return(false); } lock (_syncObj) { string key = potentialBaseResourceAccessor.CanonicalLocalResourcePath.Serialize(); try { IsoResourceProxy proxy; if (!_isoUsages.TryGetValue(key, out proxy)) { _isoUsages.Add(key, proxy = CreateIsoResourceProxy(key, potentialBaseResourceAccessor)); } resultResourceAccessor = new IsoResourceAccessor(this, proxy, path); } catch (Exception e) { ServiceRegistration.Get <ILogger>().Warn("IsoResourceProvider: Error chaining up to '{0}'", e, potentialBaseResourceAccessor.CanonicalLocalResourcePath); return(false); } return(true); } }
protected VirtualBaseDirectory(string name, IResourceAccessor resourceAccessor) : base(name, resourceAccessor) { }
/// <summary> /// Extracts relations from all <see cref="IRelationshipRoleExtractor"/>s that support the given aspects. /// </summary> /// <param name="mediaItemId">The id of the media item.</param> /// <param name="aspects">The aspects of the media item.</param> /// <returns></returns> protected async Task <ICollection <ExtractedRelation> > ExtractRelationshipMetadata(IResourceAccessor mediaItemAccessor, Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects) { ICollection <ExtractedRelation> relations = new List <ExtractedRelation>(); foreach (IList <IRelationshipRoleExtractor> extractorList in GetExtractorsByRoleLinkedRole(aspects).Values) { await ExtractRelationshipMetadata(extractorList, mediaItemAccessor, mediaItemId, aspects, relations).ConfigureAwait(false); } return(relations); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { // The following is bad practice as it wastes one ThreadPool thread. // ToDo: Once the IMetadataExtractor interface is updated to support async operations, call TryExtractMetadataAsync directly return TryExtractMetadataAsync(mediaItemAccessor, extractedAspectData, forceQuickMode).Result; }
/// <summary> /// Extracts relations for the specified <paramref name="roleExtractor"/> and adds them to <paramref name="relations"/> collection. /// </summary> /// <param name="roleExtractor">The extractor to use to extract relations.</param> /// <param name="mediaItemId">The id of the media item.</param> /// <param name="aspects">The aspects of the media item.</param> /// <param name="relations">Collection of relations to add any extracted relations.</param> protected async Task ExtractRelationshipMetadata(IList <IRelationshipRoleExtractor> roleExtractors, IResourceAccessor mediaItemAccessor, Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects, ICollection <ExtractedRelation> relations) { IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedItems = new List <IDictionary <Guid, IList <MediaItemAspect> > >(); foreach (IRelationshipRoleExtractor roleExtractor in roleExtractors) { await roleExtractor.TryExtractRelationshipsAsync(mediaItemAccessor, aspects, extractedItems).ConfigureAwait(false); } foreach (IDictionary <Guid, IList <MediaItemAspect> > extractedItem in extractedItems) { relations.Add(new ExtractedRelation(roleExtractors[0], extractedItem)); } ServiceRegistration.Get <ILogger>().Debug("Extractor {0} extracted {1} media items from media item {2}", roleExtractors[0].GetType().Name, extractedItems.Count, mediaItemId); }
public IDictionary<Guid, MediaItemAspect> ExtractMetadata(IResourceAccessor mediaItemAccessor, IEnumerable<Guid> metadataExtractorIds, bool forceQuickMode) { ICollection<IMetadataExtractor> extractors = new List<IMetadataExtractor>(); foreach (Guid extractorId in metadataExtractorIds) { IMetadataExtractor extractor; if (LocalMetadataExtractors.TryGetValue(extractorId, out extractor)) extractors.Add(extractor); } return ExtractMetadata(mediaItemAccessor, extractors, forceQuickMode); }
/// <summary> /// Creates _resourceAccessor from the _resourceLocator which can be used by the specific player. /// </summary> protected virtual void CreateResourceAccessor() { _resourceAccessor = _resourceLocator.CreateAccessor(); }
public MediaItem CreateLocalMediaItem(IResourceAccessor mediaItemAccessor, IEnumerable<Guid> metadataExtractorIds) { ISystemResolver systemResolver = ServiceRegistration.Get<ISystemResolver>(); const bool forceQuickMode = true; IDictionary<Guid, MediaItemAspect> aspects = ExtractMetadata(mediaItemAccessor, metadataExtractorIds, forceQuickMode); if (aspects == null) return null; MediaItemAspect providerResourceAspect = MediaItemAspect.GetOrCreateAspect(aspects, ProviderResourceAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, mediaItemAccessor.CanonicalLocalResourcePath.Serialize()); return new MediaItem(Guid.Empty, aspects); }
public virtual Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { IResourceAccessor metaFileAccessor; if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor)) { return(Task.FromResult(false)); } Tags tags; using (metaFileAccessor) { using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead()) tags = (Tags)GetTagsXmlSerializer().Deserialize(metaStream); } string value; MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false); if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(value)); } if (TryGet(tags, TAG_GENRE, out value) && !string.IsNullOrEmpty(value?.Trim())) { List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo { Name = value.Trim() } }); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in genreList) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId)) { genre.Id = genreId; } } MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata); genreAspect.SetAttribute(GenreAspect.ATTR_ID, genreList[0].Id); genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genreList[0].Name); } if (TryGet(tags, TAG_PLOT, out value)) { MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value); Match yearMatch = _yearMatcher.Match(value); int guessedYear; if (int.TryParse(yearMatch.Value, out guessedYear)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1)); } } if (TryGet(tags, TAG_CHANNEL, out value)) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value); } // Recording date formatted: 2011-11-04 20:55 DateTime tmpValue; DateTime?recordingStart = null; DateTime?recordingEnd = null; DateTime?programStart = null; DateTime?programEnd = null; // First try to read program start and end times, they will be preferred. if (TryGet(tags, TAG_PROGRAMSTARTTIME, out value) && DateTime.TryParse(value, out tmpValue)) { programStart = tmpValue; } if (TryGet(tags, TAG_PROGRAMENDTIME, out value) && DateTime.TryParse(value, out tmpValue)) { programEnd = tmpValue; } if (TryGet(tags, TAG_STARTTIME, out value) && DateTime.TryParse(value, out tmpValue)) { recordingStart = tmpValue; } if (TryGet(tags, TAG_ENDTIME, out value) && DateTime.TryParse(value, out tmpValue)) { recordingEnd = tmpValue; } // Correct start time if recording started before the program (skip pre-recording offset) if (programStart.HasValue && recordingStart.HasValue && programStart > recordingStart) { recordingStart = programStart; } // Correct end time if recording ended after the program (skip the post-recording offset) if (programEnd.HasValue && recordingEnd.HasValue && programEnd < recordingEnd) { recordingEnd = programEnd; } if (recordingStart.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recordingStart.Value); } if (recordingEnd.HasValue) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recordingEnd.Value); } return(Task.FromResult(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("Tve3RecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(Task.FromResult(false)); }
public bool IsStubResource(IResourceAccessor mediaItemAccessor) { return(false); }
protected static bool CanExtract(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, out IResourceAccessor metaFileAccessor) { metaFileAccessor = null; IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { return(false); } string title; if (!MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) || string.IsNullOrEmpty(title)) { return(false); } string filePath = mediaItemAccessor.CanonicalLocalResourcePath.ToString(); string lowerExtension = StringUtils.TrimToEmpty(ProviderPathHelper.GetExtension(filePath)).ToLowerInvariant(); if (lowerExtension != ".ts") { return(false); } string metaFilePath = ProviderPathHelper.ChangeExtension(filePath, ".xml"); if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out metaFileAccessor)) { return(false); } return(true); }
public bool IsDirectorySingleResource(IResourceAccessor mediaItemAccessor) { return(false); }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { result = GetResourceAccessor(path); return result != null; }
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 using (lfsra.EnsureLocalFileSystemAccess()) { BDInfoExt bdinfo = new BDInfoExt(lfsra.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 bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { result = SlimTvResourceAccessor.GetResourceAccessor(path); return(result != null); }
public bool TryCreateResourceAccessor(string path, out IResourceAccessor result) { string nativeSystemId; ResourcePath nativeResourcePath; if (!TryExtractSystemAndPath(path, out nativeSystemId, out nativeResourcePath)) throw new InvalidDataException("Path '{0}' is not a valid path for remote resource provider", path); ISystemResolver systemResolver = ServiceRegistration.Get<ISystemResolver>(); SystemName nativeSystem = systemResolver.GetSystemNameForSystemId(nativeSystemId); if (nativeSystem == null) throw new IllegalCallException("Cannot create resource accessor for resource location '{0}' at system '{1}': System is not available", nativeResourcePath, nativeSystemId); // Try to access resource locally. This might work if we have the correct resource providers installed. if (nativeSystem.IsLocalSystem() && nativeResourcePath.IsValidLocalPath && nativeResourcePath.TryCreateLocalResourceAccessor(out result)) return true; IFileSystemResourceAccessor fsra; if (RemoteFileSystemResourceAccessor.ConnectFileSystem(nativeSystemId, nativeResourcePath, out fsra)) { result = fsra; return true; } IResourceAccessor ra; if (RemoteFileResourceAccessor.ConnectFile(nativeSystemId, nativeResourcePath, out ra)) { result = ra; return true; } result = null; return false; }
public async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { string fileName = mediaItemAccessor.ResourceName; if (!HasImageExtension(fileName)) { return(false); } if (DosPathHelper.GetFileNameWithoutExtension(fileName).ToLowerInvariant() == "folder") { return(false); //Ignore folder images } 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_TYPE, ProviderResourceAspect.TYPE_PRIMARY); if (!(mediaItemAccessor is IFileSystemResourceAccessor)) { return(false); } providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size); if (!forceQuickMode) { // 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); } } else { string mimeType = MimeTypeDetector.GetMimeTypeFromExtension(fileName) ?? DEFAULT_MIMETYPE; providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mimeType); } } MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata); mediaAspect.SetAttribute(MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect imageAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, ImageAspect.Metadata); if (!refresh) { mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, ProviderPathHelper.GetFileNameWithoutExtension(fileName)); if (!forceQuickMode) { // Extract EXIF information from media item. using (ExifMetaInfo.ExifMetaInfo exif = new ExifMetaInfo.ExifMetaInfo(fsra)) { 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); } } } else { mediaAspect.SetAttribute(MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged); imageAspect.SetAttribute(ImageAspect.ATTR_ORIENTATION, 0); } 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, forceQuickMode, 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 (!forceQuickMode && IncludeGeoLocationDetails && latitude.HasValue && longitude.HasValue && string.IsNullOrEmpty(imageAspect.GetAttributeValue <string>(ImageAspect.ATTR_COUNTRY))) { var geoCoordinate = new GeoCoordinate(latitude.Value, longitude.Value); var lookupResult = await GeoLocationService.Instance.TryLookupAsync(geoCoordinate).ConfigureAwait(false); if (lookupResult.Success) { CivicAddress locationInfo = lookupResult.Result; 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); }
public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode) { try { IResourceAccessor ra = mediaItemAccessor.Clone(); try { using (ILocalFsResourceAccessor fsra = StreamedResourceToLocalFsAccessBridge.GetLocalFsResourceAccessor(ra)) if (fsra != null && fsra.IsDirectory && fsra.ResourceExists("BDMV")) { IFileSystemResourceAccessor fsraBDMV = fsra.GetResource("BDMV"); if (fsraBDMV != null && fsraBDMV.ResourceExists("index.bdmv")) { // BluRay MediaItemAspect mediaAspect; if (!extractedAspectData.TryGetValue(MediaAspect.ASPECT_ID, out mediaAspect)) extractedAspectData[MediaAspect.ASPECT_ID] = mediaAspect = new MediaItemAspect(MediaAspect.Metadata); MediaItemAspect videoAspect; if (!extractedAspectData.TryGetValue(VideoAspect.ASPECT_ID, out videoAspect)) extractedAspectData[VideoAspect.ASPECT_ID] = new MediaItemAspect(VideoAspect.Metadata); mediaAspect.SetAttribute(MediaAspect.ATTR_MIME_TYPE, "video/bluray"); // BluRay disc string bdmvDirectory = fsra.LocalFileSystemPath; BDInfoExt bdinfo = new BDInfoExt(bdmvDirectory); mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, bdinfo.GetTitle() ?? mediaItemAccessor.ResourceName); return true; } } } catch { ra.Dispose(); throw; } 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; } }
/// <summary> /// Instantiates all the necessary DataflowBlocks for the given ImportJob /// </summary> /// <remarks> /// We first have to distinguish between two cases here: /// - BasePath points to a resource for which we can only create an IResourceAccessor - not an IFilesystemResourceAccessor /// For this case we only import that single resource and don't have to take care of directories and subdirectories /// ToDo: This still needs to be implemented /// - BasePath points to a resource for which we can create an IFilesystemResourceAccessor /// Here we first check whether the resource on the given BasePath exists. If not, we do nothing. /// If it does exist, we distinguish two cases: /// - The ImportJob was restored from disk, in which case we push the existing PendingImportResources to the respective DataflowBlocks. /// - The ImportJob was freshly created, in which case we push the BasePath to the first DataFlowBlock. /// In this case there are three subcases: /// - BasePath points to a single resource /// - BasePath points to a directory which is not a single resource and the ImportJob does not include subdirectories /// - BasePath points to a directory which is not a single resource and the ImportJob does include subdirectories /// These subcases, however, are taken care of by the DataflowBlocks - not by the ImportJobController /// </remarks> private void SetupDataflowBlocks(IEnumerable <PendingImportResourceNewGen> pendingImportResources) { // If we cannot access the BasePath at all, we just log and return IResourceAccessor ra = null; try { if (!_importJobInformation.BasePath.TryCreateLocalResourceAccessor(out ra)) { ServiceRegistration.Get <ILogger>().Warn("ImporterWorker.{0}: Unable to access resource path '{1}'.", this, _importJobInformation.BasePath); return; } } catch (Exception e) { ServiceRegistration.Get <ILogger>().Error("ImporterWorker.{0}: Error while creating ResourceAccessor for resource path '{1}'.", e, this, _importJobInformation.BasePath); if (ra != null) { ra.Dispose(); } return; } try { // As of now we have a ResourceAccessor that needs to be disposed using (ra) { // If we have a ResourceAccessor which is not an IFileSystemResourceAccessor, just import that single resource if (!(ra is IFileSystemResourceAccessor)) { // ToDo: Implement import of Non-IFilesSystemResourceAccessors return; } // Now we are sure it is an IFileSystemResourceAccessor var fsra = ra as IFileSystemResourceAccessor; // If the BasePath does not exist, we do nothing. This is necessary to avoid whole shares being removed from // the MediaLibrary when a RefreshImport is scheduled while e.g. network resources are unavailable. // If fsra is a NetworkNeighborhoddResourceAccessor and its IsServerPath() method returns true, fsra.Exists() // will always return true. If therefore the BasePath of this Import points to a whole server and this server // is not available during a RefreshImport, the whole share will be deleted from the MediaLibrary. // ToDo: Rework this in NetworkNeighborhoodResourceAccessor if (!fsra.Exists) { ServiceRegistration.Get <ILogger>().Warn("ImporterWorker.{0}: Resource '{1}' does not exists or is not available.", this, _importJobInformation.BasePath); return; } // Now we are sure that we need a DataflowBlock network // Create the blocks _dataflowBlocks.Add(new DirectoryUnfoldBlock(_cts.Token, _importJobInformation, this)); _dataflowBlocks.Add(new DirectorySaveBlock(_cts.Token, _importJobInformation, this)); _dataflowBlocks.Add(new FileUnfoldBlock(_cts.Token, _importJobInformation, this)); _dataflowBlocks.Add(new MediaItemLoadBlock(_cts.Token, _importJobInformation, this)); _dataflowBlocks.Add(new MetadataExtractorBlock(_cts.Token, _importJobInformation, this, false)); _dataflowBlocks.Add(new MediaItemSaveBlock(_cts.Token, _importJobInformation, this)); _dataflowBlocks.Add(new RelationshipExtractorBlock(_cts.Token, _importJobInformation, this)); // Link the blocks for (int i = 0; i < _dataflowBlocks.Count - 1; i++) { _dataflowBlocks[i].LinkTo(_dataflowBlocks[i + 1], new DataflowLinkOptions { PropagateCompletion = true }); } _dataflowBlocks[_dataflowBlocks.Count - 1].LinkTo(DataflowBlock.NullTarget <PendingImportResourceNewGen>()); // Fill the blocks var completeFirstBlockAfterTheseTasks = new HashSet <Task>(); bool firstBlockNeedsCompletion = true; if (pendingImportResources == null) { // This ImportJob was freshly created and not persisted to disk before // Just post the BasePath as new PendingImportResource var rootImportResource = new PendingImportResourceNewGen(null, fsra.Clone() as IFileSystemResourceAccessor, DirectoryUnfoldBlock.BLOCK_NAME, this); _dataflowBlocks[0].Post(rootImportResource); firstBlockNeedsCompletion = false; } else { // This ImportJob was persisted to disk before int numberOfRestoredPendingResources = 0; foreach (var pendingImportResource in pendingImportResources) { pendingImportResource.InitializeAfterDeserialization(this); ImporterWorkerDataflowBlockBase block = _dataflowBlocks.Find(b => b.ToString() == pendingImportResource.CurrentBlock); if (block != null) { completeFirstBlockAfterTheseTasks.Add(block.SendAsync(pendingImportResource, _cts.Token)); numberOfRestoredPendingResources++; if (block == _dataflowBlocks[0]) { firstBlockNeedsCompletion = false; } } else { ServiceRegistration.Get <ILogger>().Error("ImporterWorker.{0}: Could not add {1} after deserialization. DataflowBlock with name {2} does not exist.", this, pendingImportResource, pendingImportResource.CurrentBlock); pendingImportResource.Dispose(); } } ServiceRegistration.Get <ILogger>().Debug("ImporterWorker.{0}: {1} PendingImportResources restored.", this, numberOfRestoredPendingResources); } completeFirstBlockAfterTheseTasks.Add(_firstBlockHasFinished.Task); if (firstBlockNeedsCompletion) { FirstBlockHasFinished(); } // The first DataflowBlock in the network (DirectoryUnfoldBlock) must be set to completed when // (a) The DirectoryUnfoldBlock has signaled that it is finished (by calling FirstBlockHasFinished()) and // (b) in case of an ImportJob that has been restored from disk, all restored PendingImportResources // have been put into the Dataflow network Task.WhenAll(completeFirstBlockAfterTheseTasks).ContinueWith(previousTask => _dataflowBlocks[0].Complete()); } } catch (Exception e) { ServiceRegistration.Get <ILogger>().Error("ImporterWorker.{0}: Error while setting up DataflowBlocks for resource path '{1}.", e, this, _importJobInformation.BasePath); } }