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);
        }
Example #2
0
        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);
        }
Example #3
0
        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);
            }
        }
Example #4
0
        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));
        }
Example #6
0
        /// <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);
            }
        }