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, ".arg"); if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out metaFileAccessor)) { return(false); } return(true); }
protected static bool CanExtract(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData) { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.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"); if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out _)) { return(false); } return(true); }
private static void TestRecording(string filename) { if (Directory.Exists("_Test")) { Directory.Delete("_Test", true); } ServiceRegistration.Set <IPathManager>(new PathManager()); ServiceRegistration.Get <IPathManager>().SetPath("DATA", "_Test/Data"); ServiceRegistration.Set <ILocalization>(new NoLocalization()); ServiceRegistration.Set <ILogger>(new ConsoleLogger(LogLevel.All, true)); ServiceRegistration.Set <IMediaAccessor>(new TestMediaAccessor()); ServiceRegistration.Set <IMediaItemAspectTypeRegistration>(new MockMediaItemAspectTypeRegistration()); ApplicationCore.RegisterDefaultMediaItemAspectTypes().Wait(); ServiceRegistration.Set <SeriesTvDbMatcher>(new SeriesTvDbMatcher()); IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >(); string ext = StringUtils.TrimToEmpty(ProviderPathHelper.GetExtension(filename)).ToLowerInvariant(); if (ext != ".xml") { Console.WriteLine("Filetype must be XML"); return; } XmlSerializer serializer = new XmlSerializer(typeof(Tve3RecordingMetadataExtractor.Tags)); using (Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { Tve3RecordingMetadataExtractor.Tags tags = (Tve3RecordingMetadataExtractor.Tags)serializer.Deserialize(stream); Tve3RecordingMetadataExtractor.SimpleTag tag = tags.Tag.Find(t => t.Name == "TITLE"); MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_TITLE, tag.Value); } IMediaItemAspectTypeRegistration registration = ServiceRegistration.Get <IMediaItemAspectTypeRegistration>(); Console.WriteLine("Before extract:"); ShowMIAs(aspects, registration); IMetadataExtractor extractor = new Tve3RecordingMetadataExtractor(); IResourceAccessor accessor = new MockLocalFsResourceAccessor(ProviderPathHelper.ChangeExtension(filename, ".ts")); extractor.TryExtractMetadataAsync(accessor, aspects, false); Console.WriteLine("After extract:"); ShowMIAs(aspects, registration); string value; if (MediaItemAspect.TryGetExternalAttribute(aspects, ExternalIdentifierAspect.SOURCE_TVDB, ExternalIdentifierAspect.TYPE_SERIES, out value)) { SeriesInfo seriesInfo = new SeriesInfo() { TvdbId = Int32.Parse(value) }; SeriesTvDbMatcher.Instance.UpdateSeriesAsync(seriesInfo, false).Wait(); Console.WriteLine("{0}: {1}", seriesInfo.SeriesName, seriesInfo.Description); } }
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 override async Task <AsyncResult <ContentDirectoryMessaging.MediaItemChangeType> > ProcessAsync(MediaItem mediaItem) { IContentDirectory cd = ServiceRegistration.Get <IServerConnectionManager>().ContentDirectory; bool removeFromML = IsManagedByMediaLibrary(mediaItem) && cd != null; var falseResult = new AsyncResult <ContentDirectoryMessaging.MediaItemChangeType>(false, ContentDirectoryMessaging.MediaItemChangeType.None); // Support multi-resource media items and secondary resources IList <MultipleMediaItemAspect> providerAspects; if (!MediaItemAspect.TryGetAspects(mediaItem.Aspects, ProviderResourceAspect.Metadata, out providerAspects)) { return(falseResult); } foreach (MultipleMediaItemAspect providerAspect in providerAspects) { string systemId = (string)providerAspect[ProviderResourceAspect.ATTR_SYSTEM_ID]; string resourceAccessorPath = (string)providerAspect[ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH]; var rl = new ResourceLocator(systemId, ResourcePath.Deserialize(resourceAccessorPath)); using (var ra = rl.CreateAccessor()) { var rad = ra as IResourceDeletor; if (rad == null) { return(falseResult); } // First try to delete the file from storage. if (!rad.Delete()) { return(falseResult); } // If the MediaItem was loaded from ML, remove it there as well. if (removeFromML) { await cd.DeleteMediaItemOrPathAsync(rl.NativeSystemId, rl.NativeResourcePath, true); } } } // Check for special cases here: // 1) Recordings have an additional .xml attached // 2) Deleting files could lead to empty folders that should be also removed foreach (DeleteRule rule in _defaultRules.Where(r => r.IsEnabled)) { if (mediaItem.Aspects.ContainsKey(rule.HasAspectGuid)) { var tsPath = mediaItem.GetResourceLocator().NativeResourcePath.ToString(); foreach (string otherExtension in rule.DeleteOtherExtensions) { string otherFilePath = ProviderPathHelper.ChangeExtension(tsPath, otherExtension); IResourceAccessor ra; if (!ResourcePath.Deserialize(otherFilePath).TryCreateLocalResourceAccessor(out ra)) { continue; } // Delete other file. We do not check here for existance of file, the Delete needs to handle this. using (ra) { var rad = ra as IResourceDeletor; rad?.Delete(); } } if (rule.DeleteEmptyFolders) { var folderPath = ProviderPathHelper.GetDirectoryName(tsPath); IResourceAccessor ra; if (!ResourcePath.Deserialize(folderPath).TryCreateLocalResourceAccessor(out ra)) { continue; } // Delete folder if empty using (ra) { IFileSystemResourceAccessor fsra = ra as IFileSystemResourceAccessor; if (fsra != null) { var isEmpty = fsra.GetFiles().Count == 0 && fsra.GetChildDirectories().Count == 0; if (isEmpty) { var rad = ra as IResourceDeletor; rad?.Delete(); } } } } } } return(new AsyncResult <ContentDirectoryMessaging.MediaItemChangeType>(true, ContentDirectoryMessaging.MediaItemChangeType.Deleted)); }
/// <summary> /// Tries to load chapter information from external file. This method checks for ComSkip files (.txt). /// </summary> /// <returns></returns> protected virtual bool EnumerateExternalChapters() { var fsra = _resourceAccessor as IFileSystemResourceAccessor; if (fsra == null || !fsra.IsFile) { return(false); } try { string filePath = _resourceAccessor.CanonicalLocalResourcePath.ToString(); string metaFilePath = ProviderPathHelper.ChangeExtension(filePath, ".txt"); IResourceAccessor raTextFile; if (!ResourcePath.Deserialize(metaFilePath).TryCreateLocalResourceAccessor(out raTextFile)) { return(false); } List <double> positions = new List <double>(); using (LocalFsResourceAccessorHelper lfsra = new LocalFsResourceAccessorHelper(raTextFile)) { if (lfsra.LocalFsResourceAccessor == null) { return(false); } using (var stream = lfsra.LocalFsResourceAccessor.OpenRead()) using (var chaptersReader = new StreamReader(stream)) { string line = chaptersReader.ReadLine(); int fps; if (string.IsNullOrWhiteSpace(line) || !int.TryParse(line.Substring(line.LastIndexOf(' ') + 1), out fps)) { ServiceRegistration.Get <ILogger>().Warn("VideoPlayer: EnumerateExternalChapters() - Invalid ComSkip chapter file"); return(false); } double framesPerSecond = fps / 100.0; while ((line = chaptersReader.ReadLine()) != null) { if (String.IsNullOrEmpty(line)) { continue; } string[] tokens = line.Split('\t'); if (tokens.Length != 2) { continue; } foreach (var token in tokens) { int time; if (int.TryParse(token, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out time)) { positions.Add(time / framesPerSecond); } } } } } // Insert start of video as position if (!positions.Contains(0d)) { positions.Insert(0, 0d); } var chapterNames = new List <string>(); var chapterTimes = new List <double>(); for (int index = 0; index < positions.Count - 1; index++) { var timeFrom = positions[index]; var timeTo = positions[index + 1]; // Filter out segments with less than 2 seconds duration if (timeTo - timeFrom <= 2) { continue; } var chapterName = string.Format("ComSkip {0} [{1} - {2}]", chapterNames.Count + 1, FormatDuration(timeFrom), FormatDuration(timeTo)); chapterNames.Add(chapterName); chapterTimes.Add(timeFrom); } _chapterNames = chapterNames.ToArray(); _chapterTimestamps = chapterTimes.ToArray(); return(_chapterNames.Length > 0); } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Error("VideoPlayer: EnumerateExternalChapters() - Exception while reading ComSkip chapter file", ex); return(false); } }