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:
parent
a6361d0bbd
commit
3ca5e478ff
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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, "")));
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
70
src/NzbDrone.Core/Metadata/ExistingMetadataService.cs
Normal file
70
src/NzbDrone.Core/Metadata/ExistingMetadataService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/NzbDrone.Core/Metadata/Files/MetadataFile.cs
Normal file
16
src/NzbDrone.Core/Metadata/Files/MetadataFile.cs
Normal 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; }
|
||||
}
|
||||
}
|
62
src/NzbDrone.Core/Metadata/Files/MetadataFileRepository.cs
Normal file
62
src/NzbDrone.Core/Metadata/Files/MetadataFileRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
104
src/NzbDrone.Core/Metadata/Files/MetadataFileService.cs
Normal file
104
src/NzbDrone.Core/Metadata/Files/MetadataFileService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
14
src/NzbDrone.Core/Metadata/Files/MetadataFileUpdated.cs
Normal file
14
src/NzbDrone.Core/Metadata/Files/MetadataFileUpdated.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user