コード例 #1
0
    public static bool TryMatchTmdbId(ILocalFsResourceAccessor folderOrFileLfsra, out int tmdbId)
    {
      // Calling EnsureLocalFileSystemAccess not necessary; only string operation
      string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(folderOrFileLfsra.LocalFileSystemPath)).ToLower();
      if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
      {
        tmdbId = 0;
        return false;
      }

      MatroskaInfoReader mkvReader = new MatroskaInfoReader(folderOrFileLfsra);
      // Add keys to be extracted to tags dictionary, matching results will returned as value
      Dictionary<string, IList<string>> tagsToExtract = MatroskaConsts.DefaultTags;
      mkvReader.ReadTags(tagsToExtract);

      if (tagsToExtract[MatroskaConsts.TAG_MOVIE_TMDB_ID] != null)
      {
        foreach (string candidate in tagsToExtract[MatroskaConsts.TAG_MOVIE_TMDB_ID])
        {
          if (int.TryParse(candidate, out tmdbId))
            return true;
        }
      }

      tmdbId = 0;
      return false;
    }
コード例 #2
0
    protected ILocalFsResourceAccessor _underlayingResource = null; // Only set if the path points to a file system resource - not a server

    #endregion

    public NetworkNeighborhoodResourceAccessor(NetworkNeighborhoodResourceProvider parent, string path)
    {
      _parent = parent;
      _path = path;
      if (IsServerPath(path))
        return;
      IResourceAccessor ra;
      if (!LocalFsResourceProvider.Instance.TryCreateResourceAccessor("/" + path, out ra))
        throw new IllegalCallException("Unable to access resource '{0}'", path);
      _underlayingResource = (ILocalFsResourceAccessor) ra;
    }
コード例 #3
0
    protected ILocalFsResourceAccessor _underlayingResource = null; // Only set if the path points to a file system resource - not a server or root

    #endregion

    #region Ctor

    public NetworkNeighborhoodResourceAccessor(NetworkNeighborhoodResourceProvider parent, string path)
    {
      _parent = parent;
      _path = path;
      if (IsRootPath(path ) || IsServerPath(path))
        return;

      IResourceAccessor ra;
      using (ServiceRegistration.Get<IImpersonationService>().CheckImpersonationFor(CanonicalLocalResourcePath))
        if (LocalFsResourceProvider.Instance.TryCreateResourceAccessor("/" + path, out ra))
          _underlayingResource = (ILocalFsResourceAccessor)ra;
    }
コード例 #4
0
    public NetworkNeighborhoodResourceAccessor(NetworkNeighborhoodResourceProvider parent, string path)
    {
      _parent = parent;
      _path = path;
      if (IsServerPath(path))
        return;

      _impersonationContext = ImpersonateUser(null);

      IResourceAccessor ra;
      if (LocalFsResourceProvider.Instance.TryCreateResourceAccessor("/" + path, out ra))
        _underlayingResource = (ILocalFsResourceAccessor) ra;
    }
コード例 #5
0
    /// <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;
      }
    }
コード例 #6
0
    /// <summary>
    /// Tries to match series by reading matroska tags from <paramref name="folderOrFileLfsra"/>.
    /// </summary>
    /// <param name="folderOrFileLfsra"><see cref="ILocalFsResourceAccessor"/> to file or folder</param>
    /// <param name="seriesInfo">Returns the parsed SeriesInfo</param>
    /// <param name="extractedAspectData">Dictionary containing a mapping of media item aspect ids to
    /// already present media item aspects, this metadata extractor should edit. If a media item aspect is not present
    /// in this dictionary but found by this metadata extractor, it will add it to the dictionary.</param>
    /// <returns><c>true</c> if successful.</returns>
    public bool MatchSeries(ILocalFsResourceAccessor folderOrFileLfsra, out SeriesInfo seriesInfo, ref IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // Calling EnsureLocalFileSystemAccess not necessary; only string operation
      string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(folderOrFileLfsra.LocalFileSystemPath)).ToLower();

      if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
      {
        seriesInfo = null;
        return false;
      }

      MatroskaInfoReader mkvReader = new MatroskaInfoReader(folderOrFileLfsra);
      // Add keys to be extracted to tags dictionary, matching results will returned as value
      Dictionary<string, IList<string>> tagsToExtract = MatroskaConsts.DefaultTags;
      mkvReader.ReadTags(tagsToExtract);

      string title = string.Empty;
      IList<string> tags = tagsToExtract[MatroskaConsts.TAG_SIMPLE_TITLE];
      if (tags != null)
        title = tags.FirstOrDefault();

      if (!string.IsNullOrEmpty(title))
        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);

      string yearCandidate = null;
      tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_YEAR] ?? tagsToExtract[MatroskaConsts.TAG_SEASON_YEAR];
      if (tags != null)
        yearCandidate = (tags.FirstOrDefault() ?? string.Empty).Substring(0, 4);

      int year;
      if (int.TryParse(yearCandidate, out year))
        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));

      tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
      string plot = tags != null ? tags.FirstOrDefault() : string.Empty;
      if (!string.IsNullOrEmpty(plot))
        MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, plot);

      // Series and episode handling. Prefer information from tags.
      seriesInfo = GetSeriesFromTags(tagsToExtract);

      return true;
    }
コード例 #7
0
 /// <summary>
 /// Executes an external program and ensures that it has access to the respective resource
 /// </summary>
 /// <param name="lfsra"><see cref="ILocalFsResourceAccessor"/> to which the external program needs access to</param>
 /// <param name="executable">External program to execute</param>
 /// <param name="arguments">Arguments for the external program</param>
 /// <param name="priorityClass">Process priority</param>
 /// <param name="maxWaitMs">Maximum time to wait for completion</param>
 /// <returns>A <see cref="Task"/> representing the result of executing the external program</returns>
 /// <remarks>
 /// This is a convenience method that enables executing an external procgram directly on the <see cref="ILocalFsResourceAccessor"/>
 /// interface to which the external program needs access. The purpose of an <see cref="ILocalFsResourceAccessor"/> is providing
 /// access to a resource - not executing programs which is why this method is implemented as an extension method instead of
 /// a method directly on the interface.
 /// </remarks>
 public static Task <ProcessExecutionResult> ExecuteWithResourceAccessAsync(this ILocalFsResourceAccessor lfsra, string executable, string arguments, ProcessPriorityClass priorityClass = ProcessPriorityClass.Normal, int maxWaitMs = ProcessUtils.DEFAULT_TIMEOUT)
 {
     return(ServiceRegistration.Get <IImpersonationService>().ExecuteWithResourceAccessAsync(lfsra.CanonicalLocalResourcePath, executable, arguments, priorityClass, maxWaitMs));
 }
コード例 #8
0
        protected void ExtractMatroskaTags(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(lfsra.LocalFileSystemPath)).ToLower();

            if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
            {
                return;
            }

            // Try to get extended information out of matroska files)
            MatroskaInfoReader mkvReader = new MatroskaInfoReader(lfsra);
            // Add keys to be extracted to tags dictionary, matching results will returned as value
            Dictionary <string, IList <string> > tagsToExtract = MatroskaConsts.DefaultTags;

            mkvReader.ReadTags(tagsToExtract);

            // Read title
            string         title = string.Empty;
            IList <string> tags  = tagsToExtract[MatroskaConsts.TAG_SIMPLE_TITLE];

            if (tags != null)
            {
                title = tags.FirstOrDefault();
            }
            if (!string.IsNullOrEmpty(title))
            {
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
            }

            // Read release date
            int    year;
            string yearCandidate = null;

            tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_YEAR] ?? tagsToExtract[MatroskaConsts.TAG_SEASON_YEAR];
            if (tags != null)
            {
                yearCandidate = (tags.FirstOrDefault() ?? string.Empty).Substring(0, 4);
            }

            if (int.TryParse(yearCandidate, out year))
            {
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));
            }

            // Read plot
            tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
            string plot = tags != null?tags.FirstOrDefault() : string.Empty;

            if (!string.IsNullOrEmpty(plot))
            {
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, plot);
            }

            // Read genre
            tags = tagsToExtract[MatroskaConsts.TAG_SERIES_GENRE];
            if (tags != null)
            {
                MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_GENRES, tags);
            }

            // Read actors
            IEnumerable <string> actors;
            // Combine series actors and episode actors if both are available
            var tagSeriesActors = tagsToExtract[MatroskaConsts.TAG_SERIES_ACTORS];
            var tagActors       = tagsToExtract[MatroskaConsts.TAG_ACTORS];

            if (tagSeriesActors != null && tagActors != null)
            {
                actors = tagSeriesActors.Union(tagActors);
            }
            else
            {
                actors = tagSeriesActors ?? tagActors;
            }

            if (actors != null)
            {
                MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, actors);
            }

            tags = tagsToExtract[MatroskaConsts.TAG_DIRECTORS];
            if (tags != null)
            {
                MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_DIRECTORS, tags);
            }

            tags = tagsToExtract[MatroskaConsts.TAG_WRITTEN_BY];
            if (tags != null)
            {
                MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_WRITERS, tags);
            }
        }
コード例 #9
0
        public bool TryGetFanArt(string mediaType, string fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <FanArtImage> result)
        {
            result = null;
            Guid mediaItemId;

            if (mediaType != FanArtMediaTypes.Album && mediaType != FanArtMediaTypes.Audio)
            {
                return(false);
            }

            if (!Guid.TryParse(name, out mediaItemId))
            {
                return(false);
            }

            IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false);

            if (mediaLibrary == null)
            {
                return(false);
            }

            IFilter filter = null;

            if (mediaType == FanArtMediaTypes.Album)
            {
                filter = new RelationshipFilter(AudioAspect.ROLE_TRACK, AudioAlbumAspect.ROLE_ALBUM, mediaItemId);
            }
            else if (mediaType == FanArtMediaTypes.Audio)
            {
                filter = new MediaItemIdFilter(mediaItemId);
            }
            MediaItemQuery mediaQuery = new MediaItemQuery(NECESSARY_MIAS, filter);

            mediaQuery.Limit = 1;
            IList <MediaItem> items = mediaLibrary.Search(mediaQuery, false, null, true);

            if (items == null || items.Count == 0)
            {
                return(false);
            }

            MediaItem mediaItem = items.First();

            // Virtual resources won't have any local fanart
            if (mediaItem.IsVirtual)
            {
                return(false);
            }
            var    resourceLocator       = mediaItem.GetResourceLocator();
            string fileSystemPath        = string.Empty;
            IList <PictureType> patterns = new List <PictureType>();

            switch (fanArtType)
            {
            case FanArtTypes.Undefined:
            case FanArtTypes.Poster:
            case FanArtTypes.Cover:
            case FanArtTypes.Thumbnail:
                patterns.Add(PictureType.FrontCover);
                patterns.Add(PictureType.Other);
                break;

            default:
                return(false);
            }
            // File based access
            try
            {
                using (var accessor = resourceLocator?.CreateAccessor())
                {
                    ILocalFsResourceAccessor fsra = accessor as ILocalFsResourceAccessor;
                    if (fsra != null)
                    {
                        fileSystemPath = fsra.LocalFileSystemPath;
                        var ext = Path.GetExtension(fsra.LocalFileSystemPath);
                        if (!SUPPORTED_EXTENSIONS.Contains(ext))
                        {
                            return(false);
                        }

                        ByteVector.UseBrokenLatin1Behavior = true; // Otherwise we have problems retrieving non-latin1 chars
                        using (var tag = TagLib.File.Create(fsra.LocalFileSystemPath))
                        {
                            IPicture[] pics = tag.Tag.Pictures;
                            if (pics.Length > 0)
                            {
                                foreach (var pattern in patterns)
                                {
                                    var picTag = pics.FirstOrDefault(p => p.Type == pattern);
                                    if (picTag != null)
                                    {
                                        result = new List <FanArtImage> {
                                            new FanArtImage(name, picTag.Data.Data)
                                        };
                                        return(true);
                                    }
                                }
                                //If no matching images found, use first images for thumbnails
                                if (fanArtType == FanArtTypes.Thumbnail)
                                {
                                    result = new List <FanArtImage> {
                                        new FanArtImage(name, pics[0].Data.Data)
                                    };
                                    return(true);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Warn("AudioTagProvider: Exception while reading tag of type '{0}' from '{1}'", ex, fanArtType, fileSystemPath);
            }
            return(false);
        }
コード例 #10
0
        private bool CollectionFolderHasFanArt(ILocalFsResourceAccessor lfsra, out string collectionName)
        {
            collectionName = null;

            // File based access
            try
            {
                using (lfsra.EnsureLocalFileSystemAccess())
                {
                    string collectionMediaItemDirectoryPath;
                    if (Directory.GetParent(lfsra.LocalFileSystemPath) != null && Directory.GetParent(Directory.GetParent(lfsra.LocalFileSystemPath).FullName) != null)
                    {
                        DirectoryInfo dir = Directory.GetParent(Directory.GetParent(lfsra.LocalFileSystemPath).FullName);
                        collectionMediaItemDirectoryPath = dir.FullName;
                        collectionName = dir.Name;
                    }
                    else
                    {
                        return(false);
                    }

                    var potentialFanArtFiles = GetPotentialFanArtFiles(collectionMediaItemDirectoryPath);

                    if ((from potentialFanArtFile in potentialFanArtFiles
                         let potentialFanArtFileNameWithoutExtension = Path.GetFileNameWithoutExtension(potentialFanArtFile.ToString()).ToLowerInvariant()
                                                                       where potentialFanArtFileNameWithoutExtension == "poster" || potentialFanArtFileNameWithoutExtension == "folder" || potentialFanArtFileNameWithoutExtension == "movieset-poster"
                                                                       select potentialFanArtFile).Count() > 0)
                    {
                        return(true);
                    }

                    if ((from potentialFanArtFile in potentialFanArtFiles
                         let potentialFanArtFileNameWithoutExtension = Path.GetFileNameWithoutExtension(potentialFanArtFile.ToString()).ToLowerInvariant()
                                                                       where potentialFanArtFileNameWithoutExtension == "banner" || potentialFanArtFileNameWithoutExtension == "movieset-banner"
                                                                       select potentialFanArtFile).Count() > 0)
                    {
                        return(true);
                    }

                    if ((from potentialFanArtFile in potentialFanArtFiles
                         let potentialFanArtFileNameWithoutExtension = Path.GetFileNameWithoutExtension(potentialFanArtFile.ToString()).ToLowerInvariant()
                                                                       where potentialFanArtFileNameWithoutExtension == "backdrop" || potentialFanArtFileNameWithoutExtension == "fanart" || potentialFanArtFileNameWithoutExtension == "movieset-fanart"
                                                                       select potentialFanArtFile).Count() > 0)
                    {
                        return(true);
                    }

                    string fanArtFolder = Path.Combine(collectionMediaItemDirectoryPath, "ExtraFanArt");
                    if (Directory.Exists(fanArtFolder))
                    {
                        if (GetPotentialFanArtFiles(fanArtFolder).Count() > 0)
                        {
                            return(true);
                        }
                    }
                }
            }
            catch
            {
            }
            return(false);
        }
コード例 #11
0
    private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
      // VideoAspect must be present to be sure it is actually a video resource.
      if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
        return false;

      byte[] thumb;
      // We only want to create missing thumbnails here, so check for existing ones first
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
        return true;

      // Check for a reasonable time offset
      long defaultVideoOffset = 720;
      long videoDuration;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
      {
        if (defaultVideoOffset > videoDuration * 1 / 3)
          defaultVideoOffset = videoDuration * 1 / 3;
      }

      string downscale = ",scale=iw/2:-1"; // Reduces the video frame size to a half of original

      int videoWidth;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_WIDTH, out videoWidth))
      {
        // Don't downscale SD video frames, quality is already quite low.
        if (videoWidth > 0 && videoWidth <= 720)
          downscale = "";
      }

      // ToDo: Move creation of temp file names to FileUtils class
      string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
      string executable = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
      string arguments = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
        defaultVideoOffset,
        // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
        lfsra.LocalFileSystemPath,
        tempFileName,
        downscale);

      try
      {
        bool success;
        lock (FFMPEG_THROTTLE_LOCK)
          success = lfsra.ExecuteWithResourceAccessAsync(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS).Result.Success;
        if (success && File.Exists(tempFileName))
        {
          var binary = FileUtils.ReadFile(tempFileName);
          MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
          // Calling EnsureLocalFileSystemAccess not necessary; only string operation
          ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
        }
        else
          // Calling EnsureLocalFileSystemAccess not necessary; only string operation
          ServiceRegistration.Get<ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
      }
      catch (AggregateException ae)
      {
        ae.Handle(e =>
        {
          if (e is TaskCanceledException)
          {
            ServiceRegistration.Get<ILogger>().Warn("VideoThumbnailer.ExtractThumbnail: External process aborted due to timeout: Executable='{0}', Arguments='{1}', Timeout='{2}'", executable, arguments, PROCESS_TIMEOUT_MS);
            return true;
          }
          return false;
        });
      }
      finally
      {
        if (File.Exists(tempFileName))
          File.Delete(tempFileName);
      }
      return true;
    }
コード例 #12
0
 public bool TryCreateLocalFsAccessor(out ILocalFsResourceAccessor localFsResourceAccessor)
 {
   IResourceAccessor accessor = CreateAccessor();
   IFileSystemResourceAccessor fsra = accessor as IFileSystemResourceAccessor;
   if (fsra == null)
   {
     accessor.Dispose();
     localFsResourceAccessor = null;
     return false;
   }
   try
   {
     localFsResourceAccessor = StreamedResourceToLocalFsAccessBridge.StreamedResourceToLocalFsAccessBridge.GetLocalFsResourceAccessor(fsra);
     return true;
   }
   catch
   {
     accessor.Dispose();
     throw;
   }
 }
コード例 #13
0
    protected void ExtractThumbnailData(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode)
    {
      // In quick mode only allow thumbs taken from cache.
      bool cachedOnly = forceQuickMode;

      // Thumbnail extraction
      IThumbnailGenerator generator = ServiceRegistration.Get<IThumbnailGenerator>();
      byte[] thumbData;
      ImageType imageType;
      using (lfsra.EnsureLocalFileSystemAccess())
        if (generator.GetThumbnail(lfsra.LocalFileSystemPath, 256, 256, cachedOnly, out thumbData, out imageType))
          MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, thumbData);
    }
コード例 #14
0
        private async Task <bool> ExtractThumbnailAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID))
            {
                return(false);
            }

            //ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: Evaluate {0}", lfsra.ResourceName);

            bool isPrimaryResource = false;
            IList <MultipleMediaItemAspect> resourceAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out resourceAspects))
            {
                foreach (MultipleMediaItemAspect pra in resourceAspects)
                {
                    string       accessorPath = (string)pra.GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH);
                    ResourcePath resourcePath = ResourcePath.Deserialize(accessorPath);
                    if (resourcePath.Equals(lfsra.CanonicalLocalResourcePath))
                    {
                        if (pra.GetAttributeValue <int?>(ProviderResourceAspect.ATTR_TYPE) == ProviderResourceAspect.TYPE_PRIMARY)
                        {
                            isPrimaryResource = true;
                            break;
                        }
                    }
                }
            }

            if (!isPrimaryResource) //Ignore subtitles
            {
                return(false);
            }

            // Check for a reasonable time offset
            long   defaultVideoOffset = 720;
            long   videoDuration;
            string downscale = ",scale='min(256,iw)':-1"; // 256 is max size of large thumbnail aspect
            IList <MultipleMediaItemAspect> videoAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoStreamAspect.Metadata, out videoAspects))
            {
                if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0)
                {
                    if (defaultVideoOffset > videoDuration * 1 / 3)
                    {
                        defaultVideoOffset = videoDuration * 1 / 3;
                    }
                }
            }

            string tempFileName = FileUtils.GetTempFileName(".jpg");
            string arguments    = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
                                                defaultVideoOffset,
                                                // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
                                                lfsra.LocalFileSystemPath,
                                                tempFileName,
                                                downscale);

            //ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: FFMpeg {0} {1}", executable, arguments);

            await FFMPEG_THROTTLE_LOCK.WaitAsync().ConfigureAwait(false);

            try
            {
                ProcessExecutionResult executionResult = await FFMpegBinary.FFMpegExecuteWithResourceAccessAsync(lfsra, arguments, ProcessPriorityClass.BelowNormal, PROCESS_TIMEOUT_MS).ConfigureAwait(false);

                if (executionResult.Success && File.Exists(tempFileName))
                {
                    var binary = FileUtils.ReadFile(tempFileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
                else
                {
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                    ServiceRegistration.Get <ILogger>().Debug("VideoThumbnailer: FFMpeg failure {0} dump:\n{1}", executionResult.ExitCode, executionResult.StandardError);
                }
            }
            catch (TaskCanceledException)
            {
                ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: External process aborted due to timeout: Executable='{0}', Arguments='{1}'", FFMpegBinary.FFMpegPath, arguments);
            }
            finally
            {
                FFMPEG_THROTTLE_LOCK.Release();

                try
                {
                    if (File.Exists(tempFileName))
                    {
                        File.Delete(tempFileName);
                    }
                }
                catch { }
            }
            return(true);
        }
コード例 #15
0
 public static bool IsExtracted(ILocalFsResourceAccessor accessor, string selectedItem, out string extractedPath)
 {
     extractedPath = GetExtractionPath(accessor.CanonicalLocalResourcePath.LastPathSegment.Path, selectedItem);
     return(File.Exists(extractedPath));
 }
コード例 #16
0
ファイル: VideoPlayer.cs プロジェクト: joconno4/MediaPortal-2
    public void SetMediaItem(IResourceLocator locator, string mediaItemTitle)
    {
      // free previous opened resource
      FilterGraphTools.TryDispose(ref _resourceAccessor);
      FilterGraphTools.TryDispose(ref _rot);

      _state = PlayerState.Active;
      _isPaused = true;
      try
      {
        _resourceLocator = locator;
        _mediaItemTitle = mediaItemTitle;
        _resourceAccessor = _resourceLocator.CreateLocalFsAccessor();
        ServiceRegistration.Get<ILogger>().Debug("{0}: Initializing for media item '{1}'", PlayerTitle, _resourceAccessor.LocalFileSystemPath);

        // Create a DirectShow FilterGraph
        CreateGraphBuilder();

        // Add it in ROT (Running Object Table) for debug purpose, it allows to view the Graph from outside (i.e. graphedit)
        _rot = new DsROTEntry(_graphBuilder);

        // Add a notification handler (see WndProc)
        _instancePtr = Marshal.AllocCoTaskMem(4);
        IMediaEventEx mee = _graphBuilder as IMediaEventEx;
        if (mee != null)
          mee.SetNotifyWindow(SkinContext.Form.Handle, WM_GRAPHNOTIFY, _instancePtr);

        // Create the Allocator / Presenter object
        FreeEvrCallback();
        CreateEvrCallback();

        AddEvr();

        ServiceRegistration.Get<ILogger>().Debug("{0}: Adding audio renderer", PlayerTitle);
        AddAudioRenderer();

        ServiceRegistration.Get<ILogger>().Debug("{0}: Adding preferred codecs", PlayerTitle);
        AddPreferredCodecs();

        ServiceRegistration.Get<ILogger>().Debug("{0}: Adding file source", PlayerTitle);
        AddFileSource();

        ServiceRegistration.Get<ILogger>().Debug("{0}: Run graph", PlayerTitle);

        //This needs to be done here before we check if the evr pins are connected
        //since this method gives players the chance to render the last bits of the graph
        OnBeforeGraphRunning();

        // Now run the graph, i.e. the DVD player needs a running graph before getting informations from dvd filter.
        IMediaControl mc = (IMediaControl) _graphBuilder;
        int hr = mc.Run();
        DsError.ThrowExceptionForHR(hr);

        _initialized = true;
        OnGraphRunning();
      }
      catch (Exception)
      {
        Shutdown();
        throw;
      }
    }
コード例 #17
0
        private bool ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
            string   title       = null;
            string   sortTitle   = null;

            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            if (extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID) && !importOnly)
            {
                return(false); //Subtitles can only be imported not refreshed
            }
            bool refresh = false;

            if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID))
            {
                refresh = true;
            }

            MovieInfo movieInfo = new MovieInfo();

            if (refresh)
            {
                movieInfo.FromMetadata(extractedAspectData);
            }

            if (movieInfo.MovieName.IsEmpty)
            {
                //Try to get title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) &&
                    !string.IsNullOrEmpty(title) && !lfsra.ResourceName.StartsWith(title, StringComparison.InvariantCultureIgnoreCase))
                {
                    movieInfo.MovieName = title;
                    /* Clear the names from unwanted strings */
                    MovieNameMatcher.CleanupTitle(movieInfo);
                }
            }
            if (movieInfo.MovieNameSort.IsEmpty)
            {
                //Try to get sort title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, out sortTitle) && !string.IsNullOrEmpty(sortTitle))
                {
                    movieInfo.MovieNameSort = sortTitle;
                }
            }

            if (movieInfo.MovieDbId == 0)
            {
                try
                {
                    // Try to use an existing TMDB id for exact mapping
                    string tmdbId;
                    if (MatroskaMatcher.TryMatchTmdbId(lfsra, out tmdbId))
                    {
                        movieInfo.MovieDbId = Convert.ToInt32(tmdbId);
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading TMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (string.IsNullOrEmpty(movieInfo.ImdbId))
            {
                try
                {
                    // Try to use an existing IMDB id for exact mapping
                    string imdbId = null;
                    if (pathsToTest.Any(path => MatroskaMatcher.TryMatchImdbId(lfsra, out imdbId)))
                    {
                        movieInfo.ImdbId = imdbId;
                    }
                    else if (pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId)))
                    {
                        movieInfo.ImdbId = imdbId;
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading IMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (!movieInfo.IsBaseInfoPresent)
            {
                // Also test the full path year. This is useful if the path contains the real name and year.
                foreach (string path in pathsToTest)
                {
                    if (MovieNameMatcher.MatchTitleYear(path, movieInfo))
                    {
                        break;
                    }
                }
                //Fall back to MediaAspect.ATTR_TITLE
                if (movieInfo.MovieName.IsEmpty && !string.IsNullOrEmpty(title))
                {
                    movieInfo.MovieName = title;
                }

                /* Clear the names from unwanted strings */
                MovieNameMatcher.CleanupTitle(movieInfo);
            }

            if (!movieInfo.ReleaseDate.HasValue && !movieInfo.HasExternalId)
            {
                // When searching movie title, the year can be relevant for multiple titles with same name but different years
                DateTime recordingDate;
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate))
                {
                    movieInfo.ReleaseDate = recordingDate;
                }
            }

            // Allow the online lookup to choose best matching language for metadata
            if (movieInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !movieInfo.Languages.Contains(language))
                        {
                            movieInfo.Languages.Add(language);
                        }
                    }
                }
            }

            if (importOnly)
            {
                try
                {
                    MatroskaMatcher.ExtractFromTags(lfsra, movieInfo);
                    MP4Matcher.ExtractFromTags(lfsra, movieInfo);
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                MovieInfo tempInfo = movieInfo.Clone();
                OnlineMatcherService.Instance.FindAndUpdateMovie(tempInfo, importOnly);
                movieInfo.CopyIdsFrom(tempInfo);
                movieInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.FindAndUpdateMovie(movieInfo, importOnly);
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !movieInfo.HasExternalId)
            {
                return(false);
            }

            if (importOnly)
            {
                //Create custom collection (overrides online collection)
                MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>();
                string collectionName;
                if (string.IsNullOrEmpty(collectionInfo.NameId) && CollectionFolderHasFanArt(lfsra, out collectionName))
                {
                    collectionInfo = new MovieCollectionInfo();
                    collectionInfo.CollectionName = collectionName;
                    if (!collectionInfo.CollectionName.IsEmpty)
                    {
                        movieInfo.CollectionName = collectionInfo.CollectionName;
                        movieInfo.CopyIdsFrom(collectionInfo); //Reset ID's
                        movieInfo.HasChanged = true;
                    }
                }
            }
            movieInfo.AssignNameId();

            if (refresh)
            {
                if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && movieInfo.Actors.Count > 0) ||
                    (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && movieInfo.Characters.Count > 0) ||
                    (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && movieInfo.Directors.Count > 0) ||
                    (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && movieInfo.Writers.Count > 0) ||
                    (IncludeProductionCompanyDetails && !BaseInfo.HasRelationship(extractedAspectData, CompanyAspect.ROLE_COMPANY) && movieInfo.ProductionCompanies.Count > 0))
                {
                    movieInfo.HasChanged = true;
                }
            }

            if (!movieInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            movieInfo.SetMetadata(extractedAspectData);

            return(movieInfo.IsBaseInfoPresent);
        }
コード例 #18
0
        private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            byte[] thumb;
            // We only want to create missing thumbnails here, so check for existing ones first
            if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
            {
                return(true);
            }

            // Check for a reasonable time offset
            long defaultVideoOffset = 720;
            long videoDuration;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
            {
                if (defaultVideoOffset > videoDuration * 1 / 3)
                {
                    defaultVideoOffset = videoDuration * 1 / 3;
                }
            }

            // ToDo: Move creation of temp file names to FileUtils class
            string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
            string executable   = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
            string arguments    = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1,scale=iw/2:-1\" -y \"{2}\"",
                                                defaultVideoOffset,
                                                lfsra.LocalFileSystemPath,
                                                tempFileName);

            try
            {
                bool success;
                lock (FFMPEG_THROTTLE_LOCK)
                    success = ProcessUtils.TryExecute_AutoImpersonate(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS);
                if (success && File.Exists(tempFileName))
                {
                    var binary = FileUtils.ReadFile(tempFileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                    ServiceRegistration.Get <ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
                else
                {
                    ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
            }
            finally
            {
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }
            }
            return(true);
        }
コード例 #19
0
        private void ExtractMkvImages(ILocalFsResourceAccessor lfsra, Guid?seriesMediaItemId, SeriesInfo series)
        {
            if (!seriesMediaItemId.HasValue)
            {
                return;
            }

            string mediaItemId    = seriesMediaItemId.Value.ToString().ToUpperInvariant();
            string fileSystemPath = string.Empty;
            IDictionary <string, string> patterns = new Dictionary <string, string>()
            {
                { "banner.", FanArtTypes.Banner },
                { "clearart.", FanArtTypes.ClearArt },
                { "cover.", FanArtTypes.Cover },
                { "poster.", FanArtTypes.Poster },
                { "folder.", FanArtTypes.Poster },
                { "backdrop.", FanArtTypes.FanArt },
                { "fanart.", FanArtTypes.FanArt },
            };

            // File based access
            try
            {
                if (lfsra != null)
                {
                    fileSystemPath = lfsra.LocalFileSystemPath;
                    var ext = ResourcePathHelper.GetExtension(lfsra.LocalFileSystemPath);
                    if (!MKV_EXTENSIONS.Contains(ext))
                    {
                        return;
                    }

                    MatroskaInfoReader mkvReader = new MatroskaInfoReader(lfsra);
                    foreach (string pattern in patterns.Keys)
                    {
                        byte[] binaryData;
                        if (mkvReader.GetAttachmentByName(pattern, out binaryData))
                        {
                            string fanArtType = patterns[pattern];
                            using (FanArtCache.FanArtCountLock countLock = FanArtCache.GetFanArtCountLock(mediaItemId, fanArtType))
                            {
                                if (countLock.Count >= FanArtCache.MAX_FANART_IMAGES[fanArtType])
                                {
                                    return;
                                }

                                FanArtCache.InitFanArtCache(mediaItemId, series.ToString());
                                string cacheFile = GetCacheFileName(mediaItemId, fanArtType, "File." + pattern + Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath) + ".jpg");
                                if (!File.Exists(cacheFile))
                                {
                                    using (MemoryStream ms = new MemoryStream(binaryData))
                                    {
                                        using (Image img = Image.FromStream(ms, true, true))
                                        {
                                            img.Save(cacheFile, System.Drawing.Imaging.ImageFormat.Jpeg);
                                            countLock.Count++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Warn("SeriesFanArtHandler: Exception while reading mkv attachments from '{0}'", ex, fileSystemPath);
            }
        }
コード例 #20
0
        public bool TryGetFanArt(string mediaType, string fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <FanArtImage> result)
        {
            result = null;

            if (mediaType != FanArtMediaTypes.Episode && mediaType != FanArtMediaTypes.Movie)
            {
                return(false);
            }

            Guid mediaItemId;

            if (!Guid.TryParse(name, out mediaItemId))
            {
                return(false);
            }

            IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false);

            if (mediaLibrary == null)
            {
                return(false);
            }

            IFilter           filter = new MediaItemIdFilter(mediaItemId);
            IList <MediaItem> items  = mediaLibrary.Search(new MediaItemQuery(NECESSARY_MIAS, filter), false, null, true);

            if (items == null || items.Count == 0)
            {
                return(false);
            }

            MediaItem mediaItem = items.First();

            // Virtual resources won't have any local fanart
            if (mediaItem.IsVirtual)
            {
                return(false);
            }
            var            resourceLocator = mediaItem.GetResourceLocator();
            string         fileSystemPath  = string.Empty;
            IList <string> patterns        = new List <string>();

            switch (fanArtType)
            {
            case FanArtTypes.Banner:
                patterns.Add("banner.");
                break;

            case FanArtTypes.ClearArt:
                patterns.Add("clearart.");
                break;

            case FanArtTypes.Undefined:
            case FanArtTypes.Poster:
            case FanArtTypes.Thumbnail:
                patterns.Add("cover.");
                patterns.Add("poster.");
                patterns.Add("folder.");
                break;

            case FanArtTypes.FanArt:
                patterns.Add("backdrop.");
                patterns.Add("fanart.");
                break;

            case FanArtTypes.Logo:
                patterns.Add("clearlogo.");
                break;

            default:
                return(false);
            }
            // File based access
            try
            {
                using (var accessor = resourceLocator?.CreateAccessor())
                {
                    ILocalFsResourceAccessor fsra = accessor as ILocalFsResourceAccessor;
                    if (fsra != null)
                    {
                        var ext = Path.GetExtension(fsra.LocalFileSystemPath);
                        if (!SUPPORTED_EXTENSIONS.Contains(ext))
                        {
                            return(false);
                        }

                        MatroskaBinaryReader mkvReader = new MatroskaBinaryReader(fsra);
                        foreach (string pattern in patterns)
                        {
                            byte[] binaryData = mkvReader.GetAttachmentByNameAsync(pattern).Result;
                            if (binaryData != null)
                            {
                                result = new List <FanArtImage> {
                                    new FanArtImage(name, binaryData)
                                };
                                return(true);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Warn("MkvAttachmentsProvider: Exception while reading mkv attachment of type '{0}' from '{1}'", ex, fanArtType, fileSystemPath);
            }
            return(false);
        }
コード例 #21
0
    protected void ExtractMatroskaTags(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode)
    {
      // Calling EnsureLocalFileSystemAccess not necessary; only string operation
      string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(lfsra.LocalFileSystemPath)).ToLower();
      if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
        return;

      // Try to get extended information out of matroska files)
      MatroskaInfoReader mkvReader = new MatroskaInfoReader(lfsra);
      // Add keys to be extracted to tags dictionary, matching results will returned as value
      Dictionary<string, IList<string>> tagsToExtract = MatroskaConsts.DefaultTags;
      mkvReader.ReadTags(tagsToExtract);

      // Read title
      string title = string.Empty;
      IList<string> tags = tagsToExtract[MatroskaConsts.TAG_SIMPLE_TITLE];
      if (tags != null)
        title = tags.FirstOrDefault();
      if (!string.IsNullOrEmpty(title))
        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);

      // Read release date
      int year;
      string yearCandidate = null;
      tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_YEAR] ?? tagsToExtract[MatroskaConsts.TAG_SEASON_YEAR];
      if (tags != null)
        yearCandidate = (tags.FirstOrDefault() ?? string.Empty).Substring(0, 4);

      if (int.TryParse(yearCandidate, out year))
        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));

      // Read plot
      tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
      string plot = tags != null ? tags.FirstOrDefault() : string.Empty;
      if (!string.IsNullOrEmpty(plot))
        MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, plot);

      // Read genre
      tags = tagsToExtract[MatroskaConsts.TAG_SERIES_GENRE];
      if (tags != null)
        MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_GENRES, tags);

      // Read actors
      IEnumerable<string> actors;
      // Combine series actors and episode actors if both are available
      var tagSeriesActors = tagsToExtract[MatroskaConsts.TAG_SERIES_ACTORS];
      var tagActors = tagsToExtract[MatroskaConsts.TAG_ACTORS];
      if (tagSeriesActors != null && tagActors != null)
        actors = tagSeriesActors.Union(tagActors);
      else
        actors = tagSeriesActors ?? tagActors;

      if (actors != null)
        MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, actors);

      tags = tagsToExtract[MatroskaConsts.TAG_DIRECTORS];
      if (tags != null)
        MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_DIRECTORS, tags);

      tags = tagsToExtract[MatroskaConsts.TAG_WRITTEN_BY];
      if (tags != null)
        MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_WRITERS, tags);
    }
コード例 #22
0
        private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID))
            {
                return(false);
            }

            byte[] thumb;
            // We only want to create missing thumbnails here, so check for existing ones first
            if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
            {
                return(false);
            }

            //ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: Evaluate {0}", lfsra.ResourceName);

            bool isPrimaryResource = false;
            IList <MultipleMediaItemAspect> resourceAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out resourceAspects))
            {
                foreach (MultipleMediaItemAspect pra in resourceAspects)
                {
                    string       accessorPath = (string)pra.GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH);
                    ResourcePath resourcePath = ResourcePath.Deserialize(accessorPath);
                    if (resourcePath.Equals(lfsra.CanonicalLocalResourcePath))
                    {
                        if (pra.GetAttributeValue <bool?>(ProviderResourceAspect.ATTR_PRIMARY) == true)
                        {
                            isPrimaryResource = true;
                            break;
                        }
                    }
                }
            }

            if (!isPrimaryResource) //Ignore subtitles
            {
                return(false);
            }

            // Check for a reasonable time offset
            long   defaultVideoOffset = 720;
            long   videoDuration;
            string downscale = ",scale=iw/2:-1"; // Reduces the video frame size to a half of original
            IList <MultipleMediaItemAspect> videoAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoStreamAspect.Metadata, out videoAspects))
            {
                if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0)
                {
                    if (defaultVideoOffset > videoDuration * 1 / 3)
                    {
                        defaultVideoOffset = videoDuration * 1 / 3;
                    }
                }

                int videoWidth = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_WIDTH);
                // Don't downscale SD video frames, quality is already quite low.
                if (videoWidth > 0 && videoWidth <= 720)
                {
                    downscale = "";
                }
            }

            // ToDo: Move creation of temp file names to FileUtils class
            string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
            string executable   = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
            string arguments    = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
                                                defaultVideoOffset,
                                                // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
                                                lfsra.LocalFileSystemPath,
                                                tempFileName,
                                                downscale);

            //ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: FFMpeg {0} {1}", executable, arguments);

            try
            {
                Task <ProcessExecutionResult> executionResult = null;
                FFMPEG_THROTTLE_LOCK.Wait();
                executionResult = FFMpegBinary.FFMpegExecuteWithResourceAccessAsync(lfsra, arguments, ProcessPriorityClass.BelowNormal, PROCESS_TIMEOUT_MS);
                if (executionResult.Result.Success && File.Exists(tempFileName))
                {
                    var binary = FileUtils.ReadFile(tempFileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
                else
                {
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                    ServiceRegistration.Get <ILogger>().Debug("VideoThumbnailer: FFMpeg failure {0} dump:\n{1}", executionResult.Result.ExitCode, executionResult.Result.StandardError);
                }
            }
            catch (AggregateException ae)
            {
                ae.Handle(e =>
                {
                    if (e is TaskCanceledException)
                    {
                        ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer.ExtractThumbnail: External process aborted due to timeout: Executable='{0}', Arguments='{1}', Timeout='{2}'", executable, arguments, PROCESS_TIMEOUT_MS);
                        return(true);
                    }
                    return(false);
                });
            }
            finally
            {
                FFMPEG_THROTTLE_LOCK.Release();

                try
                {
                    if (File.Exists(tempFileName))
                    {
                        File.Delete(tempFileName);
                    }
                }
                catch { }
            }
            return(true);
        }
コード例 #23
0
ファイル: SeriesMatcher.cs プロジェクト: aspik/MediaPortal-2
 /// <summary>
 /// Tries to match series by checking the <paramref name="folderOrFileLfsra"/> for known patterns. The match is only successful,
 /// if the <see cref="SeriesInfo.IsCompleteMatch"/> is <c>true</c>.
 /// </summary>
 /// <param name="folderOrFileLfsra"><see cref="ILocalFsResourceAccessor"/> to file</param>
 /// <param name="seriesInfo">Returns the parsed SeriesInfo</param>
 /// <returns><c>true</c> if successful.</returns>
 public bool MatchSeries(ILocalFsResourceAccessor folderOrFileLfsra, out SeriesInfo seriesInfo)
 {
   return MatchSeries(folderOrFileLfsra.LocalFileSystemPath, out seriesInfo);
 }
コード例 #24
0
        protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            if (extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID) && !importOnly)
            {
                return(false); //Subtitles can only be imported not refreshed
            }
            bool refresh = false;

            if (extractedAspectData.ContainsKey(EpisodeAspect.ASPECT_ID))
            {
                refresh = true;
            }

            EpisodeInfo episodeInfo = new EpisodeInfo();

            if (refresh)
            {
                episodeInfo.FromMetadata(extractedAspectData);
            }
            ISeriesRelationshipExtractor.UpdateEpisodeSeries(extractedAspectData, episodeInfo);
            if (!episodeInfo.IsBaseInfoPresent)
            {
                string title = null;
                int    seasonNumber;
                SingleMediaItemAspect episodeAspect;
                MediaItemAspect.TryGetAspect(extractedAspectData, EpisodeAspect.Metadata, out episodeAspect);
                IEnumerable <int> episodeNumbers;
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SERIES_NAME, out title) &&
                    MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SEASON, out seasonNumber) &&
                    (episodeNumbers = episodeAspect.GetCollectionAttribute <int>(EpisodeAspect.ATTR_EPISODE)) != null)
                {
                    episodeInfo.SeriesName   = title;
                    episodeInfo.SeasonNumber = seasonNumber;
                    episodeInfo.EpisodeNumbers.Clear();
                    episodeNumbers.ToList().ForEach(n => episodeInfo.EpisodeNumbers.Add(n));
                }
            }

            // If there was no complete match, yet, try to get extended information out of matroska files)
            if (!episodeInfo.IsBaseInfoPresent || !episodeInfo.HasExternalId)
            {
                try
                {
                    MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
                    if (matroskaMatcher.MatchSeries(lfsra, episodeInfo))
                    {
                        ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found EpisodeInfo by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, TMDB {3}, AreReqiredFieldsFilled {4}",
                                                                  episodeInfo.SeriesName, episodeInfo.SeriesImdbId, episodeInfo.SeriesTvdbId, episodeInfo.SeriesMovieDbId, episodeInfo.IsBaseInfoPresent);
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Exception reading matroska tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            // If no information was found before, try name matching
            if (!episodeInfo.IsBaseInfoPresent)
            {
                // Try to match series from folder and file naming
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, episodeInfo);
            }

            //Prepare online search improvements
            if (episodeInfo.SeriesFirstAired == null)
            {
                EpisodeInfo   tempEpisodeInfo = new EpisodeInfo();
                SeriesMatcher seriesMatcher   = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, tempEpisodeInfo);
                if (tempEpisodeInfo.SeriesFirstAired.HasValue)
                {
                    episodeInfo.SeriesFirstAired = tempEpisodeInfo.SeriesFirstAired;
                }
            }
            if (string.IsNullOrEmpty(episodeInfo.SeriesAlternateName))
            {
                var mediaItemPath = lfsra.CanonicalLocalResourcePath;
                var seriesMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                episodeInfo.SeriesAlternateName = seriesMediaItemDirectoryPath.FileName;
            }

            if (episodeInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !episodeInfo.Languages.Contains(language))
                        {
                            episodeInfo.Languages.Add(language);
                        }
                    }
                }
            }

            episodeInfo.AssignNameId();

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                EpisodeInfo tempInfo = episodeInfo.Clone();
                OnlineMatcherService.Instance.FindAndUpdateEpisode(tempInfo, importOnly);
                episodeInfo.CopyIdsFrom(tempInfo);
                episodeInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.FindAndUpdateEpisode(episodeInfo, importOnly);
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !episodeInfo.HasExternalId)
            {
                return(false);
            }

            if (refresh)
            {
                if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && episodeInfo.Actors.Count > 0) ||
                    (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && episodeInfo.Characters.Count > 0) ||
                    (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && episodeInfo.Directors.Count > 0) ||
                    (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && episodeInfo.Writers.Count > 0) ||
                    (!BaseInfo.HasRelationship(extractedAspectData, SeriesAspect.ROLE_SERIES) && !episodeInfo.SeriesName.IsEmpty) ||
                    (!BaseInfo.HasRelationship(extractedAspectData, SeasonAspect.ROLE_SEASON) && episodeInfo.SeasonNumber.HasValue))
                {
                    episodeInfo.HasChanged = true;
                }
            }

            if (!episodeInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            episodeInfo.SetMetadata(extractedAspectData);

            return(episodeInfo.IsBaseInfoPresent);
        }
コード例 #25
0
        /// <summary>
        /// Tries to match series by reading matroska tags from <paramref name="folderOrFileLfsra"/>.
        /// </summary>
        /// <param name="folderOrFileLfsra"><see cref="ILocalFsResourceAccessor"/> to file or folder</param>
        /// <param name="episodeInfo">Returns the parsed EpisodeInfo</param>
        /// <param name="extractedAspectData">Dictionary containing a mapping of media item aspect ids to
        /// already present media item aspects, this metadata extractor should edit. If a media item aspect is not present
        /// in this dictionary but found by this metadata extractor, it will add it to the dictionary.</param>
        /// <returns><c>true</c> if successful.</returns>
        public async Task <bool> MatchSeriesAsync(ILocalFsResourceAccessor folderOrFileLfsra, EpisodeInfo episodeInfo)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(folderOrFileLfsra.LocalFileSystemPath)).ToLower();

            if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
            {
                return(false);
            }

            MatroskaBinaryReader mkvReader = new MatroskaBinaryReader(folderOrFileLfsra);
            // Add keys to be extracted to tags dictionary, matching results will returned as value
            Dictionary <string, IList <string> > tagsToExtract = MatroskaConsts.DefaultVideoTags;
            await mkvReader.ReadTagsAsync(tagsToExtract).ConfigureAwait(false);

            IList <string> tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
            string         plot = tags != null?tags.FirstOrDefault() : string.Empty;

            if (!string.IsNullOrEmpty(plot))
            {
                episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateString(ref episodeInfo.Summary, plot, true);
            }

            // Series and episode handling. Prefer information from tags.
            if (tagsToExtract[MatroskaConsts.TAG_EPISODE_TITLE] != null)
            {
                string title = tagsToExtract[MatroskaConsts.TAG_EPISODE_TITLE].FirstOrDefault();
                if (!string.IsNullOrEmpty(title))
                {
                    title = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(title);
                    episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateString(ref episodeInfo.EpisodeName, title, true);
                }
            }

            if (tagsToExtract[MatroskaConsts.TAG_SERIES_TITLE] != null)
            {
                string title = tagsToExtract[MatroskaConsts.TAG_SERIES_TITLE].FirstOrDefault();
                if (!string.IsNullOrEmpty(title))
                {
                    title = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(title);
                    episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateString(ref episodeInfo.SeriesName, title, true);
                }
            }

            if (tagsToExtract[MatroskaConsts.TAG_SERIES_IMDB_ID] != null)
            {
                string imdbId;
                foreach (string candidate in tagsToExtract[MatroskaConsts.TAG_SERIES_IMDB_ID])
                {
                    if (ImdbIdMatcher.TryMatchImdbId(candidate, out imdbId))
                    {
                        episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateId(ref episodeInfo.SeriesImdbId, imdbId);
                        break;
                    }
                }
            }

            if (tagsToExtract[MatroskaConsts.TAG_SERIES_ACTORS] != null)
            {
                episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateList(episodeInfo.Actors,
                                                                          tagsToExtract[MatroskaConsts.TAG_SERIES_ACTORS].Select(t => new PersonInfo()
                {
                    Name      = t, Occupation = PersonAspect.OCCUPATION_ACTOR,
                    MediaName = episodeInfo.EpisodeName.Text, ParentMediaName = episodeInfo.SeriesName.Text
                }).ToList(), false);
            }

            // On Series, the counting tag is "TVDB"
            if (tagsToExtract[MatroskaConsts.TAG_SERIES_TVDB_ID] != null)
            {
                int tmp;
                foreach (string candidate in tagsToExtract[MatroskaConsts.TAG_SERIES_TVDB_ID])
                {
                    if (int.TryParse(candidate, out tmp) == true)
                    {
                        episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateId(ref episodeInfo.SeriesTvdbId, tmp);
                        break;
                    }
                }
            }

            int tmpInt;

            if (tagsToExtract[MatroskaConsts.TAG_SEASON_NUMBER] != null && int.TryParse(tagsToExtract[MatroskaConsts.TAG_SEASON_NUMBER].FirstOrDefault(), out tmpInt))
            {
                episodeInfo.HasChanged |= MetadataUpdater.SetOrUpdateValue(ref episodeInfo.SeasonNumber, tmpInt);
            }

            if (tagsToExtract[MatroskaConsts.TAG_EPISODE_NUMBER] != null)
            {
                int episodeNum;

                foreach (string s in tagsToExtract[MatroskaConsts.TAG_EPISODE_NUMBER])
                {
                    if (int.TryParse(s, out episodeNum))
                    {
                        if (!episodeInfo.EpisodeNumbers.Contains(episodeNum))
                        {
                            episodeInfo.EpisodeNumbers.Add(episodeNum);
                        }
                    }
                }
            }

            return(true);
        }
コード例 #26
0
 /// <summary>
 /// Tries to match series by checking the <paramref name="folderOrFileLfsra"/> for known patterns. The match is only successful,
 /// if the <see cref="EpisodeInfo.IsBaseInfoPresent"/> is <c>true</c>.
 /// </summary>
 /// <param name="folderOrFileLfsra"><see cref="ILocalFsResourceAccessor"/> to file</param>
 /// <param name="episodeInfo">Returns the parsed EpisodeInfo</param>
 /// <returns><c>true</c> if successful.</returns>
 public bool MatchSeries(ILocalFsResourceAccessor folderOrFileLfsra, EpisodeInfo episodeInfo)
 {
     return(MatchSeries(folderOrFileLfsra.LocalFileSystemPath, episodeInfo));
 }
コード例 #27
0
        private async Task <bool> ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
            string   title       = null;
            string   sortTitle   = null;
            bool     isReimport  = extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID);

            MovieInfo movieInfo = new MovieInfo();

            if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID))
            {
                movieInfo.FromMetadata(extractedAspectData);
            }

            if (movieInfo.MovieName.IsEmpty)
            {
                //Try to get title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) &&
                    !string.IsNullOrEmpty(title) && !lfsra.ResourceName.StartsWith(title, StringComparison.InvariantCultureIgnoreCase))
                {
                    //The title may still contain tags and other noise, try and parse it for a title and year.
                    MovieNameMatcher.MatchTitleYear(title, movieInfo);
                }
            }
            if (movieInfo.MovieNameSort.IsEmpty)
            {
                //Try to get sort title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, out sortTitle) && !string.IsNullOrEmpty(sortTitle))
                {
                    movieInfo.MovieNameSort = sortTitle;
                }
            }

            if (!isReimport) //Ignore tags or file based information for reimport because they might be the cause of the wrong import
            {
                if (movieInfo.MovieDbId == 0)
                {
                    try
                    {
                        // Try to use an existing TMDB id for exact mapping
                        string tmdbId = await MatroskaMatcher.TryMatchTmdbIdAsync(lfsra).ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(tmdbId))
                        {
                            movieInfo.MovieDbId = Convert.ToInt32(tmdbId);
                        }
                    }
                    catch (Exception ex)
                    {
                        ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading TMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                    }
                }

                if (string.IsNullOrEmpty(movieInfo.ImdbId))
                {
                    try
                    {
                        // Try to use an existing IMDB id for exact mapping
                        string imdbId = await MatroskaMatcher.TryMatchImdbIdAsync(lfsra).ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(imdbId))
                        {
                            movieInfo.ImdbId = imdbId;
                        }
                        else if (pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId)))
                        {
                            movieInfo.ImdbId = imdbId;
                        }
                    }
                    catch (Exception ex)
                    {
                        ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading IMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                    }
                }

                if (!movieInfo.IsBaseInfoPresent || !movieInfo.ReleaseDate.HasValue)
                {
                    // Also test the full path year. This is useful if the path contains the real name and year.
                    foreach (string path in pathsToTest)
                    {
                        if (MovieNameMatcher.MatchTitleYear(path, movieInfo))
                        {
                            break;
                        }
                    }
                    //Fall back to MediaAspect.ATTR_TITLE
                    if (movieInfo.MovieName.IsEmpty && !string.IsNullOrEmpty(title))
                    {
                        movieInfo.MovieName = title;
                    }

                    /* Clear the names from unwanted strings */
                    MovieNameMatcher.CleanupTitle(movieInfo);
                }

                if (!movieInfo.ReleaseDate.HasValue && !movieInfo.HasExternalId)
                {
                    // When searching movie title, the year can be relevant for multiple titles with same name but different years
                    DateTime recordingDate;
                    if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate))
                    {
                        movieInfo.ReleaseDate = recordingDate;
                    }
                }

                try
                {
                    await MatroskaMatcher.ExtractFromTagsAsync(lfsra, movieInfo).ConfigureAwait(false);

                    MP4Matcher.ExtractFromTags(lfsra, movieInfo);
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            // Allow the online lookup to choose best matching language for metadata
            if (movieInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !movieInfo.Languages.Contains(language))
                        {
                            movieInfo.Languages.Add(language);
                        }
                    }
                }
            }

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                MovieInfo tempInfo = movieInfo.Clone();
                if (await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(tempInfo).ConfigureAwait(false))
                {
                    movieInfo.CopyIdsFrom(tempInfo);
                    movieInfo.HasChanged = tempInfo.HasChanged;
                }
            }
            else if (!SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(movieInfo).ConfigureAwait(false);
            }

            //Asign genre ids
            if (movieInfo.Genres.Count > 0)
            {
                IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                foreach (var genre in movieInfo.Genres)
                {
                    if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                    {
                        genre.Id             = genreId;
                        movieInfo.HasChanged = true;
                    }
                }
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !movieInfo.HasExternalId)
            {
                return(false);
            }

            //Create custom collection (overrides online collection)
            MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>();
            string collectionName;

            if (string.IsNullOrEmpty(collectionInfo.NameId) && CollectionFolderHasFanArt(lfsra, out collectionName))
            {
                collectionInfo = new MovieCollectionInfo();
                collectionInfo.CollectionName = collectionName;
                if (!collectionInfo.CollectionName.IsEmpty)
                {
                    movieInfo.CollectionName = collectionInfo.CollectionName;
                    movieInfo.CopyIdsFrom(collectionInfo); //Reset ID's
                    movieInfo.HasChanged = true;
                }
            }

            if (movieInfo.MovieNameSort.IsEmpty)
            {
                if (!movieInfo.CollectionName.IsEmpty && movieInfo.ReleaseDate.HasValue)
                {
                    movieInfo.MovieNameSort = $"{movieInfo.CollectionName.Text} {movieInfo.ReleaseDate.Value.Year}-{movieInfo.ReleaseDate.Value.Month.ToString("00")}";
                }
                else if (!movieInfo.MovieName.IsEmpty)
                {
                    movieInfo.MovieNameSort = BaseInfo.GetSortTitle(movieInfo.MovieName.Text);
                }
                else
                {
                    movieInfo.MovieNameSort = BaseInfo.GetSortTitle(title);
                }
            }
            movieInfo.SetMetadata(extractedAspectData);

            return(movieInfo.IsBaseInfoPresent);
        }
コード例 #28
0
        protected async Task <bool> ExtractSeriesDataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            bool isReimport = extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID);

            EpisodeInfo episodeInfo = new EpisodeInfo();

            episodeInfo.FromMetadata(extractedAspectData);

            if (!isReimport) //Ignore file based information for reimports because they might be the cause of the wrong match
            {
                // If there was no complete match, yet, try to get extended information out of matroska files)
                if (!episodeInfo.IsBaseInfoPresent || !episodeInfo.HasExternalId)
                {
                    try
                    {
                        MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
                        if (await matroskaMatcher.MatchSeriesAsync(lfsra, episodeInfo).ConfigureAwait(false))
                        {
                            ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found EpisodeInfo by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, TMDB {3}, AreReqiredFieldsFilled {4}",
                                                                      episodeInfo.SeriesName, episodeInfo.SeriesImdbId, episodeInfo.SeriesTvdbId, episodeInfo.SeriesMovieDbId, episodeInfo.IsBaseInfoPresent);
                        }
                    }
                    catch (Exception ex)
                    {
                        ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Exception reading matroska tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                    }
                }

                // If no information was found before, try name matching
                if (!episodeInfo.IsBaseInfoPresent)
                {
                    // Try to match series from folder and file naming
                    SeriesMatcher seriesMatcher = new SeriesMatcher();
                    seriesMatcher.MatchSeries(lfsra, episodeInfo);
                }

                //Prepare online search improvements
                if (episodeInfo.SeriesFirstAired == null)
                {
                    EpisodeInfo   tempEpisodeInfo = new EpisodeInfo();
                    SeriesMatcher seriesMatcher   = new SeriesMatcher();
                    seriesMatcher.MatchSeries(lfsra, tempEpisodeInfo);
                    if (tempEpisodeInfo.SeriesFirstAired.HasValue)
                    {
                        episodeInfo.SeriesFirstAired = tempEpisodeInfo.SeriesFirstAired;
                    }
                }
                if (string.IsNullOrEmpty(episodeInfo.SeriesAlternateName))
                {
                    var mediaItemPath = lfsra.CanonicalLocalResourcePath;
                    var seriesMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                    episodeInfo.SeriesAlternateName = seriesMediaItemDirectoryPath.FileName;
                }
            }

            if (episodeInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !episodeInfo.Languages.Contains(language))
                        {
                            episodeInfo.Languages.Add(language);
                        }
                    }
                }
            }

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                EpisodeInfo tempInfo = episodeInfo.Clone();
                await OnlineMatcherService.Instance.FindAndUpdateEpisodeAsync(tempInfo).ConfigureAwait(false);

                episodeInfo.CopyIdsFrom(tempInfo);
                episodeInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.FindAndUpdateEpisodeAsync(episodeInfo).ConfigureAwait(false);
            }

            if (episodeInfo.EpisodeName.IsEmpty)
            {
                if (episodeInfo.EpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeName = $"E{episodeInfo.EpisodeNumbers.First().ToString("000")}";
                }
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !episodeInfo.HasExternalId)
            {
                return(false);
            }

            if (episodeInfo.EpisodeNameSort.IsEmpty)
            {
                if (!episodeInfo.SeriesName.IsEmpty && episodeInfo.SeasonNumber.HasValue && episodeInfo.DvdEpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeNameSort = $"{episodeInfo.SeriesName.Text} S{episodeInfo.SeasonNumber.Value.ToString("00")}E{episodeInfo.DvdEpisodeNumbers.First().ToString("000.000")}";
                }
                if (!episodeInfo.SeriesName.IsEmpty && episodeInfo.SeasonNumber.HasValue && episodeInfo.EpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeNameSort = $"{episodeInfo.SeriesName.Text} S{episodeInfo.SeasonNumber.Value.ToString("00")}E{episodeInfo.EpisodeNumbers.First().ToString("000")}";
                }
                else if (!episodeInfo.EpisodeName.IsEmpty)
                {
                    episodeInfo.EpisodeNameSort = BaseInfo.GetSortTitle(episodeInfo.EpisodeName.Text);
                }
            }
            episodeInfo.SetMetadata(extractedAspectData);

            return(episodeInfo.IsBaseInfoPresent);
        }
コード例 #29
0
        protected virtual Task <bool> ExtractMetadataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            if (!CanExtract(lfsra, extractedAspectData))
            {
                return(Task.FromResult(false));
            }

            using (var rec = new MCRecMetadataEditor(lfsra.LocalFileSystemPath))
            {
                // Handle series information
                IDictionary tags = rec.GetAttributes();

                // Force MimeType
                IList <MultipleMediaItemAspect> providerAspects;
                MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects);
                foreach (MultipleMediaItemAspect aspect in providerAspects)
                {
                    aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/wtv");
                }

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                string value;
                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))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo {
                        Name = s.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;
                        }
                    }
                    foreach (GenreInfo genre in genreList)
                    {
                        MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                        genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id);
                        genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name);
                    }
                }

                if (TryGet(tags, TAG_PLOT, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value);
                }

                if (TryGet(tags, TAG_ORIGINAL_TIME, out value))
                {
                    DateTime origTime;
                    if (DateTime.TryParse(value, out origTime))
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, origTime);
                    }
                }

                if (TryGet(tags, TAG_CHANNEL, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value);
                }

                long lValue;
                if (TryGet(tags, TAG_STARTTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, FromMCEFileTime(lValue));
                }
                if (TryGet(tags, TAG_ENDTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, FromMCEFileTime(lValue));
                }
            }
            return(Task.FromResult(true));
        }
コード例 #30
0
        private static bool ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
            string   title;

            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            if (!MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) || string.IsNullOrEmpty(title))
            {
                return(false);
            }

            MovieInfo movieInfo = new MovieInfo
            {
                MovieName = title,
            };

            // Allow the online lookup to choose best matching language for metadata
            ICollection <string> movieLanguages;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_AUDIOLANGUAGES, out movieLanguages) && movieLanguages.Count > 0)
            {
                movieInfo.Languages.AddRange(movieLanguages);
            }

            // Try to use an existing IMDB id for exact mapping
            string imdbId;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, MovieAspect.ATTR_IMDB_ID, out imdbId) ||
                pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId)) ||
                NfoReader.TryMatchImdbId(lfsra, out imdbId) ||
                MatroskaMatcher.TryMatchImdbId(lfsra, out imdbId))
            {
                movieInfo.ImdbId = imdbId;
            }

            // Also test the full path year, using a dummy. This is useful if the path contains the real name and year.
            foreach (string path in pathsToTest)
            {
                MovieInfo dummy = new MovieInfo {
                    MovieName = path
                };
                if (NamePreprocessor.MatchTitleYear(dummy))
                {
                    movieInfo.MovieName = dummy.MovieName;
                    movieInfo.Year      = dummy.Year;
                    break;
                }
            }

            // When searching movie title, the year can be relevant for multiple titles with same name but different years
            DateTime recordingDate;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate))
            {
                movieInfo.Year = recordingDate.Year;
            }

            if (MovieTheMovieDbMatcher.Instance.FindAndUpdateMovie(movieInfo))
            {
                movieInfo.SetMetadata(extractedAspectData);
                return(true);
            }
            return(false);
        }
コード例 #31
0
        public bool TryGetFanArt(string mediaType, string fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <FanArtImage> result)
        {
            result = null;

            if ((mediaType != FanArtMediaTypes.Episode && mediaType != FanArtMediaTypes.Movie) || (fanArtType != FanArtTypes.Thumbnail && fanArtType != FanArtTypes.Undefined))
            {
                return(false);
            }

            Guid mediaItemId;

            if (!Guid.TryParse(name, out mediaItemId))
            {
                return(false);
            }

            IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false);

            if (mediaLibrary == null)
            {
                return(false);
            }

            IFilter           filter = new MediaItemIdFilter(mediaItemId);
            IList <MediaItem> items  = mediaLibrary.Search(new MediaItemQuery(NECESSARY_MIAS, filter), false, null, true);

            if (items == null || items.Count == 0)
            {
                return(false);
            }

            MediaItem mediaItem = items.First();

            // Virtual resources won't have any local fanart
            if (mediaItem.IsVirtual)
            {
                return(false);
            }
            var    resourceLocator = mediaItem.GetResourceLocator();
            string fileSystemPath  = string.Empty;

            // File based access
            try
            {
                using (var accessor = resourceLocator?.CreateAccessor())
                {
                    ILocalFsResourceAccessor lfsra = accessor as ILocalFsResourceAccessor;
                    if (lfsra != null)
                    {
                        // Check for a reasonable time offset
                        int    defaultVideoOffset = 720;
                        long   videoDuration;
                        double width     = 0;
                        double height    = 0;
                        double downscale = 7.5; // Reduces the HD video frame size to a quarter size to around 256
                        IList <MultipleMediaItemAspect> videoAspects;
                        if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, VideoStreamAspect.Metadata, out videoAspects))
                        {
                            if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0)
                            {
                                if (defaultVideoOffset > videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET)
                                {
                                    defaultVideoOffset = Convert.ToInt32(videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET);
                                }
                            }

                            width     = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_WIDTH);
                            height    = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_HEIGHT);
                            downscale = width / 256.0; //256 is max size of large thumbnail aspect
                        }

                        var sw = Stopwatch.StartNew();
                        using (VideoCapture capture = new VideoCapture())
                        {
                            capture.Open(lfsra.LocalFileSystemPath);
                            int capturePos = defaultVideoOffset * 1000;
                            if (capture.FrameCount > 0 && capture.Fps > 0)
                            {
                                var duration = capture.FrameCount / capture.Fps;
                                if (defaultVideoOffset > duration)
                                {
                                    capturePos = Convert.ToInt32(duration * DEFAULT_OPENCV_THUMBNAIL_OFFSET * 1000);
                                }
                            }

                            if (capture.FrameWidth > 0)
                            {
                                downscale = capture.FrameWidth / MAX_THUMBNAIL_WIDTH;
                            }

                            capture.PosMsec = capturePos;
                            using (var mat = capture.RetrieveMat())
                            {
                                if (mat.Height > 0 && mat.Width > 0)
                                {
                                    width  = mat.Width;
                                    height = mat.Height;
                                    using (var scaledMat = mat.Resize(new OpenCvSharp.Size(width / downscale, height / downscale)))
                                    {
                                        var binary = scaledMat.ToBytes();
                                        result = new List <FanArtImage> {
                                            new FanArtImage(name, binary)
                                        };
                                        ServiceRegistration.Get <ILogger>().Debug("OpenCvFanartProvider: Successfully extracted thumbnail for resource '{0}' ({1} ms)", lfsra.LocalFileSystemPath, sw.ElapsedMilliseconds);
                                        return(true);
                                    }
                                }
                                else
                                {
                                    ServiceRegistration.Get <ILogger>().Warn("OpenCvFanartProvider: Failed to extract thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Warn("OpenCvFanartProvider: Exception while reading thumbnail of type '{0}' from '{1}'", ex, fanArtType, fileSystemPath);
            }
            return(false);
        }
コード例 #32
0
        private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            byte[] thumb;
            // We only want to create missing thumbnails here, so check for existing ones first
            if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
            {
                return(true);
            }

            // Check for a reasonable time offset
            long defaultVideoOffset = 720;
            long videoDuration;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
            {
                if (defaultVideoOffset > videoDuration * 1 / 3)
                {
                    defaultVideoOffset = videoDuration * 1 / 3;
                }
            }

            string downscale = ",scale=iw/2:-1"; // Reduces the video frame size to a half of original

            int videoWidth;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_WIDTH, out videoWidth))
            {
                // Don't downscale SD video frames, quality is already quite low.
                if (videoWidth > 0 && videoWidth <= 720)
                {
                    downscale = "";
                }
            }

            // ToDo: Move creation of temp file names to FileUtils class
            string tempFileName = Path.GetTempPath() + Guid.NewGuid() + ".jpg";
            string executable   = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
            string arguments    = string.Format("-ss {0} -i \"{1}\" -vframes 1 -an -dn -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1/1{3}\" -y \"{2}\"",
                                                defaultVideoOffset,
                                                // Calling EnsureLocalFileSystemAccess not necessary; access for external process ensured by ExecuteWithResourceAccess
                                                lfsra.LocalFileSystemPath,
                                                tempFileName,
                                                downscale);

            try
            {
                bool success;
                lock (FFMPEG_THROTTLE_LOCK)
                    success = lfsra.ExecuteWithResourceAccessAsync(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS).Result.Success;
                if (success && File.Exists(tempFileName))
                {
                    var binary = FileUtils.ReadFile(tempFileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
                else
                {
                    // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                    ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                }
            }
            catch (AggregateException ae)
            {
                ae.Handle(e =>
                {
                    if (e is TaskCanceledException)
                    {
                        ServiceRegistration.Get <ILogger>().Warn("VideoThumbnailer.ExtractThumbnail: External process aborted due to timeout: Executable='{0}', Arguments='{1}', Timeout='{2}'", executable, arguments, PROCESS_TIMEOUT_MS);
                        return(true);
                    }
                    return(false);
                });
            }
            finally
            {
                if (File.Exists(tempFileName))
                {
                    File.Delete(tempFileName);
                }
            }
            return(true);
        }
コード例 #33
0
        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);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, lfsra.LastChanged);
                            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);
        }
コード例 #34
0
    protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // VideoAspect must be present to be sure it is actually a video resource.
      if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
        return false;

      SeriesInfo seriesInfo;

      // Try to get extended information out of matroska files)
      MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
      if (matroskaMatcher.MatchSeries(lfsra, out seriesInfo, ref extractedAspectData))
      {
        ServiceRegistration.Get<ILogger>().Debug("ExtractSeriesData: Found SeriesInformation by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, IsCompleteMatch {3}",
          seriesInfo.Series, seriesInfo.ImdbId, seriesInfo.TvdbId, seriesInfo.IsCompleteMatch);
      }

      // If no information from mkv were found, try name matching
      if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
      {
        // Try to match series from folder and file namings
        SeriesMatcher seriesMatcher = new SeriesMatcher();
        seriesMatcher.MatchSeries(lfsra, out seriesInfo);
      }

      // Lookup online information (incl. fanart)
      if (seriesInfo != null && seriesInfo.IsCompleteMatch)
      {
        SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
        seriesInfo.SetMetadata(extractedAspectData);
      }
      return (seriesInfo != null && seriesInfo.IsCompleteMatch);
    }
コード例 #35
0
        public override async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <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);
            }
            if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID)) //Ignore for reimports because the tags might be the cause of the wrong match
            {
                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;
                    }
                }

                TrackInfo trackInfo = new TrackInfo();
                if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
                {
                    trackInfo.FromMetadata(extractedAspectData);
                }
                else
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
                    IList <MultipleMediaItemAspect> providerResourceAspect;
                    if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspect))
                    {
                        providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size);
                        // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                        providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "audio/" + Path.GetExtension(fsra.LocalFileSystemPath).Substring(1));
                    }
                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, tags.bitrate);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tags.comment));
                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, (long)tags.duration);
                }

                if (!trackInfo.IsBaseInfoPresent)
                {
                    trackInfo.TrackName = title;
                    trackInfo.Album     = StringUtils.TrimToNull(tags.album);
                    if (trackNo.HasValue)
                    {
                        trackInfo.TrackNum = (int)trackNo.Value;
                    }

                    trackInfo.Artists = new List <PersonInfo>();
                    foreach (string artistName in ApplyAdditionalSeparator(artists))
                    {
                        trackInfo.Artists.Add(new PersonInfo()
                        {
                            Name            = artistName,
                            Occupation      = PersonAspect.OCCUPATION_ARTIST,
                            ParentMediaName = trackInfo.Album,
                            MediaName       = trackInfo.TrackName
                        });
                    }

                    IEnumerable <string> albumArtists = SplitTagEnum(tags.albumartist);
                    albumArtists           = PatchID3v23Enumeration(albumArtists);
                    trackInfo.AlbumArtists = new List <PersonInfo>();
                    foreach (string artistName in ApplyAdditionalSeparator(albumArtists))
                    {
                        trackInfo.AlbumArtists.Add(new PersonInfo()
                        {
                            Name            = artistName,
                            Occupation      = PersonAspect.OCCUPATION_ARTIST,
                            ParentMediaName = trackInfo.Album,
                            MediaName       = trackInfo.TrackName
                        });
                    }

                    IEnumerable <string> composers = SplitTagEnum(tags.composer);
                    composers           = PatchID3v23Enumeration(composers);
                    trackInfo.Composers = new List <PersonInfo>();
                    foreach (string composerName in ApplyAdditionalSeparator(composers))
                    {
                        trackInfo.Composers.Add(new PersonInfo()
                        {
                            Name            = composerName,
                            Occupation      = PersonAspect.OCCUPATION_COMPOSER,
                            ParentMediaName = trackInfo.Album,
                            MediaName       = trackInfo.TrackName
                        });
                    }

                    IEnumerable <string> genres = SplitTagEnum(tags.genre);
                    genres           = PatchID3v23Enumeration(genres);
                    trackInfo.Genres = ApplyAdditionalSeparator(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;
                        }
                    }

                    int year;
                    if (int.TryParse(tags.year, out year))
                    {
                        if (year >= 30 && year <= 99)
                        {
                            year += 1900;
                        }
                        if (year >= 1930 && year <= 2030)
                        {
                            trackInfo.ReleaseDate = new DateTime(year, 1, 1);
                        }
                    }

                    if (!trackInfo.HasThumbnail)
                    {
                        // 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 (MemoryStream result = new MemoryStream())
                                    {
                                        cover.Save(result, ImageFormat.Jpeg);
                                        trackInfo.Thumbnail  = result.ToArray();
                                        trackInfo.HasChanged = true;
                                    }
                            }
                            // Decoding of invalid image data can fail, but main MediaItem is correct.
                            catch { }
                        }
                    }
                }

                if (!SkipOnlineSearches && !forceQuickMode)
                {
                    await OnlineMatcherService.Instance.FindAndUpdateTrackAsync(trackInfo).ConfigureAwait(false);
                }

                if (!trackInfo.HasChanged)
                {
                    return(false);
                }

                trackInfo.SetMetadata(extractedAspectData);

                return(trackInfo.IsBaseInfoPresent);
            }
            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);
        }
コード例 #36
0
        public bool TryGetFanArt(FanArtConstants.FanArtMediaType mediaType, FanArtConstants.FanArtType fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <FanArtImage> result)
        {
            result = null;
            Guid mediaItemId;

            if (!Guid.TryParse(name, out mediaItemId))
            {
                return(false);
            }

            IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false);

            if (mediaLibrary == null)
            {
                return(false);
            }

            IFilter           filter = new MediaItemIdFilter(mediaItemId);
            IList <MediaItem> items  = mediaLibrary.Search(new MediaItemQuery(NECESSARY_MIAS, filter), false);

            if (items == null || items.Count == 0)
            {
                return(false);
            }

            MediaItem mediaItem = items.First();

            string         fileSystemPath = string.Empty;
            IList <string> patterns       = new List <string>();

            switch (fanArtType)
            {
            case FanArtConstants.FanArtType.Banner:
                patterns.Add("banner.");
                break;

            case FanArtConstants.FanArtType.ClearArt:
                patterns.Add("clearart.");
                break;

            case FanArtConstants.FanArtType.Poster:
            case FanArtConstants.FanArtType.Thumbnail:
                patterns.Add("cover.");
                patterns.Add("poster.");
                patterns.Add("folder.");
                break;

            case FanArtConstants.FanArtType.FanArt:
                patterns.Add("backdrop.");
                patterns.Add("fanart.");
                break;

            default:
                return(false);
            }
            // File based access
            try
            {
                var resourceLocator = mediaItem.GetResourceLocator();
                using (var accessor = resourceLocator.CreateAccessor())
                {
                    ILocalFsResourceAccessor fsra = accessor as ILocalFsResourceAccessor;
                    if (fsra != null)
                    {
                        fileSystemPath = fsra.LocalFileSystemPath;
                        var ext = Path.GetExtension(fileSystemPath);
                        if (!SUPPORTED_EXTENSIONS.Contains(ext))
                        {
                            return(false);
                        }

                        MatroskaInfoReader mkvReader  = new MatroskaInfoReader(fileSystemPath);
                        byte[]             binaryData = null;
                        if (patterns.Any(pattern => mkvReader.GetAttachmentByName(pattern, out binaryData)))
                        {
                            result = new List <FanArtImage> {
                                new FanArtImage(name, binaryData)
                            };
                            return(true);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Warn("MkvAttachmentsProvider: Exception while reading mkv attachment of type '{0}' from '{1}'", ex, fanArtType, fileSystemPath);
            }
            return(false);
        }
コード例 #37
0
 /// <summary>
 /// Constructs a new <see cref="MatroskaInfoReader"/>.
 /// </summary>
 /// <param name="lfsra">
 /// <see cref="ILocalFsResourceAccessor"/> pointing to the MKV file to extract information from; The caller
 /// is responsible that it is valid while this class is used and that it is disposed afterwards.
 /// </param>
 public MatroskaInfoReader(ILocalFsResourceAccessor lfsra)
 {
   _lfsra = lfsra;
   _mkvInfoPath = FileUtils.BuildAssemblyRelativePath("mkvinfo.exe");
   _mkvExtractPath = FileUtils.BuildAssemblyRelativePath("mkvextract.exe");
 }
コード例 #38
0
        public static async Task <bool> ExtractFromTagsAsync(ILocalFsResourceAccessor folderOrFileLfsra, MovieInfo movieInfo)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string extensionLower = StringUtils.TrimToEmpty(Path.GetExtension(folderOrFileLfsra.LocalFileSystemPath)).ToLower();

            if (!MatroskaConsts.MATROSKA_VIDEO_EXTENSIONS.Contains(extensionLower))
            {
                return(false);
            }

            // Try to get extended information out of matroska files)
            MatroskaBinaryReader mkvReader = new MatroskaBinaryReader(folderOrFileLfsra);
            // Add keys to be extracted to tags dictionary, matching results will returned as value
            Dictionary <string, IList <string> > tagsToExtract = MatroskaConsts.DefaultVideoTags;
            await mkvReader.ReadTagsAsync(tagsToExtract).ConfigureAwait(false);

            // Read plot
            IList <string> tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
            string         plot = tags != null?tags.FirstOrDefault() : string.Empty;

            if (!string.IsNullOrEmpty(plot))
            {
                movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateString(ref movieInfo.Summary, plot, true);
            }

            // Read genre
            tags = tagsToExtract[MatroskaConsts.TAG_SERIES_GENRE];
            if (tags != null)
            {
                List <GenreInfo> genreList = tags.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo {
                    Name = s.Trim()
                }).ToList();
                movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateList(movieInfo.Genres, genreList, movieInfo.Genres.Count == 0);
            }

            // Read actors
            tags = tagsToExtract[MatroskaConsts.TAG_ACTORS];
            if (tags != null)
            {
                movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateList(movieInfo.Actors,
                                                                        tags.Select(t => new PersonInfo()
                {
                    Name = t, Occupation = PersonAspect.OCCUPATION_ACTOR, MediaName = movieInfo.MovieName.Text
                }).ToList(), false);
            }

            tags = tagsToExtract[MatroskaConsts.TAG_DIRECTORS];
            if (tags != null)
            {
                movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateList(movieInfo.Directors,
                                                                        tags.Select(t => new PersonInfo()
                {
                    Name = t, Occupation = PersonAspect.OCCUPATION_DIRECTOR, MediaName = movieInfo.MovieName.Text
                }).ToList(), false);
            }

            tags = tagsToExtract[MatroskaConsts.TAG_WRITTEN_BY];
            if (tags != null)
            {
                movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateList(movieInfo.Writers,
                                                                        tags.Select(t => new PersonInfo()
                {
                    Name = t, Occupation = PersonAspect.OCCUPATION_WRITER, MediaName = movieInfo.MovieName.Text
                }).ToList(), false);
            }

            if (tagsToExtract[MatroskaConsts.TAG_MOVIE_IMDB_ID] != null)
            {
                string imdbId;
                foreach (string candidate in tagsToExtract[MatroskaConsts.TAG_MOVIE_IMDB_ID])
                {
                    if (ImdbIdMatcher.TryMatchImdbId(candidate, out imdbId))
                    {
                        movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateId(ref movieInfo.ImdbId, imdbId);
                        break;
                    }
                }
            }
            if (tagsToExtract[MatroskaConsts.TAG_MOVIE_TMDB_ID] != null)
            {
                int tmp;
                foreach (string candidate in tagsToExtract[MatroskaConsts.TAG_MOVIE_TMDB_ID])
                {
                    if (int.TryParse(candidate, out tmp) == true)
                    {
                        movieInfo.HasChanged |= MetadataUpdater.SetOrUpdateId(ref movieInfo.MovieDbId, tmp);
                        break;
                    }
                }
            }

            return(true);
        }
コード例 #39
0
    protected void ExtractMp4Tags(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData, bool forceQuickMode)
    {
      // Calling EnsureLocalFileSystemAccess not necessary; only string operation
      string extensionUpper = StringUtils.TrimToEmpty(Path.GetExtension(lfsra.LocalFileSystemPath)).ToUpper();

      // Try to get extended information out of MP4 files)
      if (extensionUpper != ".MP4") return;

      using (lfsra.EnsureLocalFileSystemAccess())
      {
        TagLib.File mp4File = TagLib.File.Create(lfsra.LocalFileSystemPath);
        if (ReferenceEquals(mp4File, null) || ReferenceEquals(mp4File.Tag, null))
          return;

        TagLib.Tag tag = mp4File.Tag;

        string title = tag.Title;
        if (!string.IsNullOrEmpty(title))
          MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);

        int year = (int)tag.Year;
        if (year != 0)
          MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));

        if (!ReferenceEquals(tag.Genres, null) && tag.Genres.Length > 0)
          MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_GENRES, tag.Genres);

        if (!ReferenceEquals(tag.Performers, null) && tag.Performers.Length > 0)
          MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, tag.Performers);
      }
    }
コード例 #40
0
 /// <summary>
 /// Constructs a new <see cref="MatroskaInfoReader"/>.
 /// </summary>
 /// <param name="lfsra">
 /// <see cref="ILocalFsResourceAccessor"/> pointing to the MKV file to extract information from; The caller
 /// is responsible that it is valid while this class is used and that it is disposed afterwards.
 /// </param>
 public MatroskaInfoReader(ILocalFsResourceAccessor lfsra)
 {
     _lfsra          = lfsra;
     _mkvInfoPath    = FileUtils.BuildAssemblyRelativePath("mkvinfo.exe");
     _mkvExtractPath = FileUtils.BuildAssemblyRelativePath("mkvextract.exe");
 }
コード例 #41
0
    private bool ExtractThumbnail(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
      string title;
      if (!lfsra.IsFile || !MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title))
        return false;

      byte[] thumb;
      // We only want to create missing thumbnails here, so check for existing ones first
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, out thumb) && thumb != null)
        return true;

      // Check for a reasonable time offset
      long defaultVideoOffset = 720;
      long videoDuration;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_DURATION, out videoDuration))
      {
        if (defaultVideoOffset > videoDuration * 1 / 3)
          defaultVideoOffset = videoDuration * 1 / 3;
      }

      string tempFileName = Path.ChangeExtension(Path.GetTempFileName(), ".jpg");
      string executable = FileUtils.BuildAssemblyRelativePath("ffmpeg.exe");
      string arguments = string.Format("-ss {0} -itsoffset -5 -i \"{1}\" -vframes 1 -vf \"yadif='mode=send_frame:parity=auto:deint=all',scale=iw*sar:ih,setsar=1:1,scale=iw/2:-1\" -y \"{2}\"",
        defaultVideoOffset,
        lfsra.LocalFileSystemPath,
        tempFileName);

      try
      {
        if (ProcessUtils.TryExecute_AutoImpersonate(executable, arguments, ProcessPriorityClass.Idle, PROCESS_TIMEOUT_MS) && File.Exists(tempFileName))
        {
          var binary = FileUtils.ReadFile(tempFileName);
          MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
          ServiceRegistration.Get<ILogger>().Info("VideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
        }
        else
          ServiceRegistration.Get<ILogger>().Warn("VideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
      }
      finally
      {
        if (File.Exists(tempFileName))
          File.Delete(tempFileName);
      }
      return true;
    }
コード例 #42
0
        /// <summary>
        /// Extracts a frame image and caches them in the <see cref="IFanArtCache"/> service.
        /// </summary>
        /// <param name="lfsra"><see cref="ILocalFsResourceAccessor>"/> for the file.</param>
        /// <param name="mediaItemId">Id of the media item.</param>
        /// <param name="title">Title of the media item.</param>
        /// <returns><see cref="Task"/> that completes when the images have been cached.</returns>
        protected async Task ExtractThumbnailFanArt(ILocalFsResourceAccessor lfsra, Guid mediaItemId, string title, IDictionary <Guid, IList <MediaItemAspect> > aspects)
        {
            IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>();
            string       filename    = $"OpenCv.{Path.GetFileNameWithoutExtension(lfsra.LocalFileSystemPath)}";

            // Check for a reasonable time offset
            int    defaultVideoOffset = 720;
            long   videoDuration;
            double width     = 0;
            double height    = 0;
            double downscale = 7.5; // Reduces the HD video frame size to a quarter size to around 256
            IList <MultipleMediaItemAspect> videoAspects;

            if (MediaItemAspect.TryGetAspects(aspects, VideoStreamAspect.Metadata, out videoAspects))
            {
                if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0)
                {
                    if (defaultVideoOffset > videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET)
                    {
                        defaultVideoOffset = Convert.ToInt32(videoDuration * DEFAULT_OPENCV_THUMBNAIL_OFFSET);
                    }
                }

                width     = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_WIDTH);
                height    = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_HEIGHT);
                downscale = width / 256.0; //256 is max size of large thumbnail aspect
            }

            var sw = Stopwatch.StartNew();

            using (VideoCapture capture = new VideoCapture())
            {
                capture.Open(lfsra.LocalFileSystemPath);
                int capturePos = defaultVideoOffset * 1000;
                if (capture.FrameCount > 0 && capture.Fps > 0)
                {
                    var duration = capture.FrameCount / capture.Fps;
                    if (defaultVideoOffset > duration)
                    {
                        capturePos = Convert.ToInt32(duration * DEFAULT_OPENCV_THUMBNAIL_OFFSET * 1000);
                    }
                }

                if (capture.FrameWidth > 0)
                {
                    downscale = capture.FrameWidth / 256.0; //256 is max size of large thumbnail aspect
                }
                capture.PosMsec = capturePos;
                using (var mat = capture.RetrieveMat())
                {
                    if (mat.Height > 0 && mat.Width > 0)
                    {
                        width  = mat.Width;
                        height = mat.Height;
                        Logger.Debug("VideoFanArtHandler: Scaling thumbnail of size {1}x{2} for resource '{0}'", lfsra.LocalFileSystemPath, width, height);
                        using (var scaledMat = mat.Resize(new OpenCvSharp.Size(width / downscale, height / downscale)))
                        {
                            var binary = scaledMat.ToBytes();
                            await fanArtCache.TrySaveFanArt(mediaItemId, title, FanArtTypes.Thumbnail, p => TrySaveFileImage(binary, p, filename)).ConfigureAwait(false);

                            Logger.Debug("VideoFanArtHandler: Successfully created thumbnail for resource '{0}' ({1} ms)", lfsra.LocalFileSystemPath, sw.ElapsedMilliseconds);
                        }
                    }
                    else
                    {
                        Logger.Warn("VideoFanArtHandler: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                    }
                }
            }
        }
コード例 #43
0
    private bool ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // Calling EnsureLocalFileSystemAccess not necessary; only string operation
      string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
      string title;
      // VideoAspect must be present to be sure it is actually a video resource.
      if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
        return false;

      if (!MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) || string.IsNullOrEmpty(title))
        return false;

      MovieInfo movieInfo = new MovieInfo
        {
          MovieName = title,
        };

      // Allow the online lookup to choose best matching language for metadata
      ICollection<string> movieLanguages;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, VideoAspect.ATTR_AUDIOLANGUAGES, out movieLanguages) && movieLanguages.Count > 0)
        movieInfo.Languages.AddRange(movieLanguages);

      // Try to use an existing IMDB id for exact mapping
      string imdbId;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, MovieAspect.ATTR_IMDB_ID, out imdbId) ||
          pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId)) ||
          MatroskaMatcher.TryMatchImdbId(lfsra, out imdbId))
        movieInfo.ImdbId = imdbId;

      // Also test the full path year, using a dummy. This is useful if the path contains the real name and year.
      foreach (string path in pathsToTest)
      {
        MovieInfo dummy = new MovieInfo { MovieName = path };
        if (NamePreprocessor.MatchTitleYear(dummy))
        {
          movieInfo.MovieName = dummy.MovieName;
          movieInfo.Year = dummy.Year;
          break;
        }
      }

      // When searching movie title, the year can be relevant for multiple titles with same name but different years
      DateTime recordingDate;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate))
        movieInfo.Year = recordingDate.Year;

      if (MovieTheMovieDbMatcher.Instance.FindAndUpdateMovie(movieInfo))
      {
        if (!_onlyFanArt)
          movieInfo.SetMetadata(extractedAspectData);
        if (_onlyFanArt && movieInfo.MovieDbId > 0)
          MediaItemAspect.SetAttribute(extractedAspectData, MovieAspect.ATTR_TMDB_ID, movieInfo.MovieDbId);
        if (_onlyFanArt && movieInfo.CollectionMovieDbId > 0)
          MediaItemAspect.SetAttribute(extractedAspectData, MovieAspect.ATTR_COLLECTION_ID, movieInfo.CollectionMovieDbId);
        return true;
      }
      return false;
    }
コード例 #44
0
        private Task <bool> ExtractThumbnailAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // We can only work on files and make sure this file was detected by a lower MDE before (title is set then).
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!lfsra.IsFile || !extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID))
            {
                return(Task.FromResult(false));
            }

            //ServiceRegistration.Get<ILogger>().Info("OCVVideoThumbnailer: Evaluate {0}", lfsra.ResourceName);

            bool isPrimaryResource = false;
            IList <MultipleMediaItemAspect> resourceAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out resourceAspects))
            {
                foreach (MultipleMediaItemAspect pra in resourceAspects)
                {
                    string       accessorPath = (string)pra.GetAttributeValue(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH);
                    ResourcePath resourcePath = ResourcePath.Deserialize(accessorPath);
                    if (resourcePath.Equals(lfsra.CanonicalLocalResourcePath))
                    {
                        if (pra.GetAttributeValue <int?>(ProviderResourceAspect.ATTR_TYPE) == ProviderResourceAspect.TYPE_PRIMARY)
                        {
                            isPrimaryResource = true;
                            break;
                        }
                    }
                }
            }

            if (!isPrimaryResource) //Ignore subtitles
            {
                return(Task.FromResult(false));
            }

            // Check for a reasonable time offset
            int    defaultVideoOffset = 720;
            long   videoDuration;
            double width     = 0;
            double height    = 0;
            double downscale = 2; // Reduces the video frame size to a half of original
            IList <MultipleMediaItemAspect> videoAspects;

            if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoStreamAspect.Metadata, out videoAspects))
            {
                if ((videoDuration = videoAspects[0].GetAttributeValue <long>(VideoStreamAspect.ATTR_DURATION)) > 0)
                {
                    if (defaultVideoOffset > videoDuration * DEFAULT_THUMBNAIL_OFFSET)
                    {
                        defaultVideoOffset = Convert.ToInt32(videoDuration * DEFAULT_THUMBNAIL_OFFSET);
                    }
                }

                width     = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_WIDTH);
                height    = videoAspects[0].GetAttributeValue <int>(VideoStreamAspect.ATTR_HEIGHT);
                downscale = width / 256.0; //256 is max size of large thumbnail aspect
            }

            using (lfsra.EnsureLocalFileSystemAccess())
            {
                using (VideoCapture capture = new VideoCapture())
                {
                    capture.Open(lfsra.LocalFileSystemPath);
                    int capturePos = defaultVideoOffset * 1000;
                    if (capture.FrameCount > 0 && capture.Fps > 0)
                    {
                        var duration = capture.FrameCount / capture.Fps;
                        if (defaultVideoOffset > duration)
                        {
                            capturePos = Convert.ToInt32(duration * DEFAULT_THUMBNAIL_OFFSET * 1000);
                        }
                    }
                    if (capture.FrameWidth > 0 && width == 0)
                    {
                        width     = capture.FrameWidth;
                        downscale = width / 256.0; //256 is max size of large thumbnail aspect
                    }
                    capture.PosMsec = capturePos;
                    using (var mat = capture.RetrieveMat())
                    {
                        if (mat.Height > 0 && mat.Width > 0)
                        {
                            width  = mat.Width;
                            height = mat.Height;
                            ServiceRegistration.Get <ILogger>().Debug("OCVVideoThumbnailer: Scaling thumbnail of size {1}x{2} for resource '{0}'", lfsra.LocalFileSystemPath, width, height);
                            using (var scaledMat = mat.Resize(new OpenCvSharp.Size(width / downscale, height / downscale)))
                            {
                                var binary = scaledMat.ToBytes();
                                MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, binary);
                                ServiceRegistration.Get <ILogger>().Info("OCVVideoThumbnailer: Successfully created thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                            }
                        }
                        else
                        {
                            ServiceRegistration.Get <ILogger>().Warn("OCVVideoThumbnailer: Failed to create thumbnail for resource '{0}'", lfsra.LocalFileSystemPath);
                        }
                    }
                }
            }
            return(Task.FromResult(true));
        }
コード例 #45
0
 /// <summary>
 /// Executes FFProbe and ensures that it has access to the respective resource
 /// </summary>
 /// <param name="lfsra"><see cref="ILocalFsResourceAccessor"/> to which FFProbe needs access to</param>
 /// <param name="arguments">Arguments for FFProbe</param>
 /// <param name="priorityClass">Process priority</param>
 /// <param name="maxWaitMs">Maximum time to wait for completion</param>
 /// <returns>A <see cref="Task"/> representing the result of executing FFProbe</returns>
 /// <remarks>
 /// This is a convenience method that enables executing FFProbe directly on the <see cref="ILocalFsResourceAccessor"/>
 /// interface to which FFProbe needs access. The purpose of an <see cref="ILocalFsResourceAccessor"/> is providing
 /// access to a resource - not executing programs which is why this method is implemented as an extension method instead of
 /// a method directly on the interface.
 /// </remarks>
 public static Task <ProcessExecutionResult> FFProbeExecuteWithResourceAccessAsync(ILocalFsResourceAccessor lfsra, string arguments, ProcessPriorityClass priorityClass, int maxWaitMs)
 {
     return(lfsra.ExecuteWithResourceAccessAsync(_ffProbeBinPath, arguments, priorityClass, maxWaitMs));
 }
コード例 #46
0
        /// <summary>
        /// Gets a list of <see cref="FanArtImage"/>s for a requested <paramref name="mediaType"/>, <paramref name="fanArtType"/> and <paramref name="name"/>.
        /// The name can be: Series name, Actor name, Artist name depending on the <paramref name="mediaType"/>.
        /// </summary>
        /// <param name="mediaType">Requested FanArtMediaType</param>
        /// <param name="fanArtType">Requested FanArtType</param>
        /// <param name="name">Requested name of Series, Actor, Artist...</param>
        /// <param name="maxWidth">Maximum width for image. <c>0</c> returns image in original size.</param>
        /// <param name="maxHeight">Maximum height for image. <c>0</c> returns image in original size.</param>
        /// <param name="singleRandom">If <c>true</c> only one random image URI will be returned</param>
        /// <param name="result">Result if return code is <c>true</c>.</param>
        /// <returns><c>true</c> if at least one match was found.</returns>
        public bool TryGetFanArt(FanArtConstants.FanArtMediaType mediaType, FanArtConstants.FanArtType fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList <IResourceLocator> result)
        {
            result = null;
            Guid mediaItemId;

            // Don't try to load "fanart" for images
            if (!Guid.TryParse(name, out mediaItemId) || mediaType == FanArtConstants.FanArtMediaType.Image)
            {
                return(false);
            }

            IMediaLibrary mediaLibrary = ServiceRegistration.Get <IMediaLibrary>(false);

            if (mediaLibrary == null)
            {
                return(false);
            }

            IFilter           filter = new MediaItemIdFilter(mediaItemId);
            IList <MediaItem> items  = mediaLibrary.Search(new MediaItemQuery(_necessarryMIAs, filter), false);

            if (items == null || items.Count == 0)
            {
                return(false);
            }

            MediaItem               mediaItem      = items.First();
            string                  fileSystemPath = null;
            List <string>           localPatterns  = new List <string>();
            List <IResourceLocator> files          = new List <IResourceLocator>();

            // File based access
            try
            {
                var resourceLocator = mediaItem.GetResourceLocator();
                using (var accessor = resourceLocator.CreateAccessor())
                {
                    ILocalFsResourceAccessor fsra = accessor as ILocalFsResourceAccessor;
                    if (fsra != null)
                    {
                        fileSystemPath = fsra.LocalFileSystemPath;
                        var path = Path.GetDirectoryName(fileSystemPath);
                        var file = Path.GetFileNameWithoutExtension(fileSystemPath);

                        if (fanArtType == FanArtConstants.FanArtType.Poster || fanArtType == FanArtConstants.FanArtType.Thumbnail)
                        {
                            localPatterns.Add(Path.ChangeExtension(fileSystemPath, ".jpg"));
                            localPatterns.Add(Path.Combine(path, file + "-poster.jpg"));
                            localPatterns.Add(Path.Combine(path, "folder.jpg"));
                        }
                        if (fanArtType == FanArtConstants.FanArtType.FanArt)
                        {
                            localPatterns.Add(Path.Combine(path, "backdrop.jpg"));
                            localPatterns.Add(Path.Combine(path, file + "-fanart*.jpg"));
                            localPatterns.Add(Path.Combine(path, "ExtraFanArt\\*.jpg"));
                        }

                        // Copy all patterns for .jpg -> .png + .tbn
                        localPatterns.AddRange(new List <string>(localPatterns).Select(p => p.Replace(".jpg", ".png")));
                        localPatterns.AddRange(new List <string>(localPatterns).Select(p => p.Replace(".jpg", ".tbn")));

                        foreach (var pattern in localPatterns)
                        {
                            try
                            {
                                var           pathPart      = Path.GetDirectoryName(pattern);
                                var           filePart      = Path.GetFileName(pattern);
                                DirectoryInfo directoryInfo = new DirectoryInfo(pathPart);
                                if (directoryInfo.Exists)
                                {
                                    files.AddRange(directoryInfo.GetFiles(filePart)
                                                   .Select(f => f.FullName)
                                                   .Select(fileName => new ResourceLocator(resourceLocator.NativeSystemId, ResourcePath.BuildBaseProviderPath(resourceLocator.NativeResourcePath.LastPathSegment.ProviderId, fileName))));
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
#if DEBUG
                ServiceRegistration.Get <ILogger>().Warn("Error while search fanart of type '{0}' for path '{1}'", ex, fanArtType, fileSystemPath);
#endif
            }
            result = files;
            return(files.Count > 0);
        }
コード例 #47
0
 /// <summary>
 /// Constructs a new <see cref="MatroskaBinaryReader"/>.
 /// </summary>
 /// <param name="lfsra">
 /// <see cref="ILocalFsResourceAccessor"/> pointing to the MKV file to extract information from; The caller
 /// is responsible that it is valid while this class is used and that it is disposed afterwards.
 /// </param>
 public MatroskaBinaryReader(ILocalFsResourceAccessor lfsra)
 {
     _lfsra          = lfsra;
     _descriptorsMap = _elementDescriptors.Where(d => d != null).ToDictionary(d => d.Identifier.EncodedValue, d => d);
 }
コード例 #48
0
    protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary<Guid, MediaItemAspect> extractedAspectData)
    {
      // VideoAspect must be present to be sure it is actually a video resource.
      if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
        return false;

      SeriesInfo seriesInfo = null;

      // First check if we already have a complete match from a previous MDE
      string title;
      int tvDbId;
      int seasonNumber;
      IEnumerable<int> episodeNumbers;
      if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) &&
          MediaItemAspect.TryGetAttribute(extractedAspectData, SeriesAspect.ATTR_TVDB_ID, out tvDbId) &&
          MediaItemAspect.TryGetAttribute(extractedAspectData, SeriesAspect.ATTR_SEASON, out seasonNumber) &&
          (episodeNumbers = extractedAspectData[SeriesAspect.ASPECT_ID].GetCollectionAttribute<int>(SeriesAspect.ATTR_EPISODE)) != null)
      {
        seriesInfo = new SeriesInfo
        {
          Series = title,
          TvdbId = tvDbId,
          SeasonNumber = seasonNumber,
        };
        episodeNumbers.ToList().ForEach(n => seriesInfo.EpisodeNumbers.Add(n));
      }

      // If there was no complete match, yet, try to get extended information out of matroska files)
      if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
      {
        MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
        if (matroskaMatcher.MatchSeries(lfsra, out seriesInfo, ref extractedAspectData))
        {
          ServiceRegistration.Get<ILogger>().Debug("ExtractSeriesData: Found SeriesInformation by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, IsCompleteMatch {3}",
            seriesInfo.Series, seriesInfo.ImdbId, seriesInfo.TvdbId, seriesInfo.IsCompleteMatch);
        }
      }

      // If no information was found before, try name matching
      if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
      {
        // Try to match series from folder and file namings
        SeriesMatcher seriesMatcher = new SeriesMatcher();
        seriesMatcher.MatchSeries(lfsra, out seriesInfo);
      }

      // Lookup online information (incl. fanart)
      if (seriesInfo != null && seriesInfo.IsCompleteMatch)
      {
        SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
        if (!_onlyFanArt)
          seriesInfo.SetMetadata(extractedAspectData);
      }
      return (seriesInfo != null && seriesInfo.IsCompleteMatch && !_onlyFanArt);
    }