1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-14 11:23:42 +02:00

Almost everything working except importing episode thumbs

This commit is contained in:
Mark McDowall 2014-01-24 08:17:56 -08:00
parent a6361d0bbd
commit 3ca5e478ff
24 changed files with 509 additions and 174 deletions

View File

@ -34,10 +34,7 @@ public static string GetRelativePath(string parentPath, string childPath)
throw new NotParentException("{0} is not a child of {1}", childPath, parentPath);
}
var parentUri = new Uri(parentPath, UriKind.Absolute);
var childUri = new Uri(childPath, UriKind.Absolute);
return childUri.MakeRelativeUri(parentUri).ToString();
return childPath.Substring(parentPath.Length).Trim(Path.DirectorySeparatorChar);
}
public static bool IsParent(string parentPath, string childPath)

View File

@ -33,13 +33,6 @@ private void GivenChildOfSeries()
_localEpisode.ExistingFile = true;
}
private void GivenNewFile()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsParent(_localEpisode.Series.Path, _localEpisode.Path))
.Returns(false);
}
[Test]
public void should_return_true_if_file_is_under_series_folder()
{
@ -62,8 +55,6 @@ public void should_not_check_for_file_in_use_if_child_of_series_folder()
[Test]
public void should_return_false_if_file_is_in_use()
{
GivenNewFile();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsFileLocked(It.IsAny<string>()))
.Returns(true);
@ -74,8 +65,6 @@ public void should_return_false_if_file_is_in_use()
[Test]
public void should_return_true_if_file_is_not_in_use()
{
GivenNewFile();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsFileLocked(It.IsAny<string>()))
.Returns(false);

View File

@ -36,10 +36,6 @@ public void SetUp()
Mocker.GetMock<IEpisodeService>()
.Setup(c => c.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(_episodes);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsParent(It.IsAny<String>(), It.IsAny<String>()))
.Returns(true);
}
private void GivenEpisodeFiles(IEnumerable<EpisodeFile> episodeFiles)
@ -58,13 +54,6 @@ private void GivenFilesAreNotAttachedToEpisode()
.Returns(_episodes);
}
private void GivenFileIsNotInSeriesFolder()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.IsParent(It.IsAny<String>(), It.IsAny<String>()))
.Returns(false);
}
[Test]
public void should_skip_files_that_exist_in_disk()
{
@ -118,7 +107,6 @@ public void should_delete_files_that_do_not_belong_to_the_series_path()
.Build();
GivenEpisodeFiles(episodeFiles);
GivenFileIsNotInSeriesFolder();
Subject.Execute(new CleanMediaFileDb(0));

View File

@ -1,19 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(37)]
public class add_episode_file_metadata : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("EpisodeFileMetaData")
.WithColumn("SeriesId").AsInt32().NotNullable()
.WithColumn("EpisodeFileId").AsInt32().NotNullable()
.WithColumn("Provider").AsString().NotNullable()
.WithColumn("Type").AsInt32().NotNullable()
.WithColumn("LastUpdated").AsDateTime().NotNullable()
}
}
}

View File

@ -1,19 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(37)]
public class add_metadata_consumers : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("MetadataConsumers")
.WithColumn("Enable").AsBoolean().NotNullable()
.WithColumn("Name").AsString().NotNullable()
.WithColumn("Implementation").AsString().NotNullable()
.WithColumn("Settings").AsString().NotNullable()
.WithColumn("ConfigContract").AsString().NotNullable();
}
}
}

View File

@ -0,0 +1,28 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(37)]
public class add_metadata_tables : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("Metadata")
.WithColumn("Enable").AsBoolean().NotNullable()
.WithColumn("Name").AsString().NotNullable()
.WithColumn("Implementation").AsString().NotNullable()
.WithColumn("Settings").AsString().NotNullable()
.WithColumn("ConfigContract").AsString().NotNullable();
Create.TableForModel("MetadataFiles")
.WithColumn("SeriesId").AsInt32().NotNullable()
.WithColumn("Consumer").AsString().NotNullable()
.WithColumn("Type").AsInt32().NotNullable()
.WithColumn("RelativePath").AsString().NotNullable()
.WithColumn("LastUpdated").AsDateTime().NotNullable()
.WithColumn("SeasonNumber").AsInt32().Nullable()
.WithColumn("EpisodeFileId").AsInt32().Nullable();
}
}
}

View File

@ -12,6 +12,7 @@
using NzbDrone.Core.Jobs;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Metadata;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
@ -37,7 +38,7 @@ public static void Map()
Mapper.Entity<IndexerDefinition>().RegisterModel("Indexers");
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks");
Mapper.Entity<NotificationDefinition>().RegisterModel("Notifications");
Mapper.Entity<MetadataDefinition>().RegisterModel("MetadataConsumers");
Mapper.Entity<MetadataDefinition>().RegisterModel("Metadata");
Mapper.Entity<SceneMapping>().RegisterModel("SceneMappings");
@ -60,16 +61,13 @@ public static void Map()
.Relationships.AutoMapICollectionOrComplexProperties();
Mapper.Entity<QualityProfile>().RegisterModel("QualityProfiles");
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions");
Mapper.Entity<Log>().RegisterModel("Logs");
Mapper.Entity<NamingConfig>().RegisterModel("NamingConfig");
Mapper.Entity<SeriesStatistics>().MapResultSet();
Mapper.Entity<Blacklist>().RegisterModel("Blacklist");
Mapper.Entity<MetadataFile>().RegisterModel("MetadataFiles");
}
private static void RegisterMappers()

View File

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
@ -9,6 +12,7 @@ public interface IMediaFileRepository : IBasicRepository<EpisodeFile>
{
List<EpisodeFile> GetFilesBySeries(int seriesId);
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
EpisodeFile FindFileByPath(string path, bool includeExtension = true);
}
@ -31,5 +35,14 @@ public List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber)
.ToList();
}
public EpisodeFile FindFileByPath(string path, bool includeExtension = true)
{
if (includeExtension)
{
return Query.SingleOrDefault(c => c.Path == path);
}
return Query.SingleOrDefault(c => c.Path.StartsWith(Path.ChangeExtension(path, "")));
}
}
}

View File

@ -19,6 +19,7 @@ public interface IMediaFileService
List<string> FilterExistingFiles(List<string> files, int seriesId);
EpisodeFile Get(int id);
List<EpisodeFile> Get(IEnumerable<int> ids);
EpisodeFile FindByPath(string path, bool includeExtension = true);
}
public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent>
@ -82,6 +83,11 @@ public List<EpisodeFile> Get(IEnumerable<int> ids)
return _mediaFileRepository.Get(ids).ToList();
}
public EpisodeFile FindByPath(string path, bool includeExtension = true)
{
return _mediaFileRepository.FindFileByPath(path, includeExtension);
}
public void HandleAsync(SeriesDeletedEvent message)
{
var files = GetFilesBySeries(message.Series.Id);

View File

@ -7,11 +7,12 @@
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata.Consumers.Fake
{
public class FakeMetadata : MetadataConsumerBase<FakeMetadataSettings>
public class FakeMetadata : MetadataBase<FakeMetadataSettings>
{
private readonly IDiskProvider _diskProvider;
private readonly IHttpProvider _httpProvider;
@ -25,19 +26,24 @@ public FakeMetadata(IDiskProvider diskProvider, IHttpProvider httpProvider, Logg
_logger = logger;
}
public override void OnSeriesUpdated(Series series)
public override void OnSeriesUpdated(Tv.Series series)
{
throw new NotImplementedException();
}
public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload)
public override void OnEpisodeImport(Tv.Series series, EpisodeFile episodeFile, bool newDownload)
{
throw new NotImplementedException();
}
public override void AfterRename(Series series)
public override void AfterRename(Tv.Series series)
{
throw new NotImplementedException();
}
public override MetadataFile FindMetadataFile(Series series, string path)
{
return null;
}
}
}

View File

@ -1,8 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using NLog;
@ -10,21 +10,25 @@
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Metadata.Events;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata.Consumers.Xbmc
{
public class XbmcMetadata : MetadataConsumerBase<XbmcMetadataSettings>
public class XbmcMetadata : MetadataBase<XbmcMetadataSettings>
{
private readonly IEventAggregator _eventAggregator;
private readonly IMapCoversToLocal _mediaCoverService;
private readonly IMediaFileService _mediaFileService;
private readonly IMetadataFileService _metadataFileService;
private readonly IDiskProvider _diskProvider;
private readonly IHttpProvider _httpProvider;
private readonly Logger _logger;
public XbmcMetadata(IEventAggregator eventAggregator,
IMapCoversToLocal mediaCoverService,
IMediaFileService mediaFileService,
IMetadataFileService metadataFileService,
IDiskProvider diskProvider,
IHttpProvider httpProvider,
Logger logger)
@ -32,11 +36,17 @@ public XbmcMetadata(IEventAggregator eventAggregator,
{
_eventAggregator = eventAggregator;
_mediaCoverService = mediaCoverService;
_mediaFileService = mediaFileService;
_metadataFileService = metadataFileService;
_diskProvider = diskProvider;
_httpProvider = httpProvider;
_logger = logger;
}
private static readonly Regex SeriesImagesRegex = new Regex(@"^(?<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);
public override void OnSeriesUpdated(Series series)
{
if (Settings.SeriesMetadata)
@ -62,7 +72,7 @@ public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, boo
{
if (Settings.EpisodeMetadata)
{
WriteEpisodeNfo(episodeFile);
WriteEpisodeNfo(series, episodeFile);
}
if (Settings.EpisodeImages)
@ -73,9 +83,97 @@ public override void OnEpisodeImport(Series series, EpisodeFile episodeFile, boo
public override void AfterRename(Series series)
{
//TODO: Rename media files to match episode files
var episodeFiles = _mediaFileService.GetFilesBySeries(series.Id);
var episodeFilesMetadata = _metadataFileService.GetFilesBySeries(series.Id).Where(c => c.EpisodeFileId > 0).ToList();
throw new NotImplementedException();
foreach (var episodeFile in episodeFiles)
{
var metadataFiles = episodeFilesMetadata.Where(m => m.EpisodeFileId == episodeFile.Id).ToList();
var episodeFilenameWithoutExtension =
Path.GetFileNameWithoutExtension(DiskProvider.GetRelativePath(series.Path, episodeFile.Path));
foreach (var metadataFile in metadataFiles)
{
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(metadataFile.RelativePath);
var extension = Path.GetExtension(metadataFile.RelativePath);
if (!fileNameWithoutExtension.Equals(episodeFilenameWithoutExtension))
{
var source = Path.Combine(series.Path, metadataFile.RelativePath);
var destination = Path.Combine(series.Path, fileNameWithoutExtension + extension);
_diskProvider.MoveFile(source, destination);
metadataFile.RelativePath = fileNameWithoutExtension + extension;
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadataFile));
}
}
}
}
public override MetadataFile FindMetadataFile(Series series, string path)
{
var filename = Path.GetFileName(path);
if (filename == null) return null;
var metadata = new MetadataFile
{
SeriesId = series.Id,
Consumer = GetType().Name,
RelativePath = DiskProvider.GetRelativePath(series.Path, path)
};
if (SeriesImagesRegex.IsMatch(filename))
{
metadata.Type = MetadataType.SeriesImage;
return metadata;
}
var seasonMatch = SeasonImagesRegex.Match(filename);
if (seasonMatch.Success)
{
metadata.Type = MetadataType.SeasonImage;
var seasonNumber = seasonMatch.Groups["season"].Value;
if (seasonNumber.Contains("specials"))
{
metadata.SeasonNumber = 0;
}
else
{
metadata.SeasonNumber = Convert.ToInt32(seasonNumber);
}
return metadata;
}
if (EpisodeImageRegex.IsMatch(filename))
{
metadata.Type = MetadataType.EpisodeImage;
return metadata;
}
if (filename.Equals("tvshow.nfo", StringComparison.InvariantCultureIgnoreCase))
{
metadata.Type = MetadataType.SeriesMetadata;
return metadata;
}
var parseResult = Parser.Parser.ParseTitle(filename);
if (parseResult != null &&
!parseResult.FullSeason &&
Path.GetExtension(filename) == ".nfo")
{
metadata.Type = MetadataType.EpisodeMetadata;
return metadata;
}
return null;
}
private void WriteTvShowNfo(Series series)
@ -130,7 +228,15 @@ private void WriteTvShowNfo(Series series)
_diskProvider.WriteAllText(path, doc.ToString());
_eventAggregator.PublishEvent(new SeriesMetadataUpdated(series, GetType().Name, MetadataType.SeriesMetadata, DiskProvider.GetRelativePath(series.Path, path)));
var metadata = new MetadataFile
{
SeriesId = series.Id,
Consumer = GetType().Name,
Type = MetadataType.SeriesMetadata,
RelativePath = DiskProvider.GetRelativePath(series.Path, path)
};
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadata));
}
}
@ -149,7 +255,16 @@ private void WriteSeriesImages(Series series)
}
_diskProvider.CopyFile(source, destination, false);
_eventAggregator.PublishEvent(new SeriesMetadataUpdated(series, GetType().Name, MetadataType.SeriesImage, DiskProvider.GetRelativePath(series.Path, destination)));
var metadata = new MetadataFile
{
SeriesId = series.Id,
Consumer = GetType().Name,
Type = MetadataType.SeriesImage,
RelativePath = DiskProvider.GetRelativePath(series.Path, destination)
};
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadata));
}
}
@ -169,12 +284,22 @@ private void WriteSeasonImages(Series series)
var path = Path.Combine(series.Path, filename);
DownloadImage(series, image.Url, path);
_eventAggregator.PublishEvent(new SeasonMetadataUpdated(series, season.SeasonNumber, GetType().Name, MetadataType.SeasonImage, DiskProvider.GetRelativePath(series.Path, path)));
var metadata = new MetadataFile
{
SeriesId = series.Id,
SeasonNumber = season.SeasonNumber,
Consumer = GetType().Name,
Type = MetadataType.SeasonImage,
RelativePath = DiskProvider.GetRelativePath(series.Path, path)
};
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadata));
}
}
}
private void WriteEpisodeNfo(EpisodeFile episodeFile)
private void WriteEpisodeNfo(Series series, EpisodeFile episodeFile)
{
var filename = episodeFile.Path.Replace(Path.GetExtension(episodeFile.Path), ".nfo");
@ -218,15 +343,37 @@ private void WriteEpisodeNfo(EpisodeFile episodeFile)
_logger.Debug("Saving episodedetails to: {0}", filename);
_diskProvider.WriteAllText(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
var metadata = new MetadataFile
{
SeriesId = series.Id,
EpisodeFileId = episodeFile.Id,
Consumer = GetType().Name,
Type = MetadataType.SeasonImage,
RelativePath = DiskProvider.GetRelativePath(series.Path, filename)
};
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadata));
}
private void WriteEpisodeImages(Series series, EpisodeFile episodeFile)
{
var screenshot = episodeFile.Episodes.Value.First().Images.Single(i => i.CoverType == MediaCoverTypes.Screenshot);
var filename = Path.ChangeExtension(episodeFile.Path, "jpg");
var filename = Path.GetFileNameWithoutExtension(episodeFile.Path) + "-thumb.jpg";
DownloadImage(series, screenshot.Url, filename);
_eventAggregator.PublishEvent(new EpisodeMetadataUpdated(series, episodeFile, GetType().Name, MetadataType.SeasonImage, DiskProvider.GetRelativePath(series.Path, filename)));
var metadata = new MetadataFile
{
SeriesId = series.Id,
EpisodeFileId = episodeFile.Id,
Consumer = GetType().Name,
Type = MetadataType.SeasonImage,
RelativePath = DiskProvider.GetRelativePath(series.Path, filename)
};
_eventAggregator.PublishEvent(new MetadataFileUpdated(metadata));
}
}
}

View File

@ -1,25 +0,0 @@
using System;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata.Events
{
public class EpisodeMetadataUpdated : IEvent
{
public Series Series { get; set; }
public EpisodeFile EpisodeFile { get; set; }
public String Consumer { get; set; }
public MetadataType MetadataType { get; set; }
public String Path { get; set; }
public EpisodeMetadataUpdated(Series series, EpisodeFile episodeFile, string consumer, MetadataType metadataType, string path)
{
Series = series;
EpisodeFile = episodeFile;
Consumer = consumer;
MetadataType = metadataType;
Path = path;
}
}
}

View File

@ -1,24 +0,0 @@
using System;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata.Events
{
public class SeasonMetadataUpdated : IEvent
{
public Series Series { get; set; }
public Int32 SeasonNumber { get; set; }
public String Consumer { get; set; }
public MetadataType MetadataType { get; set; }
public String Path { get; set; }
public SeasonMetadataUpdated(Series series, int seasonNumber, string consumer, MetadataType metadataType, string path)
{
Series = series;
SeasonNumber = seasonNumber;
Consumer = consumer;
MetadataType = metadataType;
Path = path;
}
}
}

View File

@ -1,22 +0,0 @@
using System;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata.Events
{
public class SeriesMetadataUpdated : IEvent
{
public Series Series { get; set; }
public String Consumer { get; set; }
public MetadataType MetadataType { get; set; }
public String Path { get; set; }
public SeriesMetadataUpdated(Series series, string consumer, MetadataType metadataType, string path)
{
Series = series;
Consumer = consumer;
MetadataType = metadataType;
Path = path;
}
}
}

View File

@ -1,4 +1,5 @@
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Tv;
@ -9,5 +10,6 @@ public interface IMetadata : IProvider
void OnSeriesUpdated(Series series);
void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload);
void AfterRename(Series series);
MetadataFile FindMetadataFile(Series series, string path);
}
}

View File

@ -1,4 +1,6 @@
using NLog;
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv.Events;
@ -11,13 +13,11 @@ public class NotificationService
IHandle<SeriesRenamedEvent>
{
private readonly IMetadataFactory _metadataFactory;
private readonly IMetadataRepository _metadataRepository;
private readonly Logger _logger;
public NotificationService(IMetadataFactory metadataFactory, IMetadataRepository metadataRepository, Logger logger)
public NotificationService(IMetadataFactory metadataFactory, Logger logger)
{
_metadataFactory = metadataFactory;
_metadataRepository = metadataRepository;
_logger = logger;
}

View File

@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Metadata
{
public class ExistingMetadataService : IHandleAsync<SeriesUpdatedEvent>
{
private readonly IDiskProvider _diskProvider;
private readonly IMetadataFileService _metadataFileService;
private readonly IMediaFileService _mediaFileService;
private readonly Logger _logger;
private readonly List<IMetadata> _consumers;
public ExistingMetadataService(IDiskProvider diskProvider,
IEnumerable<IMetadata> consumers,
IMetadataFileService metadataFileService,
IMediaFileService mediaFileService,
Logger logger)
{
_diskProvider = diskProvider;
_metadataFileService = metadataFileService;
_mediaFileService = mediaFileService;
_logger = logger;
_consumers = consumers.ToList();
}
public void HandleAsync(SeriesUpdatedEvent message)
{
if (!_diskProvider.FolderExists(message.Series.Path)) return;
_logger.Trace("Looking for existing metadata in {0}", message.Series.Path);
var filesOnDisk = _diskProvider.GetFiles(message.Series.Path, SearchOption.AllDirectories);
var possibleMetadataFiles = filesOnDisk.Where(c => !MediaFileExtensions.Extensions.Contains(Path.GetExtension(c).ToLower())).ToList();
var filteredFiles = _metadataFileService.FilterExistingFiles(possibleMetadataFiles, message.Series);
foreach (var possibleMetadataFile in filteredFiles)
{
foreach (var consumer in _consumers)
{
var metadata = consumer.FindMetadataFile(message.Series, possibleMetadataFile);
if (metadata == null) continue;
if (metadata.Type == MetadataType.EpisodeImage ||
metadata.Type == MetadataType.EpisodeMetadata)
{
//TODO: replace this with parser lookup, otherwise its impossible to link thumbs without knowing too much about the consumers
//We might want to resort to parsing the file name and
//then finding it via episodes incase the file names get out of sync
var episodeFile = _mediaFileService.FindByPath(possibleMetadataFile, false);
if (episodeFile == null) break;
metadata.EpisodeFileId = episodeFile.Id;
}
_metadataFileService.Upsert(metadata);
}
}
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Metadata.Files
{
public class MetadataFile : ModelBase
{
public Int32 SeriesId { get; set; }
public String Consumer { get; set; }
public MetadataType Type { get; set; }
public String RelativePath { get; set; }
public DateTime LastUpdated { get; set; }
public Int32? EpisodeFileId { get; set; }
public Int32? SeasonNumber { get; set; }
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Metadata.Files
{
public interface IMetadataFileRepository : IBasicRepository<MetadataFile>
{
void DeleteForSeries(int seriesId);
void DeleteForSeason(int seriesId, int seasonNumber);
void DeleteForEpisodeFile(int episodeFileId);
List<MetadataFile> GetFilesBySeries(int seriesId);
List<MetadataFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<MetadataFile> GetFilesByEpisodeFile(int episodeFileId);
MetadataFile FindByPath(string path);
}
public class MetadataFileRepository : BasicRepository<MetadataFile>, IMetadataFileRepository
{
public MetadataFileRepository(IDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public void DeleteForSeries(int seriesId)
{
Delete(c => c.SeriesId == seriesId);
}
public void DeleteForSeason(int seriesId, int seasonNumber)
{
Delete(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber);
}
public void DeleteForEpisodeFile(int episodeFileId)
{
Delete(c => c.EpisodeFileId == episodeFileId);
}
public List<MetadataFile> GetFilesBySeries(int seriesId)
{
return Query.Where(c => c.SeriesId == seriesId);
}
public List<MetadataFile> GetFilesBySeason(int seriesId, int seasonNumber)
{
return Query.Where(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber);
}
public List<MetadataFile> GetFilesByEpisodeFile(int episodeFileId)
{
return Query.Where(c => c.EpisodeFileId == episodeFileId);
}
public MetadataFile FindByPath(string path)
{
return Query.SingleOrDefault(c => c.RelativePath == path);
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Metadata.Files
{
public interface IMetadataFileService
{
List<MetadataFile> GetFilesBySeries(int seriesId);
List<MetadataFile> GetFilesByEpisodeFile(int episodeFileId);
MetadataFile FindByPath(string path);
List<string> FilterExistingFiles(List<string> files, Series series);
MetadataFile Upsert(MetadataFile metadataFile);
}
public class MetadataFileService : IMetadataFileService,
IHandleAsync<SeriesDeletedEvent>,
IHandleAsync<EpisodeFileDeletedEvent>,
IHandle<MetadataFileUpdated>
{
private readonly IMetadataFileRepository _repository;
private readonly ISeriesService _seriesService;
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public MetadataFileService(IMetadataFileRepository repository,
ISeriesService seriesService,
IDiskProvider diskProvider,
Logger logger)
{
_repository = repository;
_seriesService = seriesService;
_diskProvider = diskProvider;
_logger = logger;
}
public List<MetadataFile> GetFilesBySeries(int seriesId)
{
return _repository.GetFilesBySeries(seriesId);
}
public List<MetadataFile> GetFilesByEpisodeFile(int episodeFileId)
{
return _repository.GetFilesByEpisodeFile(episodeFileId);
}
public MetadataFile FindByPath(string path)
{
return _repository.FindByPath(path);
}
public List<string> FilterExistingFiles(List<string> files, Series series)
{
var seriesFiles = GetFilesBySeries(series.Id).Select(f => Path.Combine(series.Path, f.RelativePath)).ToList();
if (!seriesFiles.Any()) return files;
return files.Except(seriesFiles, PathEqualityComparer.Instance).ToList();
}
public MetadataFile Upsert(MetadataFile metadataFile)
{
metadataFile.LastUpdated = DateTime.UtcNow;
return _repository.Upsert(metadataFile);
}
public void HandleAsync(SeriesDeletedEvent message)
{
_logger.Trace("Deleting Metadata from database for series: {0}", message.Series);
_repository.DeleteForSeries(message.Series.Id);
}
public void HandleAsync(EpisodeFileDeletedEvent message)
{
var episodeFile = message.EpisodeFile;
var series = _seriesService.GetSeries(message.EpisodeFile.SeriesId);
foreach (var metadata in _repository.GetFilesByEpisodeFile(episodeFile.Id))
{
var path = Path.Combine(series.Path, metadata.RelativePath);
if (_diskProvider.FileExists(path))
{
_diskProvider.DeleteFile(path);
}
}
_logger.Trace("Deleting Metadata from database for episode file: {0}", episodeFile);
_repository.DeleteForEpisodeFile(episodeFile.Id);
}
public void Handle(MetadataFileUpdated message)
{
Upsert(message.Metadata);
}
}
}

View File

@ -0,0 +1,14 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Metadata.Files
{
public class MetadataFileUpdated : IEvent
{
public MetadataFile Metadata { get; set; }
public MetadataFileUpdated(MetadataFile metadata)
{
Metadata = metadata;
}
}
}

View File

@ -1,22 +1,22 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Metadata.Files;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Metadata
{
public abstract class MetadataConsumerBase<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 MetadataConsumerBase(IDiskProvider diskProvider, IHttpProvider httpProvider, Logger logger)
protected MetadataBase(IDiskProvider diskProvider, IHttpProvider httpProvider, Logger logger)
{
_diskProvider = diskProvider;
_httpProvider = httpProvider;
@ -44,6 +44,7 @@ public IEnumerable<ProviderDefinition> DefaultDefinitions
public abstract void OnSeriesUpdated(Series series);
public abstract void OnEpisodeImport(Series series, EpisodeFile episodeFile, bool newDownload);
public abstract void AfterRename(Series series);
public abstract MetadataFile FindMetadataFile(Series series, string path);
protected TSettings Settings
{

View File

@ -230,7 +230,11 @@ private static Tv.Ratings GetRatings(Trakt.Ratings ratings)
SeasonNumber = traktSeason.season
};
season.Images.Add(new MediaCover.MediaCover(MediaCoverTypes.Poster, traktSeason.images.poster));
if (traktSeason.images != null)
{
season.Images.Add(new MediaCover.MediaCover(MediaCoverTypes.Poster, traktSeason.images.poster));
}
seasons.Add(season);
}

View File

@ -196,7 +196,7 @@
<Compile Include="Datastore\Migration\037_add_configurable_qualities.cs" />
<Compile Include="Datastore\Migration\038_add_on_upgrade_to_notifications.cs" />
<Compile Include="Datastore\Migration\036_add_metadata_to_episodes_and_series.cs" />
<Compile Include="Datastore\Migration\037_add_metadata_consumers.cs" />
<Compile Include="Datastore\Migration\037_add_metadata_tables.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
@ -319,11 +319,13 @@
<Compile Include="Metadata\Consumers\Fake\FakeSettings.cs" />
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadata.cs" />
<Compile Include="Metadata\Consumers\Xbmc\XbmcMetadataSettings.cs" />
<Compile Include="Metadata\Events\EpisodeMetadataUpdated.cs" />
<Compile Include="Metadata\Events\SeasonMetadataUpdated.cs" />
<Compile Include="Metadata\Events\SeriesMetadataUpdated.cs" />
<Compile Include="Metadata\ExistingMetadataService.cs" />
<Compile Include="Metadata\Files\MetadataFile.cs" />
<Compile Include="Metadata\Files\MetadataFileRepository.cs" />
<Compile Include="Metadata\Files\MetadataFileService.cs" />
<Compile Include="Metadata\Files\MetadataFileUpdated.cs" />
<Compile Include="Metadata\IMetadata.cs" />
<Compile Include="Metadata\MetadataConsumerBase.cs" />
<Compile Include="Metadata\MetadataBase.cs" />
<Compile Include="MetadataSource\Trakt\TraktException.cs" />
<Compile Include="Metadata\MetadataDefinition.cs" />
<Compile Include="Metadata\MetadataFactory.cs" />
@ -696,9 +698,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Download\Clients\uTorrent\" />
<Folder Include="Metadata\EpisodeFiles\" />
<Folder Include="Metadata\Seasons\" />
<Folder Include="Metadata\Series\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>