mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-01-17 10:45:49 +02:00
Merge branch 'metadata' into develop
This commit is contained in:
commit
f6077238e6
@ -12,5 +12,15 @@ namespace NzbDrone.Common
|
|||||||
|
|
||||||
return source.Where(element => knownKeys.Add(keySelector(element)));
|
return source.Where(element => knownKeys.Add(keySelector(element)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddIfNotNull<TSource>(this List<TSource> source, TSource item)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
source.Add(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
using FluentMigrator;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(49)]
|
||||||
|
public class add_hash_to_metadata_files : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Alter.Table("MetadataFiles").AddColumn("Hash").AsString().Nullable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,117 +22,23 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
{
|
{
|
||||||
public class RoksboxMetadata : MetadataBase<RoksboxMetadataSettings>
|
public class RoksboxMetadata : MetadataBase<RoksboxMetadataSettings>
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
|
||||||
private readonly IMapCoversToLocal _mediaCoverService;
|
private readonly IMapCoversToLocal _mediaCoverService;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly IMetadataFileService _metadataFileService;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IHttpProvider _httpProvider;
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public RoksboxMetadata(IEventAggregator eventAggregator,
|
public RoksboxMetadata(IMapCoversToLocal mediaCoverService,
|
||||||
IMapCoversToLocal mediaCoverService,
|
|
||||||
IMediaFileService mediaFileService,
|
|
||||||
IMetadataFileService metadataFileService,
|
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IHttpProvider httpProvider,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, httpProvider, logger)
|
|
||||||
{
|
{
|
||||||
_eventAggregator = eventAggregator;
|
|
||||||
_mediaCoverService = mediaCoverService;
|
_mediaCoverService = mediaCoverService;
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_metadataFileService = metadataFileService;
|
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_httpProvider = httpProvider;
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<string> ValidCertification = new List<string> { "G", "NC-17", "PG", "PG-13", "R", "UR", "UNRATED", "NR", "TV-Y", "TV-Y7", "TV-Y7-FV", "TV-G", "TV-PG", "TV-14", "TV-MA" };
|
private static List<string> ValidCertification = new List<string> { "G", "NC-17", "PG", "PG-13", "R", "UR", "UNRATED", "NR", "TV-Y", "TV-Y7", "TV-Y7-FV", "TV-G", "TV-PG", "TV-14", "TV-MA" };
|
||||||
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<season>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<season>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public override void OnSeriesUpdated(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
public override List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(series.Path))
|
|
||||||
{
|
|
||||||
_logger.Info("Series folder ({0}) does not exist, skipping metadata creation", series.Path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeriesImages)
|
|
||||||
{
|
|
||||||
var metadata = WriteSeriesImages(series, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeasonImages)
|
|
||||||
{
|
|
||||||
var metadata = WriteSeasonImages(series, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.AddRange(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
var metadata = WriteEpisodeMetadata(series, episodeFile, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, existingMetadataFiles);
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
metadataFiles.RemoveAll(c => c == null);
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload)
|
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(WriteEpisodeMetadata(series, episodeFile, new List<MetadataFile>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, new List<MetadataFile>());
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
|
||||||
{
|
{
|
||||||
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
||||||
var updatedMetadataFiles = new List<MetadataFile>();
|
var updatedMetadataFiles = new List<MetadataFile>();
|
||||||
@ -173,7 +79,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(updatedMetadataFiles));
|
return updatedMetadataFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MetadataFile FindMetadataFile(Series series, string path)
|
public override MetadataFile FindMetadataFile(Series series, string path)
|
||||||
@ -191,7 +97,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Series and season images are both named folder.jpg, only season ones sit in season folders
|
//Series and season images are both named folder.jpg, only season ones sit in season folders
|
||||||
if (String.Compare(filename, parentdir.Name, true) == 0)
|
if (String.Compare(filename, parentdir.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||||
{
|
{
|
||||||
var seasonMatch = SeasonImagesRegex.Match(parentdir.Name);
|
var seasonMatch = SeasonImagesRegex.Match(parentdir.Name);
|
||||||
if (seasonMatch.Success)
|
if (seasonMatch.Success)
|
||||||
@ -223,24 +129,76 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
if (parseResult != null &&
|
if (parseResult != null &&
|
||||||
!parseResult.FullSeason)
|
!parseResult.FullSeason)
|
||||||
{
|
{
|
||||||
switch (Path.GetExtension(filename).ToLowerInvariant())
|
var extension = Path.GetExtension(filename).ToLowerInvariant();
|
||||||
|
|
||||||
|
if (extension == ".xml")
|
||||||
{
|
{
|
||||||
case ".xml":
|
|
||||||
metadata.Type = MetadataType.EpisodeMetadata;
|
metadata.Type = MetadataType.EpisodeMetadata;
|
||||||
return metadata;
|
return metadata;
|
||||||
case ".jpg":
|
|
||||||
metadata.Type = MetadataType.EpisodeImage;
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extension == ".jpg")
|
||||||
|
{
|
||||||
|
if (!Path.GetFileNameWithoutExtension(filename).EndsWith("-thumb"))
|
||||||
|
{
|
||||||
|
metadata.Type = MetadataType.EpisodeImage;
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataFile WriteSeriesImages(Series series, List<MetadataFile> existingMetadataFiles)
|
public override MetadataFileResult SeriesMetadata(Series series)
|
||||||
|
{
|
||||||
|
//Series metadata is not supported
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
if (!Settings.EpisodeMetadata)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
|
||||||
|
|
||||||
|
var xmlResult = String.Empty;
|
||||||
|
foreach (var episode in episodeFile.Episodes.Value)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var xws = new XmlWriterSettings();
|
||||||
|
xws.OmitXmlDeclaration = true;
|
||||||
|
xws.Indent = false;
|
||||||
|
|
||||||
|
using (var xw = XmlWriter.Create(sb, xws))
|
||||||
|
{
|
||||||
|
var doc = new XDocument();
|
||||||
|
|
||||||
|
var details = new XElement("video");
|
||||||
|
details.Add(new XElement("title", String.Format("{0} - {1}x{2} - {3}", series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title)));
|
||||||
|
details.Add(new XElement("year", episode.AirDate));
|
||||||
|
details.Add(new XElement("genre", String.Join(" / ", series.Genres)));
|
||||||
|
var actors = String.Join(" , ", series.Actors.ConvertAll(c => c.Name + " - " + c.Character).GetRange(0, Math.Min(3, series.Actors.Count)));
|
||||||
|
details.Add(new XElement("actors", actors));
|
||||||
|
details.Add(new XElement("description", episode.Overview));
|
||||||
|
details.Add(new XElement("length", series.Runtime));
|
||||||
|
details.Add(new XElement("mpaa", ValidCertification.Contains(series.Certification.ToUpperInvariant()) ? series.Certification.ToUpperInvariant() : "UNRATED"));
|
||||||
|
doc.Add(details);
|
||||||
|
doc.Save(xw);
|
||||||
|
|
||||||
|
xmlResult += doc.ToString();
|
||||||
|
xmlResult += Environment.NewLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> SeriesImages(Series series)
|
||||||
{
|
{
|
||||||
//Because we only support one image, attempt to get the Poster type, then if that fails grab the first
|
|
||||||
var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault();
|
var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault();
|
||||||
if (image == null)
|
if (image == null)
|
||||||
{
|
{
|
||||||
@ -251,39 +209,66 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
||||||
var destination = Path.Combine(series.Path, Path.GetFileName(series.Path) + Path.GetExtension(source));
|
var destination = Path.Combine(series.Path, Path.GetFileName(series.Path) + Path.GetExtension(source));
|
||||||
|
|
||||||
//TODO: Do we want to overwrite the file if it exists?
|
return new List<ImageFileResult>{ new ImageFileResult(destination, source) };
|
||||||
if (_diskProvider.FileExists(destination))
|
|
||||||
{
|
|
||||||
_logger.Debug("Series image: {0} already exists.", image.CoverType);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
_diskProvider.CopyFile(source, destination, false);
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeriesImage) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeriesImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, destination)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<MetadataFile> WriteSeasonImages(Series series, List<MetadataFile> existingMetadataFiles)
|
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||||
{
|
{
|
||||||
_logger.Debug("Writing season images for {0}.", series.Title);
|
var seasonFolders = GetSeasonFolders(series);
|
||||||
//Create a dictionary between season number and output folder
|
|
||||||
var seasonFolderMap = new Dictionary<int, string>();
|
string seasonFolder;
|
||||||
foreach (var folder in Directory.EnumerateDirectories(series.Path))
|
if (!seasonFolders.TryGetValue(season.SeasonNumber, out seasonFolder))
|
||||||
|
{
|
||||||
|
_logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Roksbox only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection
|
||||||
|
var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault();
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
_logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var filename = Path.GetFileName(seasonFolder) + ".jpg";
|
||||||
|
var path = Path.Combine(series.Path, seasonFolder, filename);
|
||||||
|
|
||||||
|
return new List<ImageFileResult> { new ImageFileResult(path, image.Url) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
||||||
|
|
||||||
|
if (screenshot == null)
|
||||||
|
{
|
||||||
|
_logger.Trace("Episode screenshot not available");
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<ImageFileResult> {new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetEpisodeMetadataFilename(string episodeFilePath)
|
||||||
|
{
|
||||||
|
return Path.ChangeExtension(episodeFilePath, "xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetEpisodeImageFilename(string episodeFilePath)
|
||||||
|
{
|
||||||
|
return Path.ChangeExtension(episodeFilePath, "jpg");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<Int32, String> GetSeasonFolders(Series series)
|
||||||
|
{
|
||||||
|
var seasonFolderMap = new Dictionary<Int32, String>();
|
||||||
|
|
||||||
|
foreach (var folder in _diskProvider.GetDirectories(series.Path))
|
||||||
{
|
{
|
||||||
var directoryinfo = new DirectoryInfo(folder);
|
var directoryinfo = new DirectoryInfo(folder);
|
||||||
var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name);
|
var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name);
|
||||||
|
|
||||||
if (seasonMatch.Success)
|
if (seasonMatch.Success)
|
||||||
{
|
{
|
||||||
var seasonNumber = seasonMatch.Groups["season"].Value;
|
var seasonNumber = seasonMatch.Groups["season"].Value;
|
||||||
@ -310,160 +295,8 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
|
|||||||
_logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title);
|
_logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var season in series.Seasons)
|
|
||||||
{
|
|
||||||
//Work out the path to this season - if we don't have a matching path then skip this season.
|
|
||||||
string seasonFolder;
|
|
||||||
if (!seasonFolderMap.TryGetValue(season.SeasonNumber, out seasonFolder))
|
|
||||||
{
|
|
||||||
_logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Roksbox only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection
|
return seasonFolderMap;
|
||||||
var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault();
|
|
||||||
if (image == null)
|
|
||||||
{
|
|
||||||
_logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var filename = Path.GetFileName(seasonFolder) + ".jpg";
|
|
||||||
|
|
||||||
var path = Path.Combine(series.Path, seasonFolder, filename);
|
|
||||||
_logger.Debug("Writing season image for series {0}, season {1} to {2}.", series.Title, season.SeasonNumber, path);
|
|
||||||
DownloadImage(series, image.Url, path);
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeasonImage &&
|
|
||||||
c.SeasonNumber == season.SeasonNumber) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
SeasonNumber = season.SeasonNumber,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeasonImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, path)
|
|
||||||
};
|
|
||||||
|
|
||||||
yield return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeMetadata(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
var filename = GetEpisodeMetadataFilename(episodeFile.Path);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Generating {0} for: {1}", filename, episodeFile.Path);
|
|
||||||
|
|
||||||
var xmlResult = String.Empty;
|
|
||||||
foreach (var episode in episodeFile.Episodes.Value)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
var xws = new XmlWriterSettings();
|
|
||||||
xws.OmitXmlDeclaration = true;
|
|
||||||
xws.Indent = false;
|
|
||||||
|
|
||||||
using (var xw = XmlWriter.Create(sb, xws))
|
|
||||||
{
|
|
||||||
var doc = new XDocument();
|
|
||||||
|
|
||||||
var details = new XElement("video");
|
|
||||||
details.Add(new XElement("title", String.Format("{0} - {1}x{2} - {3}", series.Title, episode.SeasonNumber, episode.EpisodeNumber, episode.Title)));
|
|
||||||
details.Add(new XElement("year", episode.AirDate));
|
|
||||||
details.Add(new XElement("genre", String.Join(" / ", series.Genres)));
|
|
||||||
var actors = String.Join(" , ", series.Actors.ConvertAll(c => c.Name + " - " + c.Character).GetRange(0, Math.Min(3, series.Actors.Count)));
|
|
||||||
details.Add(new XElement("actors", actors));
|
|
||||||
details.Add(new XElement("description", episode.Overview));
|
|
||||||
details.Add(new XElement("length", series.Runtime));
|
|
||||||
details.Add(new XElement("mpaa", ValidCertification.Contains( series.Certification.ToUpperInvariant() ) ? series.Certification.ToUpperInvariant() : "UNRATED" ) );
|
|
||||||
doc.Add(details);
|
|
||||||
doc.Save(xw);
|
|
||||||
|
|
||||||
xmlResult += doc.ToString();
|
|
||||||
xmlResult += Environment.NewLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Saving episodedetails to: {0}", filename);
|
|
||||||
_diskProvider.WriteAllText(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeMetadata,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeImages(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
|
||||||
|
|
||||||
if (screenshot == null)
|
|
||||||
{
|
|
||||||
_logger.Trace("Episode screenshot not available");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var filename = GetEpisodeImageFilename(episodeFile.Path);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeImage &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadImage(series, screenshot.Url, filename);
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetEpisodeMetadataFilename(string episodeFilePath)
|
|
||||||
{
|
|
||||||
return Path.ChangeExtension(episodeFilePath, "xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetEpisodeImageFilename(string episodeFilePath)
|
|
||||||
{
|
|
||||||
return Path.ChangeExtension(episodeFilePath, "jpg");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Remoting.Messaging;
|
using System.Runtime.Remoting.Messaging;
|
||||||
@ -22,116 +23,22 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
|
|||||||
{
|
{
|
||||||
public class WdtvMetadata : MetadataBase<WdtvMetadataSettings>
|
public class WdtvMetadata : MetadataBase<WdtvMetadataSettings>
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
|
||||||
private readonly IMapCoversToLocal _mediaCoverService;
|
private readonly IMapCoversToLocal _mediaCoverService;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly IMetadataFileService _metadataFileService;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IHttpProvider _httpProvider;
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public WdtvMetadata(IEventAggregator eventAggregator,
|
public WdtvMetadata(IMapCoversToLocal mediaCoverService,
|
||||||
IMapCoversToLocal mediaCoverService,
|
|
||||||
IMediaFileService mediaFileService,
|
|
||||||
IMetadataFileService metadataFileService,
|
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IHttpProvider httpProvider,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, httpProvider, logger)
|
|
||||||
{
|
{
|
||||||
_eventAggregator = eventAggregator;
|
|
||||||
_mediaCoverService = mediaCoverService;
|
_mediaCoverService = mediaCoverService;
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_metadataFileService = metadataFileService;
|
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_httpProvider = httpProvider;
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<season>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<season>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public override void OnSeriesUpdated(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
public override List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(series.Path))
|
|
||||||
{
|
|
||||||
_logger.Info("Series folder ({0}) does not exist, skipping metadata creation", series.Path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeriesImages)
|
|
||||||
{
|
|
||||||
var metadata = WriteSeriesImages(series, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeasonImages)
|
|
||||||
{
|
|
||||||
var metadata = WriteSeasonImages(series, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.AddRange(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
var metadata = WriteEpisodeMetadata(series, episodeFile, existingMetadataFiles);
|
|
||||||
if (metadata != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, existingMetadataFiles);
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
metadataFiles.RemoveAll(c => c == null);
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload)
|
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(WriteEpisodeMetadata(series, episodeFile, new List<MetadataFile>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, new List<MetadataFile>());
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
|
||||||
{
|
{
|
||||||
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
||||||
var updatedMetadataFiles = new List<MetadataFile>();
|
var updatedMetadataFiles = new List<MetadataFile>();
|
||||||
@ -171,8 +78,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return updatedMetadataFiles;
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(updatedMetadataFiles));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MetadataFile FindMetadataFile(Series series, string path)
|
public override MetadataFile FindMetadataFile(Series series, string path)
|
||||||
@ -237,137 +143,20 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataFile WriteSeriesImages(Series series, List<MetadataFile> existingMetadataFiles)
|
public override MetadataFileResult SeriesMetadata(Series series)
|
||||||
{
|
{
|
||||||
//Because we only support one image, attempt to get the Poster type, then if that fails grab the first
|
//Series metadata is not supported
|
||||||
var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault();
|
|
||||||
if (image == null)
|
|
||||||
{
|
|
||||||
_logger.Trace("Failed to find suitable Series image for series {0}.", series.Title);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile)
|
||||||
var destination = Path.Combine(series.Path, "folder" + Path.GetExtension(source));
|
{
|
||||||
|
if (!Settings.EpisodeMetadata)
|
||||||
//TODO: Do we want to overwrite the file if it exists?
|
|
||||||
if (_diskProvider.FileExists(destination))
|
|
||||||
{
|
{
|
||||||
_logger.Debug("Series image: {0} already exists.", image.CoverType);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
_diskProvider.CopyFile(source, destination, false);
|
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeriesImage) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeriesImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, destination)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<MetadataFile> WriteSeasonImages(Series series, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
_logger.Debug("Writing season images for {0}.", series.Title);
|
|
||||||
//Create a dictionary between season number and output folder
|
|
||||||
var seasonFolderMap = new Dictionary<int, string>();
|
|
||||||
foreach (var folder in Directory.EnumerateDirectories(series.Path))
|
|
||||||
{
|
|
||||||
var directoryinfo = new DirectoryInfo(folder);
|
|
||||||
var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name);
|
|
||||||
if (seasonMatch.Success)
|
|
||||||
{
|
|
||||||
var seasonNumber = seasonMatch.Groups["season"].Value;
|
|
||||||
|
|
||||||
if (seasonNumber.Contains("specials"))
|
|
||||||
{
|
|
||||||
seasonFolderMap[0] = folder;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int matchedSeason;
|
|
||||||
if (Int32.TryParse(seasonNumber, out matchedSeason))
|
|
||||||
{
|
|
||||||
seasonFolderMap[matchedSeason] = folder;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Failed to parse season number from {0} for series {1}.", folder, series.Title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var season in series.Seasons)
|
|
||||||
{
|
|
||||||
//Work out the path to this season - if we don't have a matching path then skip this season.
|
|
||||||
string seasonFolder;
|
|
||||||
if (!seasonFolderMap.TryGetValue(season.SeasonNumber, out seasonFolder))
|
|
||||||
{
|
|
||||||
_logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//WDTV only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection
|
|
||||||
var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault();
|
|
||||||
if (image == null)
|
|
||||||
{
|
|
||||||
_logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var filename = "folder.jpg";
|
|
||||||
|
|
||||||
var path = Path.Combine(series.Path, seasonFolder, filename);
|
|
||||||
_logger.Debug("Writing season image for series {0}, season {1} to {2}.", series.Title, season.SeasonNumber, path);
|
|
||||||
DownloadImage(series, image.Url, path);
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeasonImage &&
|
|
||||||
c.SeasonNumber == season.SeasonNumber) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
SeasonNumber = season.SeasonNumber,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeasonImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, path)
|
|
||||||
};
|
|
||||||
|
|
||||||
yield return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeMetadata(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
var filename = GetEpisodeMetadataFilename(episodeFile.Path);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Generating {0} for: {1}", filename, episodeFile.Path);
|
|
||||||
|
|
||||||
var xmlResult = String.Empty;
|
var xmlResult = String.Empty;
|
||||||
foreach (var episode in episodeFile.Episodes.Value)
|
foreach (var episode in episodeFile.Episodes.Value)
|
||||||
@ -406,61 +195,81 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Debug("Saving episodedetails to: {0}", filename);
|
var filename = GetEpisodeMetadataFilename(episodeFile.Path);
|
||||||
_diskProvider.WriteAllText(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
return new MetadataFileResult(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeMetadata,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeImages(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
public override List<ImageFileResult> SeriesImages(Series series)
|
||||||
{
|
{
|
||||||
|
if (!Settings.SeriesImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Because we only support one image, attempt to get the Poster type, then if that fails grab the first
|
||||||
|
var image = series.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? series.Images.FirstOrDefault();
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
_logger.Trace("Failed to find suitable Series image for series {0}.", series.Title);
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
||||||
|
var destination = Path.Combine(series.Path, "folder" + Path.GetExtension(source));
|
||||||
|
|
||||||
|
return new List<ImageFileResult>
|
||||||
|
{
|
||||||
|
new ImageFileResult(destination, source)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||||
|
{
|
||||||
|
if (!Settings.SeasonImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var seasonFolders = GetSeasonFolders(series);
|
||||||
|
|
||||||
|
//Work out the path to this season - if we don't have a matching path then skip this season.
|
||||||
|
string seasonFolder;
|
||||||
|
if (!seasonFolders.TryGetValue(season.SeasonNumber, out seasonFolder))
|
||||||
|
{
|
||||||
|
_logger.Trace("Failed to find season folder for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//WDTV only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection
|
||||||
|
var image = season.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? season.Images.FirstOrDefault();
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
_logger.Trace("Failed to find suitable season image for series {0}, season {1}.", series.Title, season.SeasonNumber);
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.Combine(series.Path, seasonFolder, "folder.jpg");
|
||||||
|
|
||||||
|
return new List<ImageFileResult>{ new ImageFileResult(path, image.Url) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
if (!Settings.EpisodeImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
||||||
|
|
||||||
if (screenshot == null)
|
if (screenshot == null)
|
||||||
{
|
{
|
||||||
_logger.Trace("Episode screenshot not available");
|
_logger.Trace("Episode screenshot not available");
|
||||||
return null;
|
return new List<ImageFileResult>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename = GetEpisodeImageFilename(episodeFile.Path);
|
return new List<ImageFileResult>{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url) };
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeImage &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadImage(series, screenshot.Url, filename);
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetEpisodeMetadataFilename(string episodeFilePath)
|
private string GetEpisodeMetadataFilename(string episodeFilePath)
|
||||||
@ -472,5 +281,45 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
|
|||||||
{
|
{
|
||||||
return Path.ChangeExtension(episodeFilePath, "metathumb");
|
return Path.ChangeExtension(episodeFilePath, "metathumb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<Int32, String> GetSeasonFolders(Series series)
|
||||||
|
{
|
||||||
|
var seasonFolderMap = new Dictionary<Int32, String>();
|
||||||
|
|
||||||
|
foreach (var folder in _diskProvider.GetDirectories(series.Path))
|
||||||
|
{
|
||||||
|
var directoryinfo = new DirectoryInfo(folder);
|
||||||
|
var seasonMatch = SeasonImagesRegex.Match(directoryinfo.Name);
|
||||||
|
|
||||||
|
if (seasonMatch.Success)
|
||||||
|
{
|
||||||
|
var seasonNumber = seasonMatch.Groups["season"].Value;
|
||||||
|
|
||||||
|
if (seasonNumber.Contains("specials"))
|
||||||
|
{
|
||||||
|
seasonFolderMap[0] = folder;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int matchedSeason;
|
||||||
|
if (Int32.TryParse(seasonNumber, out matchedSeason))
|
||||||
|
{
|
||||||
|
seasonFolderMap[matchedSeason] = folder;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Failed to parse season number from {0} for series {1}.", folder, series.Title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Rejecting folder {0} for series {1}.", Path.GetDirectoryName(folder), series.Title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return seasonFolderMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,32 +20,16 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
{
|
{
|
||||||
public class XbmcMetadata : MetadataBase<XbmcMetadataSettings>
|
public class XbmcMetadata : MetadataBase<XbmcMetadataSettings>
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
|
||||||
private readonly IMapCoversToLocal _mediaCoverService;
|
private readonly IMapCoversToLocal _mediaCoverService;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly IMetadataFileService _metadataFileService;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IHttpProvider _httpProvider;
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public XbmcMetadata(IEventAggregator eventAggregator,
|
public XbmcMetadata(IMapCoversToLocal mediaCoverService,
|
||||||
IMapCoversToLocal mediaCoverService,
|
|
||||||
IMediaFileService mediaFileService,
|
|
||||||
IMetadataFileService metadataFileService,
|
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IHttpProvider httpProvider,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, httpProvider, logger)
|
|
||||||
{
|
{
|
||||||
_eventAggregator = eventAggregator;
|
|
||||||
_mediaCoverService = mediaCoverService;
|
_mediaCoverService = mediaCoverService;
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_metadataFileService = metadataFileService;
|
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_httpProvider = httpProvider;
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,79 +37,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
private static readonly Regex SeasonImagesRegex = new Regex(@"^season(?<season>\d{2,}|-all|-specials)-(?<type>poster|banner|fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex SeasonImagesRegex = new Regex(@"^season(?<season>\d{2,}|-all|-specials)-(?<type>poster|banner|fanart)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
private static readonly Regex EpisodeImageRegex = new Regex(@"-thumb\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex EpisodeImageRegex = new Regex(@"-thumb\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public override void OnSeriesUpdated(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
public override List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(series.Path))
|
|
||||||
{
|
|
||||||
_logger.Info("Series folder does not exist, skipping metadata creation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeriesMetadata)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(WriteTvShowNfo(series, existingMetadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeriesImages)
|
|
||||||
{
|
|
||||||
metadataFiles.AddRange(WriteSeriesImages(series, existingMetadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SeasonImages)
|
|
||||||
{
|
|
||||||
metadataFiles.AddRange(WriteSeasonImages(series, existingMetadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(WriteEpisodeNfo(series, episodeFile, existingMetadataFiles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episodeFile in episodeFiles)
|
|
||||||
{
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, existingMetadataFiles);
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload)
|
|
||||||
{
|
|
||||||
var metadataFiles = new List<MetadataFile>();
|
|
||||||
|
|
||||||
if (Settings.EpisodeMetadata)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(WriteEpisodeNfo(series, episodeFile, new List<MetadataFile>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.EpisodeImages)
|
|
||||||
{
|
|
||||||
var metadataFile = WriteEpisodeImages(series, episodeFile, new List<MetadataFile>());
|
|
||||||
|
|
||||||
if (metadataFile != null)
|
|
||||||
{
|
|
||||||
metadataFiles.Add(metadataFile);
|
|
||||||
}
|
|
||||||
WriteEpisodeImages(series, episodeFile, new List<MetadataFile>());
|
|
||||||
}
|
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(metadataFiles));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles)
|
|
||||||
{
|
{
|
||||||
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
var episodeFilesMetadata = existingMetadataFiles.Where(c => c.EpisodeFileId > 0).ToList();
|
||||||
var updatedMetadataFiles = new List<MetadataFile>();
|
var updatedMetadataFiles = new List<MetadataFile>();
|
||||||
@ -166,7 +78,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new MetadataFilesUpdated(updatedMetadataFiles));
|
return updatedMetadataFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MetadataFile FindMetadataFile(Series series, string path)
|
public override MetadataFile FindMetadataFile(Series series, string path)
|
||||||
@ -240,8 +152,13 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataFile WriteTvShowNfo(Series series, List<MetadataFile> existingMetadataFiles)
|
public override MetadataFileResult SeriesMetadata(Series series)
|
||||||
{
|
{
|
||||||
|
if (!Settings.SeriesMetadata)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Debug("Generating tvshow.nfo for: {0}", series.Title);
|
_logger.Debug("Generating tvshow.nfo for: {0}", series.Title);
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
var xws = new XmlWriterSettings();
|
var xws = new XmlWriterSettings();
|
||||||
@ -255,7 +172,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
var tvShow = new XElement("tvshow");
|
var tvShow = new XElement("tvshow");
|
||||||
|
|
||||||
tvShow.Add(new XElement("title", series.Title));
|
tvShow.Add(new XElement("title", series.Title));
|
||||||
tvShow.Add(new XElement("rating", (decimal)series.Ratings.Percentage/10));
|
tvShow.Add(new XElement("rating", (decimal) series.Ratings.Percentage/10));
|
||||||
tvShow.Add(new XElement("plot", series.Overview));
|
tvShow.Add(new XElement("plot", series.Overview));
|
||||||
tvShow.Add(new XElement("episodeguide", new XElement("url", episodeGuideUrl)));
|
tvShow.Add(new XElement("episodeguide", new XElement("url", episodeGuideUrl)));
|
||||||
tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl));
|
tvShow.Add(new XElement("episodeguideurl", episodeGuideUrl));
|
||||||
@ -288,108 +205,13 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
|
|
||||||
_logger.Debug("Saving tvshow.nfo for {0}", series.Title);
|
_logger.Debug("Saving tvshow.nfo for {0}", series.Title);
|
||||||
|
|
||||||
var path = Path.Combine(series.Path, "tvshow.nfo");
|
return new MetadataFileResult(Path.Combine(series.Path, "tvshow.nfo"), doc.ToString());
|
||||||
|
|
||||||
_diskProvider.WriteAllText(path, doc.ToString());
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeriesMetadata) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeriesMetadata,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, path)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<MetadataFile> WriteSeriesImages(Series series, List<MetadataFile> existingMetadataFiles)
|
public override MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile)
|
||||||
{
|
{
|
||||||
foreach (var image in series.Images)
|
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
|
||||||
{
|
|
||||||
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
|
||||||
var destination = Path.Combine(series.Path, image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(source));
|
|
||||||
|
|
||||||
//TODO: Do we want to overwrite the file if it exists?
|
|
||||||
if (_diskProvider.FileExists(destination))
|
|
||||||
{
|
|
||||||
_logger.Debug("Series image: {0} already exists.", image.CoverType);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskProvider.CopyFile(source, destination, false);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, destination);
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeriesImage &&
|
|
||||||
c.RelativePath == relativePath) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeriesImage,
|
|
||||||
RelativePath = relativePath
|
|
||||||
};
|
|
||||||
|
|
||||||
yield return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<MetadataFile> WriteSeasonImages(Series series, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
foreach (var season in series.Seasons)
|
|
||||||
{
|
|
||||||
foreach (var image in season.Images)
|
|
||||||
{
|
|
||||||
var filename = String.Format("season{0:00}-{1}.jpg", season.SeasonNumber, image.CoverType.ToString().ToLower());
|
|
||||||
|
|
||||||
if (season.SeasonNumber == 0)
|
|
||||||
{
|
|
||||||
filename = String.Format("season-specials-{0}.jpg", image.CoverType.ToString().ToLower());
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = Path.Combine(series.Path, filename);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, path);
|
|
||||||
|
|
||||||
DownloadImage(series, image.Url, path);
|
|
||||||
|
|
||||||
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeasonImage &&
|
|
||||||
c.SeasonNumber == season.SeasonNumber &&
|
|
||||||
c.RelativePath == relativePath) ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
SeasonNumber = season.SeasonNumber,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.SeasonImage,
|
|
||||||
RelativePath = relativePath
|
|
||||||
};
|
|
||||||
|
|
||||||
yield return metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeNfo(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
|
||||||
{
|
|
||||||
var filename = GetEpisodeNfoFilename(episodeFile.Path);
|
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Generating {0} for: {1}", filename, episodeFile.Path);
|
|
||||||
|
|
||||||
var xmlResult = String.Empty;
|
var xmlResult = String.Empty;
|
||||||
foreach (var episode in episodeFile.Episodes.Value)
|
foreach (var episode in episodeFile.Episodes.Value)
|
||||||
@ -426,7 +248,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
}
|
}
|
||||||
|
|
||||||
details.Add(new XElement("watched", "false"));
|
details.Add(new XElement("watched", "false"));
|
||||||
details.Add(new XElement("rating", (decimal)episode.Ratings.Percentage/10));
|
details.Add(new XElement("rating", (decimal)episode.Ratings.Percentage / 10));
|
||||||
|
|
||||||
//Todo: get guest stars, writer and director
|
//Todo: get guest stars, writer and director
|
||||||
//details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault()));
|
//details.Add(new XElement("credits", tvdbEpisode.Writer.FirstOrDefault()));
|
||||||
@ -440,24 +262,36 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Debug("Saving episodedetails to: {0}", filename);
|
return new MetadataFileResult(GetEpisodeNfoFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
||||||
_diskProvider.WriteAllText(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeMetadata,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataFile WriteEpisodeImages(Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
public override List<ImageFileResult> SeriesImages(Series series)
|
||||||
{
|
{
|
||||||
|
if (!Settings.SeriesImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProcessSeriesImages(series).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> SeasonImages(Series series, Season season)
|
||||||
|
{
|
||||||
|
if (!Settings.SeasonImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProcessSeasonImages(series, season).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
if (!Settings.EpisodeImages)
|
||||||
|
{
|
||||||
|
return new List<ImageFileResult>();
|
||||||
|
}
|
||||||
|
|
||||||
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
var screenshot = episodeFile.Episodes.Value.First().Images.SingleOrDefault(i => i.CoverType == MediaCoverTypes.Screenshot);
|
||||||
|
|
||||||
if (screenshot == null)
|
if (screenshot == null)
|
||||||
@ -466,35 +300,38 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename = GetEpisodeImageFilename(episodeFile.Path);
|
return new List<ImageFileResult>
|
||||||
var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename);
|
|
||||||
|
|
||||||
var existingMetadata = existingMetadataFiles.FirstOrDefault(c => c.Type == MetadataType.EpisodeImage &&
|
|
||||||
c.EpisodeFileId == episodeFile.Id);
|
|
||||||
|
|
||||||
if (existingMetadata != null)
|
|
||||||
{
|
{
|
||||||
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url)
|
||||||
if (!filename.PathEquals(fullPath))
|
|
||||||
{
|
|
||||||
_diskProvider.MoveFile(fullPath, filename);
|
|
||||||
existingMetadata.RelativePath = relativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadImage(series, screenshot.Url, filename);
|
|
||||||
|
|
||||||
var metadata = existingMetadata ??
|
|
||||||
new MetadataFile
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
EpisodeFileId = episodeFile.Id,
|
|
||||||
Consumer = GetType().Name,
|
|
||||||
Type = MetadataType.EpisodeImage,
|
|
||||||
RelativePath = DiskProviderBase.GetRelativePath(series.Path, filename)
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return metadata;
|
private IEnumerable<ImageFileResult> ProcessSeriesImages(Series series)
|
||||||
|
{
|
||||||
|
foreach (var image in series.Images)
|
||||||
|
{
|
||||||
|
var source = _mediaCoverService.GetCoverPath(series.Id, image.CoverType);
|
||||||
|
var destination = Path.Combine(series.Path, image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(source));
|
||||||
|
|
||||||
|
yield return new ImageFileResult(destination, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ImageFileResult> ProcessSeasonImages(Series series, Season season)
|
||||||
|
{
|
||||||
|
foreach (var image in season.Images)
|
||||||
|
{
|
||||||
|
var filename = String.Format("season{0:00}-{1}.jpg", season.SeasonNumber, image.CoverType.ToString().ToLower());
|
||||||
|
|
||||||
|
if (season.SeasonNumber == 0)
|
||||||
|
{
|
||||||
|
filename = String.Format("season-specials-{0}.jpg", image.CoverType.ToString().ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = Path.Combine(series.Path, filename);
|
||||||
|
|
||||||
|
yield return new ImageFileResult(Path.Combine(series.Path, filename), image.Url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetEpisodeNfoFilename(string episodeFilePath)
|
private string GetEpisodeNfoFilename(string episodeFilePath)
|
||||||
|
16
src/NzbDrone.Core/MetaData/Files/ImageFileResult.cs
Normal file
16
src/NzbDrone.Core/MetaData/Files/ImageFileResult.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Metadata.Files
|
||||||
|
{
|
||||||
|
public class ImageFileResult
|
||||||
|
{
|
||||||
|
public String Path { get; set; }
|
||||||
|
public String Url { get; set; }
|
||||||
|
|
||||||
|
public ImageFileResult(string path, string url)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/NzbDrone.Core/MetaData/Files/MetadataFileResult.cs
Normal file
16
src/NzbDrone.Core/MetaData/Files/MetadataFileResult.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Metadata.Files
|
||||||
|
{
|
||||||
|
public class MetadataFileResult
|
||||||
|
{
|
||||||
|
public String Path { get; set; }
|
||||||
|
public String Contents { get; set; }
|
||||||
|
|
||||||
|
public MetadataFileResult(string path, string contents)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Contents = contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,14 @@ namespace NzbDrone.Core.Metadata
|
|||||||
{
|
{
|
||||||
public interface IMetadata : IProvider
|
public interface IMetadata : IProvider
|
||||||
{
|
{
|
||||||
void OnSeriesUpdated(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
||||||
void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload);
|
|
||||||
void AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
|
||||||
MetadataFile FindMetadataFile(Series series, string path);
|
MetadataFile FindMetadataFile(Series series, string path);
|
||||||
|
|
||||||
|
MetadataFileResult SeriesMetadata(Series series);
|
||||||
|
MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile);
|
||||||
|
List<ImageFileResult> SeriesImages(Series series);
|
||||||
|
List<ImageFileResult> SeasonImages(Series series, Season season);
|
||||||
|
List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
@ -21,6 +27,9 @@ namespace NzbDrone.Core.Metadata
|
|||||||
private readonly ICleanMetadataService _cleanMetadataService;
|
private readonly ICleanMetadataService _cleanMetadataService;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IHttpProvider _httpProvider;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public MetadataService(IMetadataFactory metadataFactory,
|
public MetadataService(IMetadataFactory metadataFactory,
|
||||||
@ -28,6 +37,9 @@ namespace NzbDrone.Core.Metadata
|
|||||||
ICleanMetadataService cleanMetadataService,
|
ICleanMetadataService cleanMetadataService,
|
||||||
IMediaFileService mediaFileService,
|
IMediaFileService mediaFileService,
|
||||||
IEpisodeService episodeService,
|
IEpisodeService episodeService,
|
||||||
|
IDiskProvider diskProvider,
|
||||||
|
IHttpProvider httpProvider,
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_metadataFactory = metadataFactory;
|
_metadataFactory = metadataFactory;
|
||||||
@ -35,17 +47,41 @@ namespace NzbDrone.Core.Metadata
|
|||||||
_cleanMetadataService = cleanMetadataService;
|
_cleanMetadataService = cleanMetadataService;
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_httpProvider = httpProvider;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(MediaCoversUpdatedEvent message)
|
public void Handle(MediaCoversUpdatedEvent message)
|
||||||
{
|
{
|
||||||
_cleanMetadataService.Clean(message.Series);
|
_cleanMetadataService.Clean(message.Series);
|
||||||
var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id);
|
|
||||||
|
if (!_diskProvider.FolderExists(message.Series.Path))
|
||||||
|
{
|
||||||
|
_logger.Info("Series folder does not exist, skipping metadata creation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seriesMetadataFiles = _metadataFileService.GetFilesBySeries(message.Series.Id);
|
||||||
|
var episodeFiles = GetEpisodeFiles(message.Series.Id);
|
||||||
|
|
||||||
foreach (var consumer in _metadataFactory.Enabled())
|
foreach (var consumer in _metadataFactory.Enabled())
|
||||||
{
|
{
|
||||||
consumer.OnSeriesUpdated(message.Series, GetMetadataFilesForConsumer(consumer, seriesMetadata), GetEpisodeFiles(message.Series.Id));
|
var consumerFiles = GetMetadataFilesForConsumer(consumer, seriesMetadataFiles);
|
||||||
|
var files = new List<MetadataFile>();
|
||||||
|
|
||||||
|
files.AddIfNotNull(ProcessSeriesMetadata(consumer, message.Series, consumerFiles));
|
||||||
|
files.AddRange(ProcessSeriesImages(consumer, message.Series, consumerFiles));
|
||||||
|
files.AddRange(ProcessSeasonImages(consumer, message.Series, consumerFiles));
|
||||||
|
|
||||||
|
foreach (var episodeFile in episodeFiles)
|
||||||
|
{
|
||||||
|
files.AddIfNotNull(ProcessEpisodeMetadata(consumer, message.Series, episodeFile, consumerFiles));
|
||||||
|
files.AddRange(ProcessEpisodeImages(consumer, message.Series, episodeFile, consumerFiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new MetadataFilesUpdated(files));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,17 +89,27 @@ namespace NzbDrone.Core.Metadata
|
|||||||
{
|
{
|
||||||
foreach (var consumer in _metadataFactory.Enabled())
|
foreach (var consumer in _metadataFactory.Enabled())
|
||||||
{
|
{
|
||||||
consumer.OnEpisodeImport(message.EpisodeInfo.Series, message.ImportedEpisode, message.NewDownload);
|
var files = new List<MetadataFile>();
|
||||||
|
|
||||||
|
files.AddIfNotNull(ProcessEpisodeMetadata(consumer, message.EpisodeInfo.Series, message.ImportedEpisode, new List<MetadataFile>()));
|
||||||
|
files.AddRange(ProcessEpisodeImages(consumer, message.EpisodeInfo.Series, message.ImportedEpisode, new List<MetadataFile>()));
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new MetadataFilesUpdated(files));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(SeriesRenamedEvent message)
|
public void Handle(SeriesRenamedEvent message)
|
||||||
{
|
{
|
||||||
var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id);
|
var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id);
|
||||||
|
var episodeFiles = GetEpisodeFiles(message.Series.Id);
|
||||||
|
|
||||||
foreach (var consumer in _metadataFactory.Enabled())
|
foreach (var consumer in _metadataFactory.Enabled())
|
||||||
{
|
{
|
||||||
consumer.AfterRename(message.Series, GetMetadataFilesForConsumer(consumer, seriesMetadata), GetEpisodeFiles(message.Series.Id));
|
var updatedMetadataFiles = consumer.AfterRename(message.Series,
|
||||||
|
GetMetadataFilesForConsumer(consumer, seriesMetadata),
|
||||||
|
episodeFiles);
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new MetadataFilesUpdated(updatedMetadataFiles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,5 +131,219 @@ namespace NzbDrone.Core.Metadata
|
|||||||
{
|
{
|
||||||
return seriesMetadata.Where(c => c.Consumer == consumer.GetType().Name).ToList();
|
return seriesMetadata.Where(c => c.Consumer == consumer.GetType().Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MetadataFile ProcessSeriesMetadata(IMetadata consumer, Series series, List<MetadataFile> existingMetadataFiles)
|
||||||
|
{
|
||||||
|
var seriesMetadata = consumer.SeriesMetadata(series);
|
||||||
|
|
||||||
|
if (seriesMetadata == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = seriesMetadata.Contents.SHA256Hash();
|
||||||
|
|
||||||
|
var metadata = existingMetadataFiles.SingleOrDefault(e => e.Type == MetadataType.SeriesMetadata) ??
|
||||||
|
new MetadataFile
|
||||||
|
{
|
||||||
|
SeriesId = series.Id,
|
||||||
|
Consumer = consumer.GetType().Name,
|
||||||
|
Type = MetadataType.SeriesMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hash == metadata.Hash)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Writing Series Metadata to: {0}", seriesMetadata.Path);
|
||||||
|
_diskProvider.WriteAllText(seriesMetadata.Path, seriesMetadata.Contents);
|
||||||
|
|
||||||
|
metadata.Hash = hash;
|
||||||
|
metadata.RelativePath = DiskProviderBase.GetRelativePath(series.Path, seriesMetadata.Path);
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
||||||
|
{
|
||||||
|
var episodeMetadata = consumer.EpisodeMetadata(series, episodeFile);
|
||||||
|
|
||||||
|
if (episodeMetadata == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = DiskProviderBase.GetRelativePath(series.Path, episodeMetadata.Path);
|
||||||
|
|
||||||
|
var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeMetadata &&
|
||||||
|
c.EpisodeFileId == episodeFile.Id);
|
||||||
|
|
||||||
|
if (existingMetadata != null)
|
||||||
|
{
|
||||||
|
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
||||||
|
if (!episodeMetadata.Path.PathEquals(fullPath))
|
||||||
|
{
|
||||||
|
_diskProvider.MoveFile(fullPath, episodeMetadata.Path);
|
||||||
|
existingMetadata.RelativePath = relativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = episodeMetadata.Contents.SHA256Hash();
|
||||||
|
|
||||||
|
var metadata = existingMetadata ??
|
||||||
|
new MetadataFile
|
||||||
|
{
|
||||||
|
SeriesId = series.Id,
|
||||||
|
EpisodeFileId = episodeFile.Id,
|
||||||
|
Consumer = consumer.GetType().Name,
|
||||||
|
Type = MetadataType.EpisodeMetadata,
|
||||||
|
RelativePath = relativePath
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hash == metadata.Hash)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Writing Episode Metadata to: {0}", episodeMetadata.Path);
|
||||||
|
_diskProvider.WriteAllText(episodeMetadata.Path, episodeMetadata.Contents);
|
||||||
|
|
||||||
|
metadata.Hash = hash;
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MetadataFile> ProcessSeriesImages(IMetadata consumer, Series series, List<MetadataFile> existingMetadataFiles)
|
||||||
|
{
|
||||||
|
var result = new List<MetadataFile>();
|
||||||
|
|
||||||
|
foreach (var image in consumer.SeriesImages(series))
|
||||||
|
{
|
||||||
|
if (_diskProvider.FileExists(image.Path))
|
||||||
|
{
|
||||||
|
_logger.Debug("Series image already exists: {0}", image.Path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = DiskProviderBase.GetRelativePath(series.Path, image.Path);
|
||||||
|
|
||||||
|
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeriesImage &&
|
||||||
|
c.RelativePath == relativePath) ??
|
||||||
|
new MetadataFile
|
||||||
|
{
|
||||||
|
SeriesId = series.Id,
|
||||||
|
Consumer = consumer.GetType().Name,
|
||||||
|
Type = MetadataType.SeriesImage,
|
||||||
|
RelativePath = relativePath
|
||||||
|
};
|
||||||
|
|
||||||
|
_diskProvider.CopyFile(image.Url, image.Path);
|
||||||
|
|
||||||
|
result.Add(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MetadataFile> ProcessSeasonImages(IMetadata consumer, Series series, List<MetadataFile> existingMetadataFiles)
|
||||||
|
{
|
||||||
|
var result = new List<MetadataFile>();
|
||||||
|
|
||||||
|
foreach (var season in series.Seasons)
|
||||||
|
{
|
||||||
|
foreach (var image in consumer.SeasonImages(series, season))
|
||||||
|
{
|
||||||
|
if (_diskProvider.FileExists(image.Path))
|
||||||
|
{
|
||||||
|
_logger.Debug("Season image already exists: {0}", image.Path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = DiskProviderBase.GetRelativePath(series.Path, image.Path);
|
||||||
|
|
||||||
|
var metadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.SeasonImage &&
|
||||||
|
c.SeasonNumber == season.SeasonNumber &&
|
||||||
|
c.RelativePath == relativePath) ??
|
||||||
|
new MetadataFile
|
||||||
|
{
|
||||||
|
SeriesId = series.Id,
|
||||||
|
SeasonNumber = season.SeasonNumber,
|
||||||
|
Consumer = consumer.GetType().Name,
|
||||||
|
Type = MetadataType.SeasonImage,
|
||||||
|
RelativePath = relativePath
|
||||||
|
};
|
||||||
|
|
||||||
|
DownloadImage(series, image.Url, image.Path);
|
||||||
|
|
||||||
|
result.Add(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MetadataFile> ProcessEpisodeImages(IMetadata consumer, Series series, EpisodeFile episodeFile, List<MetadataFile> existingMetadataFiles)
|
||||||
|
{
|
||||||
|
var result = new List<MetadataFile>();
|
||||||
|
|
||||||
|
foreach (var image in consumer.EpisodeImages(series, episodeFile))
|
||||||
|
{
|
||||||
|
if (_diskProvider.FileExists(image.Path))
|
||||||
|
{
|
||||||
|
_logger.Debug("Episode image already exists: {0}", image.Path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = DiskProviderBase.GetRelativePath(series.Path, image.Path);
|
||||||
|
|
||||||
|
var existingMetadata = existingMetadataFiles.FirstOrDefault(c => c.Type == MetadataType.EpisodeImage &&
|
||||||
|
c.EpisodeFileId == episodeFile.Id);
|
||||||
|
|
||||||
|
if (existingMetadata != null)
|
||||||
|
{
|
||||||
|
var fullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
|
||||||
|
if (!image.Path.PathEquals(fullPath))
|
||||||
|
{
|
||||||
|
_diskProvider.MoveFile(fullPath, image.Path);
|
||||||
|
existingMetadata.RelativePath = relativePath;
|
||||||
|
|
||||||
|
return new List<MetadataFile>{ existingMetadata };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata = existingMetadata ??
|
||||||
|
new MetadataFile
|
||||||
|
{
|
||||||
|
SeriesId = series.Id,
|
||||||
|
EpisodeFileId = episodeFile.Id,
|
||||||
|
Consumer = consumer.GetType().Name,
|
||||||
|
Type = MetadataType.EpisodeImage,
|
||||||
|
RelativePath = DiskProviderBase.GetRelativePath(series.Path, image.Path)
|
||||||
|
};
|
||||||
|
|
||||||
|
DownloadImage(series, image.Url, image.Path);
|
||||||
|
|
||||||
|
result.Add(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadImage(Series series, string url, string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpProvider.DownloadFile(url, path);
|
||||||
|
}
|
||||||
|
catch (WebException e)
|
||||||
|
{
|
||||||
|
_logger.Warn(string.Format("Couldn't download image {0} for {1}. {2}", url, series, e.Message));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Couldn't download image " + url + " for " + series, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ using NLog;
|
|||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Metadata.Files;
|
using NzbDrone.Core.Metadata.Files;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
using NzbDrone.Core.Tv.Events;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Metadata
|
namespace NzbDrone.Core.Metadata
|
||||||
{
|
{
|
||||||
public class ExistingMetadataService : IHandle<SeriesUpdatedEvent>
|
public class ExistingMetadataService : IHandle<SeriesScannedEvent>
|
||||||
{
|
{
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IMetadataFileService _metadataFileService;
|
private readonly IMetadataFileService _metadataFileService;
|
||||||
@ -33,7 +33,7 @@ namespace NzbDrone.Core.Metadata
|
|||||||
_consumers = consumers.ToList();
|
_consumers = consumers.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(SeriesUpdatedEvent message)
|
public void Handle(SeriesScannedEvent message)
|
||||||
{
|
{
|
||||||
if (!_diskProvider.FolderExists(message.Series.Path)) return;
|
if (!_diskProvider.FolderExists(message.Series.Path)) return;
|
||||||
|
|
||||||
|
@ -12,5 +12,6 @@ namespace NzbDrone.Core.Metadata.Files
|
|||||||
public DateTime LastUpdated { get; set; }
|
public DateTime LastUpdated { get; set; }
|
||||||
public Int32? EpisodeFileId { get; set; }
|
public Int32? EpisodeFileId { get; set; }
|
||||||
public Int32? SeasonNumber { get; set; }
|
public Int32? SeasonNumber { get; set; }
|
||||||
|
public String Hash { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,6 @@ namespace NzbDrone.Core.Metadata
|
|||||||
{
|
{
|
||||||
public abstract class MetadataBase<TSettings> : IMetadata where TSettings : IProviderConfig, new()
|
public abstract class MetadataBase<TSettings> : IMetadata where TSettings : IProviderConfig, new()
|
||||||
{
|
{
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IHttpProvider _httpProvider;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
protected MetadataBase(IDiskProvider diskProvider, IHttpProvider httpProvider, Logger logger)
|
|
||||||
{
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_httpProvider = httpProvider;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type ConfigContract
|
public Type ConfigContract
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -43,11 +32,15 @@ namespace NzbDrone.Core.Metadata
|
|||||||
|
|
||||||
public ProviderDefinition Definition { get; set; }
|
public ProviderDefinition Definition { get; set; }
|
||||||
|
|
||||||
public abstract void OnSeriesUpdated(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
public abstract List<MetadataFile> AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
||||||
public abstract void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload);
|
|
||||||
public abstract void AfterRename(Series series, List<MetadataFile> existingMetadataFiles, List<EpisodeFile> episodeFiles);
|
|
||||||
public abstract MetadataFile FindMetadataFile(Series series, string path);
|
public abstract MetadataFile FindMetadataFile(Series series, string path);
|
||||||
|
|
||||||
|
public abstract MetadataFileResult SeriesMetadata(Series series);
|
||||||
|
public abstract MetadataFileResult EpisodeMetadata(Series series, EpisodeFile episodeFile);
|
||||||
|
public abstract List<ImageFileResult> SeriesImages(Series series);
|
||||||
|
public abstract List<ImageFileResult> SeasonImages(Series series, Season season);
|
||||||
|
public abstract List<ImageFileResult> EpisodeImages(Series series, EpisodeFile episodeFile);
|
||||||
|
|
||||||
protected TSettings Settings
|
protected TSettings Settings
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -56,28 +49,6 @@ namespace NzbDrone.Core.Metadata
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void DownloadImage(Series series, string url, string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_diskProvider.FileExists(path))
|
|
||||||
{
|
|
||||||
_logger.Debug("Image already exists: {0}, will not download again.", path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_httpProvider.DownloadFile(url, path);
|
|
||||||
}
|
|
||||||
catch (WebException e)
|
|
||||||
{
|
|
||||||
_logger.Warn(string.Format("Couldn't download image {0} for {1}. {2}", url, series, e.Message));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Couldn't download image " + url + " for " + series, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return GetType().Name;
|
return GetType().Name;
|
||||||
|
@ -195,6 +195,7 @@
|
|||||||
<Compile Include="Datastore\Migration\047_add_published_date_blacklist_column.cs" />
|
<Compile Include="Datastore\Migration\047_add_published_date_blacklist_column.cs" />
|
||||||
<Compile Include="Datastore\Migration\048_add_title_to_scenemappings.cs" />
|
<Compile Include="Datastore\Migration\048_add_title_to_scenemappings.cs" />
|
||||||
<Compile Include="Datastore\Migration\049_fix_dognzb_url.cs" />
|
<Compile Include="Datastore\Migration\049_fix_dognzb_url.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\049_add_hash_to_metadata_files.cs" />
|
||||||
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
||||||
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
||||||
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
|
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
|
||||||
@ -362,6 +363,8 @@
|
|||||||
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadata.cs" />
|
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadata.cs" />
|
||||||
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadataSettings.cs" />
|
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadataSettings.cs" />
|
||||||
<Compile Include="Metadata\ExistingMetadataService.cs" />
|
<Compile Include="Metadata\ExistingMetadataService.cs" />
|
||||||
|
<Compile Include="Metadata\Files\ImageFileResult.cs" />
|
||||||
|
<Compile Include="Metadata\Files\MetadataFileResult.cs" />
|
||||||
<Compile Include="Metadata\Files\MetadataFilesUpdated.cs" />
|
<Compile Include="Metadata\Files\MetadataFilesUpdated.cs" />
|
||||||
<Compile Include="Metadata\Files\MetadataFile.cs" />
|
<Compile Include="Metadata\Files\MetadataFile.cs" />
|
||||||
<Compile Include="Metadata\Files\MetadataFileRepository.cs" />
|
<Compile Include="Metadata\Files\MetadataFileRepository.cs" />
|
||||||
@ -540,6 +543,7 @@
|
|||||||
<Compile Include="Qualities\QualityProfileItem.cs" />
|
<Compile Include="Qualities\QualityProfileItem.cs" />
|
||||||
<Compile Include="Rest\JsonNetSerializer.cs" />
|
<Compile Include="Rest\JsonNetSerializer.cs" />
|
||||||
<Compile Include="RootFolders\RootFolderRepository.cs" />
|
<Compile Include="RootFolders\RootFolderRepository.cs" />
|
||||||
|
<Compile Include="Security.cs" />
|
||||||
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
|
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
|
||||||
<Compile Include="ThingiProvider\Events\ProviderUpdatedEvent.cs" />
|
<Compile Include="ThingiProvider\Events\ProviderUpdatedEvent.cs" />
|
||||||
<Compile Include="ThingiProvider\IProvider.cs" />
|
<Compile Include="ThingiProvider\IProvider.cs" />
|
||||||
|
26
src/NzbDrone.Core/Security.cs
Normal file
26
src/NzbDrone.Core/Security.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core
|
||||||
|
{
|
||||||
|
public static class Security
|
||||||
|
{
|
||||||
|
public static string SHA256Hash(this string input)
|
||||||
|
{
|
||||||
|
var stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
using (var hash = SHA256Managed.Create())
|
||||||
|
{
|
||||||
|
var enc = Encoding.UTF8;
|
||||||
|
var result = hash.ComputeHash(enc.GetBytes(input));
|
||||||
|
|
||||||
|
foreach (var b in result)
|
||||||
|
{
|
||||||
|
stringBuilder.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user