Relative episode file paths

This commit is contained in:
Mark McDowall 2014-07-23 16:43:54 -07:00
parent 10fc875715
commit 6671934c0f
63 changed files with 571 additions and 464 deletions

View File

@ -1,5 +1,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.REST;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
@ -7,6 +10,7 @@ using NzbDrone.Api.Mapping;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.EpisodeFiles
{
@ -15,16 +19,19 @@ namespace NzbDrone.Api.EpisodeFiles
{
private readonly IMediaFileService _mediaFileService;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly ISeriesService _seriesService;
private readonly Logger _logger;
public EpisodeModule(ICommandExecutor commandExecutor,
IMediaFileService mediaFileService,
IRecycleBinProvider recycleBinProvider,
ISeriesService seriesService,
Logger logger)
: base(commandExecutor)
{
_mediaFileService = mediaFileService;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_logger = logger;
GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
@ -34,7 +41,10 @@ namespace NzbDrone.Api.EpisodeFiles
private EpisodeFileResource GetEpisodeFile(int id)
{
return _mediaFileService.Get(id).InjectTo<EpisodeFileResource>();
var episodeFile = _mediaFileService.Get(id);
var series = _seriesService.GetSeries(episodeFile.SeriesId);
return MapToResource(series, episodeFile);
}
private List<EpisodeFileResource> GetEpisodeFiles()
@ -46,7 +56,10 @@ namespace NzbDrone.Api.EpisodeFiles
throw new BadRequestException("seriesId is missing");
}
return ToListResource(() => _mediaFileService.GetFilesBySeries(seriesId.Value));
var series = _seriesService.GetSeries(seriesId.Value);
return _mediaFileService.GetFilesBySeries(seriesId.Value)
.Select(f => MapToResource(series, f)).ToList();
}
private void SetQuality(EpisodeFileResource episodeFileResource)
@ -59,12 +72,22 @@ namespace NzbDrone.Api.EpisodeFiles
private void DeleteEpisodeFile(int id)
{
var episodeFile = _mediaFileService.Get(id);
var series = _seriesService.GetSeries(episodeFile.SeriesId);
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
_logger.Info("Deleting episode file: {0}", episodeFile.Path);
_recycleBinProvider.DeleteFile(episodeFile.Path);
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath);
_mediaFileService.Delete(episodeFile);
}
private static EpisodeFileResource MapToResource(Core.Tv.Series series, EpisodeFile episodeFile)
{
var resource = episodeFile.InjectTo<EpisodeFileResource>();
resource.Path = Path.Combine(series.Path, episodeFile.RelativePath);
return resource;
}
public void Handle(EpisodeFileAddedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);

View File

@ -1,7 +1,6 @@
using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.EpisodeFiles
{
@ -9,6 +8,7 @@ namespace NzbDrone.Api.EpisodeFiles
{
public Int32 SeriesId { get; set; }
public Int32 SeasonNumber { get; set; }
public String RelativePath { get; set; }
public String Path { get; set; }
public Int64 Size { get; set; }
public DateTime DateAdded { get; set; }

View File

@ -37,7 +37,6 @@ namespace NzbDrone.Api.Series
ISceneMappingService sceneMappingService,
IMapCoversToLocal coverMapper,
RootFolderValidator rootFolderValidator,
PathExistsValidator pathExistsValidator,
SeriesPathValidator seriesPathValidator,
SeriesExistsValidator seriesExistsValidator,
DroneFactoryValidator droneFactoryValidator,

View File

@ -7,6 +7,7 @@ using NLog;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Instrumentation.Extensions;
namespace NzbDrone.Common.Disk
{
@ -192,7 +193,7 @@ namespace NzbDrone.Common.Disk
Ensure.That(source, () => source).IsValidPath();
Ensure.That(target, () => target).IsValidPath();
Logger.Debug("{0} {1} -> {2}", transferAction, source, target);
Logger.ProgressDebug("{0} {1} -> {2}", transferAction, source, target);
var sourceFolder = new DirectoryInfo(source);
var targetFolder = new DirectoryInfo(target);
@ -211,7 +212,7 @@ namespace NzbDrone.Common.Disk
{
var destFile = Path.Combine(target, sourceFile.Name);
Logger.Debug("{0} {1} -> {2}", transferAction, sourceFile, destFile);
Logger.ProgressDebug("{0} {1} -> {2}", transferAction, sourceFile, destFile);
switch (transferAction)
{

View File

@ -1,7 +1,7 @@
using System;
using NLog;
namespace NzbDrone.Core.Instrumentation.Extensions
namespace NzbDrone.Common.Instrumentation.Extensions
{
public static class LoggerExtensions
{

View File

@ -118,6 +118,7 @@
<Compile Include="IEnumerableExtensions.cs" />
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
<Compile Include="Instrumentation\ExceptronTarget.cs" />
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\LogEventExtensions.cs" />
<Compile Include="Instrumentation\LogglyTarget.cs" />

View File

@ -86,14 +86,10 @@ namespace NzbDrone.Core.Test.Datastore
Db.Insert(episode);
var loadedEpisodeFile = Db.Single<Episode>().EpisodeFile.Value;
loadedEpisodeFile.Should().NotBeNull();
loadedEpisodeFile.ShouldHave().AllProperties().But(c => c.DateAdded).EqualTo(episodeFile);
loadedEpisodeFile.ShouldHave().AllProperties().But(c => c.DateAdded).But(c => c.Path).EqualTo(episodeFile);
}
[Test]

View File

@ -102,9 +102,9 @@ namespace NzbDrone.Core.Test.InstrumentationTests
public void null_string_as_arg_should_not_fail()
{
var epFile = new EpisodeFile();
_logger.Debug("File {0} no longer exists on disk. removing from database.", epFile.Path);
_logger.Debug("File {0} no longer exists on disk. removing from database.", epFile.RelativePath);
epFile.Path.Should().BeNull();
epFile.RelativePath.Should().BeNull();
}

View File

@ -30,7 +30,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
.Build();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = @"C:\Test\File.avi")
.With(f => f.Path = null)
.With(f => f.RelativePath = @"Season 1\File.avi")
.Build();
_localEpisode = Builder<LocalEpisode>.CreateNew()
@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildFilePath(It.IsAny<Series>(), It.IsAny<Int32>(), It.IsAny<String>(), It.IsAny<String>()))
.Returns(@"C:\Test\File Name.avi");
.Returns(@"C:\Test\TV\Series\File Name.avi");
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(It.IsAny<String>()))

View File

@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
Mocker.GetMock<IMediaFileService>()
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>()))
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
.Returns(_videoFiles);
}
@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
Mocker.GetMock<IMediaFileService>()
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<int>()))
.Setup(c => c.FilterExistingFiles(_videoFiles, It.IsAny<Series>()))
.Returns(_videoFiles);
Subject.GetImportDecisions(_videoFiles, _series, false);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
@ -31,6 +32,7 @@ namespace NzbDrone.Core.Test.MediaFiles
var series = Builder<Series>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic())
.Build();
var episodes = Builder<Episode>.CreateListOfSize(5)
@ -48,7 +50,7 @@ namespace NzbDrone.Core.Test.MediaFiles
{
Series = series,
Episodes = new List<Episode> {episode},
Path = @"C:\Test\TV\30 Rock\30 Rock - S01E01 - Pilot.avi".AsOsAgnostic(),
Path = Path.Combine(series.Path, "30 Rock - S01E01 - Pilot.avi"),
Quality = new QualityModel(Quality.Bluray720p),
ParsedEpisodeInfo = new ParsedEpisodeInfo
{

View File

@ -4,7 +4,6 @@ using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.MediaFiles
{

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Moq;
@ -6,20 +7,24 @@ using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles
namespace NzbDrone.Core.Test.MediaFiles.MediaFileServiceTests
{
[TestFixture]
public class MediaFileServiceTest : CoreTest<MediaFileService>
public class FilterFixture : CoreTest<MediaFileService>
{
private Series _series;
[Test]
[TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]",
"Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")]
public void CleanFileName(string name, string expectedName)
[SetUp]
public void Setup()
{
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
_series = new Series
{
Id = 10,
Path = @"C:\".AsOsAgnostic()
};
}
[Test]
@ -27,9 +32,9 @@ namespace NzbDrone.Core.Test.MediaFiles
{
var files = new List<string>()
{
"c:\\file1.avi".AsOsAgnostic(),
"c:\\file2.avi".AsOsAgnostic(),
"c:\\file3.avi".AsOsAgnostic()
"C:\\file1.avi".AsOsAgnostic(),
"C:\\file2.avi".AsOsAgnostic(),
"C:\\file3.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
@ -37,26 +42,25 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns(new List<EpisodeFile>());
Subject.FilterExistingFiles(files, 10).Should().BeEquivalentTo(files);
Subject.FilterExistingFiles(files, _series).Should().BeEquivalentTo(files);
}
[Test]
public void filter_should_return_none_if_all_files_exist()
{
var files = new List<string>()
{
"c:\\file1.avi".AsOsAgnostic(),
"c:\\file2.avi".AsOsAgnostic(),
"c:\\file3.avi".AsOsAgnostic()
"C:\\file1.avi".AsOsAgnostic(),
"C:\\file2.avi".AsOsAgnostic(),
"C:\\file3.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
.Returns(files.Select(f => new EpisodeFile { Path = f }).ToList());
.Returns(files.Select(f => new EpisodeFile { RelativePath = Path.GetFileName(f) }).ToList());
Subject.FilterExistingFiles(files, 10).Should().BeEmpty();
Subject.FilterExistingFiles(files, _series).Should().BeEmpty();
}
[Test]
@ -64,21 +68,21 @@ namespace NzbDrone.Core.Test.MediaFiles
{
var files = new List<string>()
{
"c:\\file1.avi".AsOsAgnostic(),
"c:\\file2.avi".AsOsAgnostic(),
"c:\\file3.avi".AsOsAgnostic()
"C:\\file1.avi".AsOsAgnostic(),
"C:\\file2.avi".AsOsAgnostic(),
"C:\\file3.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
.Returns(new List<EpisodeFile>
{
new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()}
new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()}
});
Subject.FilterExistingFiles(files, 10).Should().HaveCount(2);
Subject.FilterExistingFiles(files, 10).Should().NotContain("c:\\file2.avi".AsOsAgnostic());
Subject.FilterExistingFiles(files, _series).Should().HaveCount(2);
Subject.FilterExistingFiles(files, _series).Should().NotContain("C:\\file2.avi".AsOsAgnostic());
}
[Test]
@ -88,21 +92,21 @@ namespace NzbDrone.Core.Test.MediaFiles
var files = new List<string>()
{
"c:\\file1.avi".AsOsAgnostic(),
"c:\\FILE2.avi".AsOsAgnostic(),
"c:\\file3.avi".AsOsAgnostic()
"C:\\file1.avi".AsOsAgnostic(),
"C:\\FILE2.avi".AsOsAgnostic(),
"C:\\file3.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
.Returns(new List<EpisodeFile>
{
new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()}
new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()}
});
Subject.FilterExistingFiles(files, 10).Should().HaveCount(2);
Subject.FilterExistingFiles(files, 10).Should().NotContain("c:\\file2.avi".AsOsAgnostic());
Subject.FilterExistingFiles(files, _series).Should().HaveCount(2);
Subject.FilterExistingFiles(files, _series).Should().NotContain("C:\\file2.avi".AsOsAgnostic());
}
[Test]
@ -112,19 +116,19 @@ namespace NzbDrone.Core.Test.MediaFiles
var files = new List<string>()
{
"c:\\file1.avi".AsOsAgnostic(),
"c:\\FILE2.avi".AsOsAgnostic(),
"c:\\file3.avi".AsOsAgnostic()
"C:\\file1.avi".AsOsAgnostic(),
"C:\\FILE2.avi".AsOsAgnostic(),
"C:\\file3.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
.Returns(new List<EpisodeFile>
{
new EpisodeFile{Path = "c:\\file2.avi".AsOsAgnostic()}
new EpisodeFile{ RelativePath = "file2.avi".AsOsAgnostic()}
});
Subject.FilterExistingFiles(files, 10).Should().HaveCount(3);
Subject.FilterExistingFiles(files, _series).Should().HaveCount(3);
}
[Test]
@ -132,16 +136,16 @@ namespace NzbDrone.Core.Test.MediaFiles
{
var files = new List<string>()
{
"c:\\FILE1.avi".AsOsAgnostic()
"C:\\FILE1.avi".AsOsAgnostic()
};
Mocker.GetMock<IMediaFileRepository>()
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
.Returns(new List<EpisodeFile>());
Subject.FilterExistingFiles(files, 10).Should().HaveCount(1);
Subject.FilterExistingFiles(files, 10).Should().NotContain(files.First().ToLower());
Subject.FilterExistingFiles(files, 10).Should().Contain(files.First());
Subject.FilterExistingFiles(files, _series).Should().HaveCount(1);
Subject.FilterExistingFiles(files, _series).Should().NotContain(files.First().ToLower());
Subject.FilterExistingFiles(files, _series).Should().Contain(files.First());
}
}
}

View File

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles
.Returns(Builder<Series>.CreateNew().Build());
Mocker.GetMock<IDiskProvider>()
.Setup(e => e.FileExists(It.Is<String>(c => c != DELETED_PATH)))
.Setup(e => e.FileExists(It.Is<String>(c => !c.Contains(DELETED_PATH))))
.Returns(true);
Mocker.GetMock<IEpisodeService>()
@ -68,18 +68,18 @@ namespace NzbDrone.Core.Test.MediaFiles
}
[Test]
public void should_delete_none_existing_files()
public void should_delete_non_existent_files()
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
.Random(2)
.With(c => c.Path = DELETED_PATH)
.With(c => c.RelativePath = DELETED_PATH)
.Build();
GivenEpisodeFiles(episodeFiles);
Subject.Execute(new CleanMediaFileDb(0));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.Is<EpisodeFile>(e => e.Path == DELETED_PATH), false), Times.Exactly(2));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.Is<EpisodeFile>(e => e.RelativePath == DELETED_PATH), false), Times.Exactly(2));
}
[Test]
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
.Random(10)
.With(c => c.Path = "ExistingPath")
.With(c => c.RelativePath = "ExistingPath")
.Build();
GivenEpisodeFiles(episodeFiles);
@ -98,21 +98,6 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.IsAny<EpisodeFile>(), false), Times.Exactly(10));
}
[Test]
public void should_delete_files_that_do_not_belong_to_the_series_path()
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
.Random(10)
.With(c => c.Path = "ExistingPath")
.Build();
GivenEpisodeFiles(episodeFiles);
Subject.Execute(new CleanMediaFileDb(0));
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.IsAny<EpisodeFile>(), false), Times.Exactly(10));
}
[Test]
public void should_unlink_episode_when_episodeFile_does_not_exist()
{
@ -128,7 +113,7 @@ namespace NzbDrone.Core.Test.MediaFiles
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
.Random(10)
.With(c => c.Path = "ExistingPath")
.With(c => c.RelativePath = "ExistingPath")
.Build();
GivenEpisodeFiles(episodeFiles);

View File

@ -1,23 +1,33 @@
using FizzWare.NBuilder;
using System.IO;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
[TestFixture]
public class UpdateMediaInfoServiceFixture : CoreTest<UpdateMediaInfoService>
{
private Series _series;
[SetUp]
public void Setup()
{
_series = new Series
{
Id = 1,
Path = @"C:\series".AsOsAgnostic()
};
}
private void GivenFileExists()
{
Mocker.GetMock<IDiskProvider>()
@ -44,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(3)
.All()
.With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic())
.With(v => v.RelativePath = "media.mkv")
.TheFirst(1)
.With(v => v.MediaInfo = new MediaInfoModel())
.BuildList();
@ -56,10 +66,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 }));
Subject.Handle(new SeriesScannedEvent(_series));
Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Exactly(2));
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
Mocker.GetMock<IMediaFileService>()
.Verify(v => v.Update(It.IsAny<EpisodeFile>()), Times.Exactly(2));
@ -70,7 +80,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(2)
.All()
.With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic())
.With(v => v.RelativePath = "media.mkv")
.BuildList();
Mocker.GetMock<IMediaFileService>()
@ -79,10 +89,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenSuccessfulScan();
Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 }));
Subject.Handle(new SeriesScannedEvent(_series));
Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Never());
.Verify(v => v.GetMediaInfo("media.mkv"), Times.Never());
Mocker.GetMock<IMediaFileService>()
.Verify(v => v.Update(It.IsAny<EpisodeFile>()), Times.Never());
@ -93,9 +103,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(2)
.All()
.With(v => v.Path = @"C:\series\media.mkv".AsOsAgnostic())
.With(v => v.RelativePath = "media.mkv")
.TheFirst(1)
.With(v => v.Path = @"C:\series\media2.mkv".AsOsAgnostic())
.With(v => v.RelativePath = "media2.mkv")
.BuildList();
Mocker.GetMock<IMediaFileService>()
@ -104,12 +114,12 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
GivenFailedScan(@"C:\series\media2.mkv".AsOsAgnostic());
GivenFailedScan(Path.Combine(_series.Path, "media2.mkv"));
Subject.Handle(new SeriesScannedEvent(new Tv.Series { Id = 1 }));
Subject.Handle(new SeriesScannedEvent(_series));
Mocker.GetMock<IVideoFileInfoReader>()
.Verify(v => v.GetMediaInfo(@"C:\series\media.mkv".AsOsAgnostic()), Times.Exactly(1));
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1));
Mocker.GetMock<IMediaFileService>()
.Verify(v => v.Update(It.IsAny<EpisodeFile>()), Times.Exactly(1));

View File

@ -10,6 +10,7 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles
{
@ -22,6 +23,10 @@ namespace NzbDrone.Core.Test.MediaFiles
public void Setup()
{
_localEpisode = new LocalEpisode();
_localEpisode.Series = new Series
{
Path = @"C:\Test\TV\Series".AsOsAgnostic()
};
_episodeFile = Builder<EpisodeFile>
.CreateNew()
@ -42,7 +47,7 @@ namespace NzbDrone.Core.Test.MediaFiles
new EpisodeFile
{
Id = 1,
Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi",
RelativePath = @"Season 01\30.rock.s01e01.avi",
}))
.Build()
.ToList();
@ -57,7 +62,7 @@ namespace NzbDrone.Core.Test.MediaFiles
new EpisodeFile
{
Id = 1,
Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi",
RelativePath = @"Season 01\30.rock.s01e01.avi",
}))
.Build()
.ToList();
@ -71,14 +76,14 @@ namespace NzbDrone.Core.Test.MediaFiles
new EpisodeFile
{
Id = 1,
Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi",
RelativePath = @"Season 01\30.rock.s01e01.avi",
}))
.TheNext(1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Id = 2,
Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e02.avi",
RelativePath = @"Season 01\30.rock.s01e02.avi",
}))
.Build()
.ToList();

View File

@ -188,7 +188,8 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="MediaFiles\MediaFileServiceTest.cs" />
<Compile Include="OrganizerTests\CleanFixture.cs" />
<Compile Include="MediaFiles\MediaFileServiceTests\FilterFixture.cs" />
<Compile Include="MediaFiles\MediaFileTableCleanupServiceFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoServiceFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReaderFixture.cs" />
@ -262,6 +263,7 @@
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithFilesFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithoutFilesFixture.cs" />
<Compile Include="TvTests\EpisodeRepositoryTests\FindEpisodeFixture.cs" />
<Compile Include="TvTests\MoveSeriesServiceFixture.cs" />
<Compile Include="TvTests\RefreshEpisodeServiceFixture.cs" />
<Compile Include="TvTests\RefreshSeriesServiceFixture.cs" />
<Compile Include="TvTests\SeriesRepositoryTests\SeriesRepositoryFixture.cs" />

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.OrganizerTests
{
[TestFixture]
public class CleanFixture : CoreTest
{
[TestCase("Law & Order: Criminal Intent - S10E07 - Icarus [HDTV-720p]",
"Law & Order- Criminal Intent - S10E07 - Icarus [HDTV-720p]")]
public void CleanFileName(string name, string expectedName)
{
FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
}
}
}

View File

@ -64,6 +64,8 @@ namespace NzbDrone.Core.Test.OrganizerTests
_episodeFile.Quality.Proper = true;
}
[Test]
public void should_replace_Series_space_Title()
{
@ -227,10 +229,10 @@ namespace NzbDrone.Core.Test.OrganizerTests
public void use_file_name_when_sceneName_is_null()
{
_namingConfig.RenameEpisodes = false;
_episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test";
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.Path));
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.RelativePath));
}
[Test]
@ -238,7 +240,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
{
_namingConfig.RenameEpisodes = false;
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
_episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test";
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
.Should().Be("30.Rock.S01E01.xvid-LOL");
@ -378,7 +380,7 @@ namespace NzbDrone.Core.Test.OrganizerTests
_namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Title}";
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
_episodeFile.Path = @"C:\Test\TV\30 Rock - S01E01 - Test";
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
.Should().Be("30 Rock - 30.Rock.S01E01.xvid-LOL");

View File

@ -67,9 +67,9 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
new QualitiesBelowCutoff(profile.Id, new[] {Quality.SDTV.Id})
};
var qualityMet = new EpisodeFile { Path = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } };
var qualityUnmet = new EpisodeFile { Path = "b", Quality = new QualityModel { Quality = Quality.SDTV } };
var qualityRawHD = new EpisodeFile { Path = "c", Quality = new QualityModel { Quality = Quality.RAWHD } };
var qualityMet = new EpisodeFile { RelativePath = "a", Quality = new QualityModel { Quality = Quality.WEBDL480p } };
var qualityUnmet = new EpisodeFile { RelativePath = "b", Quality = new QualityModel { Quality = Quality.SDTV } };
var qualityRawHD = new EpisodeFile { RelativePath = "c", Quality = new QualityModel { Quality = Quality.RAWHD } };
MediaFileRepository fileRepository = Mocker.Resolve<MediaFileRepository>();

View File

@ -57,7 +57,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
public void should_only_contain_episodes_for_the_given_series()
{
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = "another path")
.With(f => f.RelativePath = "another path")
.With(c => c.Quality = new QualityModel())
.BuildNew();

View File

@ -0,0 +1,100 @@
using System;
using System.IO;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Commands;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.TvTests
{
[TestFixture]
public class MoveSeriesServiceFixture : CoreTest<MoveSeriesService>
{
private Series _series;
private MoveSeriesCommand _command;
[SetUp]
public void Setup()
{
_series = Builder<Series>
.CreateNew()
.Build();
_command = new MoveSeriesCommand
{
SeriesId = 1,
SourcePath = @"C:\Test\TV\Series".AsOsAgnostic(),
DestinationPath = @"C:\Test\TV2\Series".AsOsAgnostic()
};
Mocker.GetMock<ISeriesService>()
.Setup(s => s.GetSeries(It.IsAny<Int32>()))
.Returns(_series);
}
private void GivenFailedMove()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.MoveFolder(It.IsAny<String>(), It.IsAny<String>()))
.Throws<IOException>();
}
[Test]
public void should_log_error_when_move_throws_an_exception()
{
GivenFailedMove();
Assert.Throws<IOException>(() => Subject.Execute(_command));
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void should_no_update_series_path_on_error()
{
GivenFailedMove();
Assert.Throws<IOException>(() => Subject.Execute(_command));
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
}
[Test]
public void should_build_new_path_when_root_folder_is_provided()
{
_command.DestinationPath = null;
_command.DestinationRootFolder = @"C:\Test\TV3".AsOsAgnostic();
var expectedPath = @"C:\Test\TV3\Series".AsOsAgnostic();
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.GetSeriesFolder(It.IsAny<Series>(), null))
.Returns("Series");
Subject.Execute(_command);
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Path == expectedPath)), Times.Once());
}
[Test]
public void should_use_destination_path_if_destination_root_folder_is_blank()
{
Subject.Execute(_command);
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Path == _command.DestinationPath)), Times.Once());
Mocker.GetMock<IBuildFileNames>()
.Verify(v => v.GetSeriesFolder(It.IsAny<Series>(), null), Times.Never());
}
}
}

View File

@ -8,8 +8,8 @@ using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Backup

View File

@ -0,0 +1,48 @@
using System.Data;
using System.IO;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(57)]
public class convert_episode_file_path_to_relative : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.Column("RelativePath").OnTable("EpisodeFiles").AsString().Nullable();
//TODO: Add unique contraint for series ID and Relative Path
//TODO: Warn if multiple series share the same path
Execute.WithConnection(UpdateRelativePaths);
}
private void UpdateRelativePaths(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Path FROM Series";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var seriesId = seriesReader.GetInt32(0);
var seriesPath = seriesReader.GetString(1) + Path.DirectorySeparatorChar;
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE EpisodeFiles SET RelativePath = REPLACE(Path, ?, '') WHERE SeriesId = ?";
updateCmd.AddParameter(seriesPath);
updateCmd.AddParameter(seriesId);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(58)]
public class drop_episode_file_path : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
SqLiteAlter.DropColumns("EpisodeFiles", new [] { "Path" });
}
}
}

View File

@ -1,241 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
namespace NzbDrone.Core.Datastore.Migration.Framework
{
public interface ISQLiteMigrationHelper
{
Dictionary<String, SQLiteMigrationHelper.SQLiteColumn> GetColumns(string tableName);
void CreateTable(string tableName, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> values, IEnumerable<SQLiteMigrationHelper.SQLiteIndex> indexes);
void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteMigrationHelper.SQLiteColumn> columns);
void DropTable(string tableName);
void RenameTable(string tableName, string newName);
List<T> GetDuplicates<T>(string tableName, string columnName);
SQLiteTransaction BeginTransaction();
List<SQLiteMigrationHelper.SQLiteIndex> GetIndexes(string tableName);
}
public class SQLiteMigrationHelper : ISQLiteMigrationHelper
{
private readonly SQLiteConnection _connection;
private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?<name>\w+)['\""\]]\s(?<schema>[\w-\s]+)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
private static readonly Regex IndexRegex = new Regex(@"\(""(?<col>.*)""\s(?<direction>ASC|DESC)\)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger)
{
try
{
_connection = new SQLiteConnection(connectionStringFactory.MainDbConnectionString);
_connection.Open();
}
catch (Exception e)
{
logger.ErrorException("Couldn't open database " + connectionStringFactory.MainDbConnectionString, e);
throw;
}
}
private string GetOriginalSql(string tableName)
{
var command =
new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name ='{0}'",
tableName));
command.Connection = _connection;
return (string)command.ExecuteScalar();
}
public Dictionary<String, SQLiteColumn> GetColumns(string tableName)
{
var originalSql = GetOriginalSql(tableName);
var matches = SchemaRegex.Matches(originalSql);
return matches.Cast<Match>().ToDictionary(
match => match.Groups["name"].Value.Trim(),
match => new SQLiteColumn
{
Name = match.Groups["name"].Value.Trim(),
Schema = match.Groups["schema"].Value.Trim()
});
}
private static IEnumerable<T> ReadArray<T>(SQLiteDataReader reader)
{
while (reader.Read())
{
yield return (T)Convert.ChangeType(reader[0], typeof(T));
}
}
public List<SQLiteIndex> GetIndexes(string tableName)
{
var command = new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name ='{0}'", tableName));
command.Connection = _connection;
var reader = command.ExecuteReader();
var sqls = ReadArray<string>(reader).ToList();
var indexes = new List<SQLiteIndex>();
foreach (var indexSql in sqls)
{
var newIndex = new SQLiteIndex();
var matches = IndexRegex.Match(indexSql);
newIndex.Column = matches.Groups["col"].Value;
newIndex.Unique = indexSql.Contains("UNIQUE");
newIndex.Table = tableName;
indexes.Add(newIndex);
}
return indexes;
}
public void CreateTable(string tableName, IEnumerable<SQLiteColumn> values, IEnumerable<SQLiteIndex> indexes)
{
var columns = String.Join(",", values.Select(c => c.ToString()));
ExecuteNonQuery("CREATE TABLE [{0}] ({1})", tableName, columns);
foreach (var index in indexes)
{
ExecuteNonQuery("DROP INDEX {0}", index.IndexName);
ExecuteNonQuery(index.CreateSql(tableName));
}
}
public void CopyData(string sourceTable, string destinationTable, IEnumerable<SQLiteColumn> columns)
{
var originalCount = GetRowCount(sourceTable);
var columnsToTransfer = String.Join(",", columns.Select(c => c.Name));
var transferCommand = BuildCommand("INSERT INTO {0} SELECT {1} FROM {2};", destinationTable, columnsToTransfer, sourceTable);
transferCommand.ExecuteNonQuery();
var transferredRows = GetRowCount(destinationTable);
if (transferredRows != originalCount)
{
throw new ApplicationException(string.Format("Expected {0} rows to be copied from [{1}] to [{2}]. But only copied {3}", originalCount, sourceTable, destinationTable, transferredRows));
}
}
public void DropTable(string tableName)
{
var dropCommand = BuildCommand("DROP TABLE {0};", tableName);
dropCommand.ExecuteNonQuery();
}
public void RenameTable(string tableName, string newName)
{
var renameCommand = BuildCommand("ALTER TABLE {0} RENAME TO {1};", tableName, newName);
renameCommand.ExecuteNonQuery();
}
public Dictionary<int,T> GetDuplicates<T>(string tableName, string columnName)
{
var dupCommand = BuildCommand("select id, {0} from {1}", columnName, tableName);
var result = new Dictionary<int, T>();
using (var reader = dupCommand.ExecuteReader())
{
while (reader.Read())
{
}
}
return ReadArray<T>().ToList();
}
public int GetRowCount(string tableName)
{
var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName);
return Convert.ToInt32(countCommand.ExecuteScalar());
}
public SQLiteTransaction BeginTransaction()
{
return _connection.BeginTransaction();
}
private SQLiteCommand BuildCommand(string format, params string[] args)
{
var command = new SQLiteCommand(string.Format(format, args));
command.Connection = _connection;
return command;
}
private void ExecuteNonQuery(string command, params string[] args)
{
var sqLiteCommand = new SQLiteCommand(string.Format(command, args))
{
Connection = _connection
};
sqLiteCommand.ExecuteNonQuery();
}
public class SQLiteColumn
{
public string Name { get; set; }
public string Schema { get; set; }
public override string ToString()
{
return string.Format("[{0}] {1}", Name, Schema);
}
}
public class SQLiteIndex
{
public string Column { get; set; }
public string Table { get; set; }
public bool Unique { get; set; }
public override string ToString()
{
return string.Format("[{0}] Unique: {1}", Column, Unique);
}
public string IndexName
{
get
{
return string.Format("IX_{0}_{1}", Table, Column);
}
}
public string CreateSql(string tableName)
{
return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName);
}
}
}
}

View File

@ -56,16 +56,17 @@ namespace NzbDrone.Core.Datastore
.Relationship()
.HasOne(s => s.Profile, s => s.ProfileId);
Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles")
.Ignore(f => f.Path)
.Relationships.AutoMapICollectionOrComplexProperties();
Mapper.Entity<Episode>().RegisterModel("Episodes")
.Ignore(e => e.SeriesTitle)
.Ignore(e => e.Series)
.Ignore(e => e.HasFile)
.Relationship()
.HasOne(episode => episode.EpisodeFile, episode => episode.EpisodeFileId);
Mapper.Entity<EpisodeFile>().RegisterModel("EpisodeFiles")
.Relationships.AutoMapICollectionOrComplexProperties();
Mapper.Entity<Profile>().RegisterModel("Profiles");
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions");
Mapper.Entity<Log>().RegisterModel("Logs");

View File

@ -3,12 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{

View File

@ -1,7 +1,7 @@
using System;
using NLog;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model;

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
@ -169,7 +170,7 @@ namespace NzbDrone.Core.History
//Won't have a value since we publish this event before saving to DB.
//history.Data.Add("FileId", message.ImportedEpisode.Id.ToString());
history.Data.Add("DroppedPath", message.EpisodeInfo.Path);
history.Data.Add("ImportedPath", message.ImportedEpisode.Path);
history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath));
history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("DownloadClientId", message.DownloadClientId);

View File

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Queue;
using NzbDrone.Core.Tv;

View File

@ -3,11 +3,11 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using System.Linq;

View File

@ -1,6 +1,6 @@
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.IndexerSearch

View File

@ -1,6 +1,6 @@
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Tv;

View File

@ -2,11 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;

View File

@ -4,8 +4,8 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.MediaFiles.Events;

View File

@ -8,20 +8,26 @@ namespace NzbDrone.Core.MediaFiles
{
public class EpisodeFile : ModelBase
{
public int SeriesId { get; set; }
public int SeasonNumber { get; set; }
public string Path { get; set; }
public long Size { get; set; }
public Int32 SeriesId { get; set; }
public Int32 SeasonNumber { get; set; }
public String RelativePath { get; set; }
public String Path { get; set; }
public Int64 Size { get; set; }
public DateTime DateAdded { get; set; }
public string SceneName { get; set; }
public string ReleaseGroup { get; set; }
public String SceneName { get; set; }
public String ReleaseGroup { get; set; }
public QualityModel Quality { get; set; }
public MediaInfoModel MediaInfo { get; set; }
public LazyList<Episode> Episodes { get; set; }
public override string ToString()
public override String ToString()
{
return String.Format("[{0}] {1}", Id, Path);
return String.Format("[{0}] {1}", Id, RelativePath);
}
//
// public String Path(Series series)
// {
// return System.IO.Path.Combine(series.Path, RelativePath);
// }
}
}

View File

@ -50,7 +50,7 @@ namespace NzbDrone.Core.MediaFiles
{
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.RelativePath));
_logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
@ -60,7 +60,7 @@ namespace NzbDrone.Core.MediaFiles
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
_logger.Debug("Moving episode file: {0} to {1}", episodeFile, filePath);
@ -70,27 +70,29 @@ namespace NzbDrone.Core.MediaFiles
public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
_logger.Debug("Copying episode file: {0} to {1}", episodeFile, filePath);
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, true);
}
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, string destinationFilename, bool copyOnly)
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, String destinationFilename, Boolean copyOnly)
{
Ensure.That(episodeFile, () => episodeFile).IsNotNull();
Ensure.That(series,() => series).IsNotNull();
Ensure.That(destinationFilename, () => destinationFilename).IsValidPath();
if (!_diskProvider.FileExists(episodeFile.Path))
var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath);
if (!_diskProvider.FileExists(episodeFilePath))
{
throw new FileNotFoundException("Episode file path does not exist", episodeFile.Path);
throw new FileNotFoundException("Episode file path does not exist", episodeFilePath);
}
if (episodeFile.Path.PathEquals(destinationFilename))
if (episodeFilePath.PathEquals(destinationFilename))
{
throw new SameFilenameException("File not moved, source and destination are the same", episodeFile.Path);
throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
}
var directoryName = new FileInfo(destinationFilename).DirectoryName;
@ -116,15 +118,16 @@ namespace NzbDrone.Core.MediaFiles
if (copyOnly)
{
_logger.Debug("Copying [{0}] > [{1}]", episodeFile.Path, destinationFilename);
_diskProvider.CopyFile(episodeFile.Path, destinationFilename);
_logger.Debug("Copying [{0}] > [{1}]", episodeFilePath, destinationFilename);
_diskProvider.CopyFile(episodeFilePath, destinationFilename);
}
else
{
_logger.Debug("Moving [{0}] > [{1}]", episodeFile.Path, destinationFilename);
_diskProvider.MoveFile(episodeFile.Path, destinationFilename);
_logger.Debug("Moving [{0}] > [{1}]", episodeFilePath, destinationFilename);
_diskProvider.MoveFile(episodeFilePath, destinationFilename);
}
episodeFile.Path = destinationFilename;
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename);
_updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes);

View File

@ -86,6 +86,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
oldFiles = moveResult.OldFiles;
}
else
{
episodeFile.RelativePath = localEpisode.Series.Path.GetRelativePath(episodeFile.Path);
}
_mediaFileService.Add(episodeFile);
imported.Add(importDecision);

View File

@ -44,7 +44,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, bool sceneSource, QualityModel quality = null)
{
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series.Id);
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series);
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());

View File

@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
using NzbDrone.Common;
@ -16,7 +18,7 @@ namespace NzbDrone.Core.MediaFiles
List<EpisodeFile> GetFilesBySeries(int seriesId);
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<EpisodeFile> GetFilesWithoutMediaInfo();
List<string> FilterExistingFiles(List<string> files, int seriesId);
List<string> FilterExistingFiles(List<string> files, Series series);
EpisodeFile Get(int id);
List<EpisodeFile> Get(IEnumerable<int> ids);
}
@ -68,9 +70,9 @@ namespace NzbDrone.Core.MediaFiles
return _mediaFileRepository.GetFilesWithoutMediaInfo();
}
public List<string> FilterExistingFiles(List<string> files, int seriesId)
public List<string> FilterExistingFiles(List<string> files, Series series)
{
var seriesFiles = GetFilesBySeries(seriesId).Select(f => f.Path).ToList();
var seriesFiles = GetFilesBySeries(series.Id).Select(f => Path.Combine(series.Path, f.RelativePath)).ToList();
if (!seriesFiles.Any()) return files;

View File

@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
@ -39,25 +40,20 @@ namespace NzbDrone.Core.MediaFiles
foreach (var episodeFile in seriesFile)
{
var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
try
{
if (!_diskProvider.FileExists(episodeFile.Path))
if (!_diskProvider.FileExists(episodeFilePath))
{
_logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFile.Path);
_logger.Debug("File [{0}] no longer exists on disk, removing from db", episodeFilePath);
_mediaFileService.Delete(episodeFile);
continue;
}
if (!series.Path.IsParentPath(episodeFile.Path))
{
_logger.Debug("File [{0}] does not belong to this series, removing from db", episodeFile.Path);
_mediaFileService.Delete(episodeFile);
continue;
}
if (!episodes.Any(e => e.EpisodeFileId == episodeFile.Id))
{
_logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFile.Path);
_logger.Debug("File [{0}] is not assigned to any episodes, removing from db", episodeFilePath);
_mediaFileService.Delete(episodeFile);
continue;
}

View File

@ -1,13 +1,11 @@
using NLog;
using System.IO;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.MediaFiles.MediaInfo
{
@ -29,11 +27,11 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
_logger = logger;
}
private void UpdateMediaInfo(List<EpisodeFile> mediaFiles)
private void UpdateMediaInfo(Series series, List<EpisodeFile> mediaFiles)
{
foreach (var mediaFile in mediaFiles)
{
var path = mediaFile.Path;
var path = Path.Combine(series.Path, mediaFile.RelativePath);
if (!_diskProvider.FileExists(path))
{
@ -57,7 +55,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
.Where(c => c.MediaInfo == null)
.ToList();
UpdateMediaInfo(mediaFiles);
UpdateMediaInfo(message.Series, mediaFiles);
}
}
}

View File

@ -4,7 +4,7 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands;
@ -77,18 +77,19 @@ namespace NzbDrone.Core.MediaFiles
{
var file = f;
var episodesInFile = episodes.Where(e => e.EpisodeFileId == file.Id).ToList();
var episodeFilePath = Path.Combine(series.Path, file.RelativePath);
if (!episodesInFile.Any())
{
_logger.Warn("File ({0}) is not linked to any episodes", file.Path);
_logger.Warn("File ({0}) is not linked to any episodes", episodeFilePath);
continue;
}
var seasonNumber = episodesInFile.First().SeasonNumber;
var newName = _filenameBuilder.BuildFileName(episodesInFile, series, file);
var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(file.Path));
var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(episodeFilePath));
if (!file.Path.PathEquals(newPath))
if (!episodeFilePath.PathEquals(newPath))
{
yield return new RenameEpisodeFilePreview
{
@ -96,7 +97,7 @@ namespace NzbDrone.Core.MediaFiles
SeasonNumber = seasonNumber,
EpisodeNumbers = episodesInFile.Select(e => e.EpisodeNumber).ToList(),
EpisodeFileId = file.Id,
ExistingPath = series.Path.GetRelativePath(file.Path),
ExistingPath = file.RelativePath,
NewPath = series.Path.GetRelativePath(newPath)
};
}
@ -109,6 +110,8 @@ namespace NzbDrone.Core.MediaFiles
foreach (var episodeFile in episodeFiles)
{
var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
try
{
_logger.Debug("Renaming episode file: {0}", episodeFile);
@ -125,7 +128,7 @@ namespace NzbDrone.Core.MediaFiles
}
catch (Exception ex)
{
_logger.ErrorException("Failed to rename file: " + episodeFile.Path, ex);
_logger.ErrorException("Failed to rename file: " + episodeFilePath, ex);
}
}

View File

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
@ -43,6 +44,8 @@ namespace NzbDrone.Core.MediaFiles
private bool ChangeFileDate(EpisodeFile episodeFile, Series series, List<Episode> episodes)
{
var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
switch (_configService.FileDate)
{
case FileDateType.LocalAirDate:
@ -55,7 +58,7 @@ namespace NzbDrone.Core.MediaFiles
return false;
}
return ChangeFileDateToLocalAirDate(episodeFile.Path, airDate, airTime);
return ChangeFileDateToLocalAirDate(episodeFilePath, airDate, airTime);
}
case FileDateType.UtcAirDate:
@ -67,7 +70,7 @@ namespace NzbDrone.Core.MediaFiles
return false;
}
return ChangeFileDateToUtcAirDate(episodeFile.Path, airDateUtc.Value);
return ChangeFileDateToUtcAirDate(episodeFilePath, airDateUtc.Value);
}
}

View File

@ -1,4 +1,5 @@
using System.Linq;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
@ -43,11 +44,12 @@ namespace NzbDrone.Core.MediaFiles
foreach (var existingFile in existingFiles)
{
var file = existingFile.First();
var episodeFilePath = Path.Combine(localEpisode.Series.Path, file.RelativePath);
if (_diskProvider.FileExists(file.Path))
if (_diskProvider.FileExists(episodeFilePath))
{
_logger.Debug("Removing existing episode file: {0}", file);
_recycleBinProvider.DeleteFile(file.Path);
_recycleBinProvider.DeleteFile(episodeFilePath);
}
moveFileResult.OldFiles.Add(file);

View File

@ -1,6 +1,6 @@
using System.Threading;
using NLog;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
namespace NzbDrone.Core.Messaging.Commands
{

View File

@ -49,12 +49,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
if (metadataFile.Type == MetadataType.EpisodeImage)
{
newFilename = GetEpisodeImageFilename(episodeFile.Path);
newFilename = GetEpisodeImageFilename(episodeFile.RelativePath);
}
else if (metadataFile.Type == MetadataType.EpisodeMetadata)
{
newFilename = GetEpisodeMetadataFilename(episodeFile.Path);
newFilename = GetEpisodeMetadataFilename(episodeFile.RelativePath);
}
else
@ -64,6 +64,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
}
var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath);
newFilename = Path.Combine(series.Path, newFilename);
if (!newFilename.PathEquals(existingFilename))
{
@ -159,8 +160,8 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
{
return null;
}
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.RelativePath);
var xmlResult = String.Empty;
foreach (var episode in episodeFile.Episodes.Value)
@ -191,7 +192,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
}
}
return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray()));
return new MetadataFileResult(GetEpisodeMetadataFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray()));
}
public override List<ImageFileResult> SeriesImages(Series series)
@ -244,7 +245,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Roksbox
return new List<ImageFileResult>();
}
return new List<ImageFileResult> {new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url)};
return new List<ImageFileResult> {new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url)};
}
private string GetEpisodeMetadataFilename(string episodeFilePath)

View File

@ -48,12 +48,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
if (metadataFile.Type == MetadataType.EpisodeImage)
{
newFilename = GetEpisodeImageFilename(episodeFile.Path);
newFilename = GetEpisodeImageFilename(episodeFile.RelativePath);
}
else if (metadataFile.Type == MetadataType.EpisodeMetadata)
{
newFilename = GetEpisodeMetadataFilename(episodeFile.Path);
newFilename = GetEpisodeMetadataFilename(episodeFile.RelativePath);
}
else
@ -63,6 +63,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
}
var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath);
newFilename = Path.Combine(series.Path, newFilename);
if (!newFilename.PathEquals(existingFilename))
{
@ -151,7 +152,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
return null;
}
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
_logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath));
var xmlResult = String.Empty;
foreach (var episode in episodeFile.Episodes.Value)
@ -190,7 +191,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
}
}
var filename = GetEpisodeMetadataFilename(episodeFile.Path);
var filename = GetEpisodeMetadataFilename(episodeFile.RelativePath);
return new MetadataFileResult(filename, xmlResult.Trim(Environment.NewLine.ToCharArray()));
}
@ -264,7 +265,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Wdtv
return new List<ImageFileResult>();
}
return new List<ImageFileResult>{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url) };
return new List<ImageFileResult>{ new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url) };
}
private string GetEpisodeMetadataFilename(string episodeFilePath)

View File

@ -50,12 +50,12 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
if (metadataFile.Type == MetadataType.EpisodeImage)
{
newFilename = GetEpisodeImageFilename(episodeFile.Path);
newFilename = GetEpisodeImageFilename(episodeFile.RelativePath);
}
else if (metadataFile.Type == MetadataType.EpisodeMetadata)
{
newFilename = GetEpisodeNfoFilename(episodeFile.Path);
newFilename = GetEpisodeNfoFilename(episodeFile.RelativePath);
}
else
@ -65,6 +65,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
}
var existingFilename = Path.Combine(series.Path, metadataFile.RelativePath);
newFilename = Path.Combine(series.Path, newFilename);
if (!newFilename.PathEquals(existingFilename))
{
@ -214,7 +215,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
return null;
}
_logger.Debug("Generating Episode Metadata for: {0}", episodeFile.Path);
_logger.Debug("Generating Episode Metadata for: {0}", Path.Combine(series.Path, episodeFile.RelativePath));
var xmlResult = String.Empty;
foreach (var episode in episodeFile.Episodes.Value)
@ -265,7 +266,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
}
}
return new MetadataFileResult(GetEpisodeNfoFilename(episodeFile.Path), xmlResult.Trim(Environment.NewLine.ToCharArray()));
return new MetadataFileResult(GetEpisodeNfoFilename(episodeFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray()));
}
public override List<ImageFileResult> SeriesImages(Series series)
@ -305,7 +306,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc
return new List<ImageFileResult>
{
new ImageFileResult(GetEpisodeImageFilename(episodeFile.Path), screenshot.Url)
new ImageFileResult(GetEpisodeImageFilename(episodeFile.RelativePath), screenshot.Url)
};
}

View File

@ -174,18 +174,18 @@ namespace NzbDrone.Core.Metadata
return null;
}
var relativePath = series.Path.GetRelativePath(episodeMetadata.Path);
var fullPath = Path.Combine(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))
var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
if (!episodeMetadata.Path.PathEquals(existingFullPath))
{
_diskProvider.MoveFile(fullPath, episodeMetadata.Path);
existingMetadata.RelativePath = relativePath;
_diskProvider.MoveFile(existingFullPath, fullPath);
existingMetadata.RelativePath = episodeMetadata.Path;
}
}
@ -198,7 +198,7 @@ namespace NzbDrone.Core.Metadata
EpisodeFileId = episodeFile.Id,
Consumer = consumer.GetType().Name,
Type = MetadataType.EpisodeMetadata,
RelativePath = relativePath
RelativePath = episodeMetadata.Path
};
if (hash == metadata.Hash)
@ -206,8 +206,8 @@ namespace NzbDrone.Core.Metadata
return null;
}
_logger.Debug("Writing Episode Metadata to: {0}", episodeMetadata.Path);
_diskProvider.WriteAllText(episodeMetadata.Path, episodeMetadata.Contents);
_logger.Debug("Writing Episode Metadata to: {0}", fullPath);
_diskProvider.WriteAllText(fullPath, episodeMetadata.Contents);
metadata.Hash = hash;
@ -289,24 +289,24 @@ namespace NzbDrone.Core.Metadata
foreach (var image in consumer.EpisodeImages(series, episodeFile))
{
var fullPath = Path.Combine(series.Path, image.Path);
if (_diskProvider.FileExists(image.Path))
{
_logger.Debug("Episode image already exists: {0}", image.Path);
continue;
}
var relativePath = series.Path.GetRelativePath(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))
var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
if (!fullPath.PathEquals(existingFullPath))
{
_diskProvider.MoveFile(fullPath, image.Path);
existingMetadata.RelativePath = relativePath;
_diskProvider.MoveFile(fullPath, fullPath);
existingMetadata.RelativePath = image.Path;
return new List<MetadataFile>{ existingMetadata };
}

View File

@ -215,6 +215,8 @@
<Compile Include="Datastore\Migration\054_rename_profiles.cs" />
<Compile Include="Datastore\Migration\055_drop_old_profile_columns.cs" />
<Compile Include="Datastore\Migration\056_add_mediainfo_to_episodefile.cs" />
<Compile Include="Datastore\Migration\057_convert_episode_file_path_to_relative.cs" />
<Compile Include="Datastore\Migration\058_drop_epsiode_file_path.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
@ -427,7 +429,6 @@
<Compile Include="Instrumentation\Commands\TrimLogCommand.cs" />
<Compile Include="Instrumentation\DatabaseTarget.cs" />
<Compile Include="Instrumentation\DeleteLogFilesService.cs" />
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
<Compile Include="Instrumentation\Log.cs" />
<Compile Include="Instrumentation\LogRepository.cs" />
<Compile Include="Instrumentation\LogService.cs" />
@ -701,6 +702,7 @@
<Compile Include="ThingiProvider\ProviderFactory.cs" />
<Compile Include="ThingiProvider\ProviderRepository.cs" />
<Compile Include="Tv\Actor.cs" />
<Compile Include="Tv\Commands\MoveSeriesCommand.cs" />
<Compile Include="Tv\Commands\RefreshSeriesCommand.cs" />
<Compile Include="Tv\Episode.cs" />
<Compile Include="Tv\EpisodeCutoffService.cs" />
@ -714,8 +716,10 @@
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
<Compile Include="Tv\Events\SeriesDeletedEvent.cs" />
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />
<Compile Include="Tv\Events\SeriesMovedEvent.cs" />
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
<Compile Include="Tv\MoveSeriesService.cs" />
<Compile Include="Tv\Ratings.cs" />
<Compile Include="Tv\RefreshEpisodeService.cs" />
<Compile Include="Tv\RefreshSeriesService.cs" />

View File

@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -77,7 +78,7 @@ namespace NzbDrone.Core.Organizer
{
if (episodeFile.SceneName.IsNullOrWhiteSpace())
{
return Path.GetFileNameWithoutExtension(episodeFile.Path);
return Path.GetFileNameWithoutExtension(episodeFile.RelativePath);
}
return episodeFile.SceneName;
@ -204,6 +205,8 @@ namespace NzbDrone.Core.Organizer
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
{
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
string path = series.Path;
if (series.SeasonFolder)

View File

@ -93,7 +93,7 @@ namespace NzbDrone.Core.Organizer
_singleEpisodeFile = new EpisodeFile
{
Quality = new QualityModel(Quality.HDTV720p),
Path = @"C:\Test\Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv",
RelativePath = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv",
ReleaseGroup = "RlsGrp",
MediaInfo = mediaInfo
};
@ -101,7 +101,7 @@ namespace NzbDrone.Core.Organizer
_multiEpisodeFile = new EpisodeFile
{
Quality = new QualityModel(Quality.HDTV720p),
Path = @"C:\Test\Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv",
RelativePath = "Series.Title.S01E01-E02.720p.HDTV.x264-EVOLVE.mkv",
ReleaseGroup = "RlsGrp",
MediaInfo = mediaInfo
};
@ -109,7 +109,7 @@ namespace NzbDrone.Core.Organizer
_dailyEpisodeFile = new EpisodeFile
{
Quality = new QualityModel(Quality.HDTV720p),
Path = @"C:\Test\Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv",
RelativePath = "Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv",
ReleaseGroup = "RlsGrp",
MediaInfo = mediaInfo
};
@ -117,7 +117,7 @@ namespace NzbDrone.Core.Organizer
_animeEpisodeFile = new EpisodeFile
{
Quality = new QualityModel(Quality.HDTV720p),
Path = @"C:\Test\Series.Title.001.HDTV.x264-EVOLVE.mkv",
RelativePath = "Series.Title.001.HDTV.x264-EVOLVE.mkv",
ReleaseGroup = "RlsGrp",
MediaInfo = mediaInfoAnime
};

View File

@ -8,7 +8,6 @@ using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.ProgressMessaging
{
public class ProgressMessageTarget : Target, IHandle<ApplicationStartedEvent>
{
private readonly IEventAggregator _eventAggregator;

View File

@ -0,0 +1,13 @@
using System;
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Tv.Commands
{
public class MoveSeriesCommand : Command
{
public Int32 SeriesId { get; set; }
public String SourcePath { get; set; }
public String DestinationPath { get; set; }
public String DestinationRootFolder { get; set; }
}
}

View File

@ -199,7 +199,7 @@ namespace NzbDrone.Core.Tv
foreach (var episode in message.EpisodeFile.Episodes.Value)
{
_episodeRepository.SetFileId(episode.Id, message.EpisodeFile.Id);
_logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.Path, episode);
_logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.RelativePath, episode);
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesMovedEvent : IEvent
{
public Series Series { get; set; }
public String SourcePath { get; set; }
public String DestinationPath { get; set; }
public SeriesMovedEvent(Series series, string sourcePath, string destinationPath)
{
Series = series;
SourcePath = sourcePath;
DestinationPath = destinationPath;
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Tv.Commands;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Tv
{
public class MoveSeriesService : IExecute<MoveSeriesCommand>
{
private readonly ISeriesService _seriesService;
private readonly IBuildFileNames _filenameBuilder;
private readonly IDiskProvider _diskProvider;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
public MoveSeriesService(ISeriesService seriesService,
IBuildFileNames filenameBuilder,
IDiskProvider diskProvider,
IEventAggregator eventAggregator,
Logger logger)
{
_seriesService = seriesService;
_filenameBuilder = filenameBuilder;
_diskProvider = diskProvider;
_eventAggregator = eventAggregator;
_logger = logger;
}
public void Execute(MoveSeriesCommand message)
{
var series = _seriesService.GetSeries(message.SeriesId);
var source = message.SourcePath;
var destination = message.DestinationPath;
if (!message.DestinationRootFolder.IsNullOrWhiteSpace())
{
_logger.Debug("Buiding destination path using root folder: {0} and the series title", message.DestinationRootFolder);
destination = Path.Combine(message.DestinationRootFolder, _filenameBuilder.GetSeriesFolder(series));
}
_logger.ProgressInfo("Moving {0} from '{1}' to '{2}'", series.Title, source, destination);
//TODO: Move to transactional disk operations
try
{
_diskProvider.MoveFolder(source, destination);
}
catch (IOException ex)
{
var errorMessage = String.Format("Unable to move series from '{0}' to '{1}'", source, destination);
_logger.ErrorException(errorMessage, ex);
throw;
}
_logger.ProgressInfo("{0} moved successfully to {1}", series.Title, series.Path);
//Update the series path to the new path
series.Path = destination;
series = _seriesService.UpdateSeries(series);
_eventAggregator.PublishEvent(new SeriesMovedEvent(series, source, destination));
}
}
}

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.DataAugmentation.DailySeries;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;

View File

@ -16,8 +16,6 @@ namespace NzbDrone.Core.Tv
public void Handle(SeriesEditedEvent message)
{
//TODO: Refresh if path has changed (also move files)
if (message.Series.SeriesType != message.OldSeries.SeriesType)
{
_commandExecutor.PublishCommandAsync(new RefreshSeriesCommand(message.Series.Id));

View File

@ -5,10 +5,10 @@ using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Processes;
using NzbDrone.Core.Backup;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Instrumentation.Extensions;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Update.Commands;

View File

@ -1,7 +1,7 @@
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Instrumentation.Extensions;
namespace NzbDrone.Core.Update
{