moved rootdir to eloquera

pull/4/head
kay.one 12 years ago
parent 0155de4d92
commit 9e4bb278ef

@ -3,17 +3,18 @@ using Nancy;
using NzbDrone.Api.Extentions; using NzbDrone.Api.Extentions;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Api.RootFolders namespace NzbDrone.Api.RootFolders
{ {
public class RootDirModule : NzbDroneApiModule public class RootDirModule : NzbDroneApiModule
{ {
private readonly RootDirProvider _rootDirProvider; private readonly RootFolderService _rootFolderService;
public RootDirModule(RootDirProvider rootDirProvider) public RootDirModule(RootFolderService rootFolderService)
: base("//rootdir") : base("//rootdir")
{ {
_rootDirProvider = rootDirProvider; _rootFolderService = rootFolderService;
Get["/"] = x => GetRootFolders(); Get["/"] = x => GetRootFolders();
Post["/"] = x => AddRootFolder(); Post["/"] = x => AddRootFolder();
@ -22,18 +23,18 @@ namespace NzbDrone.Api.RootFolders
private Response AddRootFolder() private Response AddRootFolder()
{ {
var dir = _rootDirProvider.Add(Request.Body.FromJson<RootDir>()); var dir = _rootFolderService.Add(Request.Body.FromJson<RootDir>());
return dir.AsResponse(HttpStatusCode.Created); return dir.AsResponse(HttpStatusCode.Created);
} }
private Response GetRootFolders() private Response GetRootFolders()
{ {
return _rootDirProvider.AllWithFreeSpace().AsResponse(); return _rootFolderService.All().AsResponse();
} }
private Response DeleteRootFolder(int folderId) private Response DeleteRootFolder(int folderId)
{ {
_rootDirProvider.Remove(folderId); _rootFolderService.Remove(folderId);
return new Response { StatusCode = HttpStatusCode.OK }; return new Response { StatusCode = HttpStatusCode.OK };
} }
} }

@ -107,7 +107,15 @@ namespace NzbDrone.Common
{ {
get get
{ {
return new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName; var path = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName;
if (path.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows),
StringComparison.InvariantCultureIgnoreCase))
{
path = Directory.GetCurrentDirectory();
}
return path;
} }
} }

@ -7,6 +7,7 @@ namespace NzbDrone.Common
{ {
private const string WEB_FOLDER = "NzbDrone.Web\\"; private const string WEB_FOLDER = "NzbDrone.Web\\";
private const string APP_DATA = "App_Data\\"; private const string APP_DATA = "App_Data\\";
private const string WEB_BIN = "bin\\";
public const string IIS_FOLDER = "IISExpress"; public const string IIS_FOLDER = "IISExpress";
public const string IIS_EXE = "iisexpress.exe"; public const string IIS_EXE = "iisexpress.exe";
@ -70,6 +71,11 @@ namespace NzbDrone.Common
return Path.Combine(environmentProvider.GetWebRoot(), APP_DATA); return Path.Combine(environmentProvider.GetWebRoot(), APP_DATA);
} }
public static string GetWebBinPath(this EnvironmentProvider environmentProvider)
{
return Path.Combine(environmentProvider.GetWebRoot(), WEB_BIN);
}
public static string GetNlogConfigPath(this EnvironmentProvider environmentProvider) public static string GetNlogConfigPath(this EnvironmentProvider environmentProvider)
{ {
return Path.Combine(environmentProvider.GetWebRoot(), LOG_CONFIG_FILE); return Path.Combine(environmentProvider.GetWebRoot(), LOG_CONFIG_FILE);

@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Eloquera.Client;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -82,6 +83,19 @@ namespace NzbDrone.Core.Test.Datastore
Db.Insert(testSeries); Db.Insert(testSeries);
testSeries.Id.Should().NotBe(0); testSeries.Id.Should().NotBe(0);
} }
[Test]
public void should_be_able_to_read_unknow_type()
{
Db.AsQueryable<UnKnowType>().ToList().Should().BeEmpty();
}
}
public class UnKnowType
{
[ID]
public string Id;
public string Field1 { get; set; }
} }
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Test.Framework namespace NzbDrone.Core.Test.Framework
@ -23,11 +24,11 @@ namespace NzbDrone.Core.Test.Framework
{ {
if (memory) if (memory)
{ {
_db = new EloqueraDbFactory().CreateMemoryDb(); _db = new EloqueraDbFactory(new EnvironmentProvider()).CreateMemoryDb();
} }
else else
{ {
_db = new EloqueraDbFactory().Create(Guid.NewGuid().ToString()); _db = new EloqueraDbFactory(new EnvironmentProvider()).Create();
} }
Mocker.SetConstant(Db); Mocker.SetConstant(Db);

@ -28,6 +28,32 @@ namespace NzbDrone.Core.Test.Framework
} }
public abstract class CoreTest<TSubject> : CoreTest
{
private TSubject _subject;
protected TSubject Subject
{
get
{
if (_subject == null)
{
_subject = Mocker.Resolve<TSubject>();
}
return _subject;
}
}
protected void InitiateSubject()
{
_subject = Mocker.Resolve<TSubject>();
}
}
public abstract class SqlCeTest : CoreTest public abstract class SqlCeTest : CoreTest
{ {

@ -79,6 +79,7 @@
</Reference> </Reference>
<Reference Include="Eloquera.Server"> <Reference Include="Eloquera.Server">
<HintPath>..\packages\EloqueraDB.5.0.0\lib\net40\Eloquera.Server.exe</HintPath> <HintPath>..\packages\EloqueraDB.5.0.0\lib\net40\Eloquera.Server.exe</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL"> <Reference Include="FizzWare.NBuilder, Version=3.0.1.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
<HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath> <HintPath>..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll</HintPath>

@ -31,5 +31,6 @@
<RegularExpression>NzbDrone\.Core\.Test\.Integeration\.ServiceIntegerationFixture\..*</RegularExpression> <RegularExpression>NzbDrone\.Core\.Test\.Integeration\.ServiceIntegerationFixture\..*</RegularExpression>
</RegexTestSelector> </RegexTestSelector>
</IgnoredTests> </IgnoredTests>
<AdditionalFilesToInclude>..\NzbDrone.Core\bin\Debug\Eloquera.Server.exe</AdditionalFilesToInclude>
<HiddenWarnings>PostBuildEventDisabled</HiddenWarnings> <HiddenWarnings>PostBuildEventDisabled</HiddenWarnings>
</ProjectConfiguration> </ProjectConfiguration>

@ -12,6 +12,7 @@ using NzbDrone.Common;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
@ -38,7 +39,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
.Setup(s => s.FreeDiskSpace(new DirectoryInfo(@"C:\"))) .Setup(s => s.FreeDiskSpace(new DirectoryInfo(@"C:\")))
.Returns(123456); .Returns(123456);
var result = Mocker.Resolve<RootDirProvider>().FreeSpaceOnDrives(); var result = Mocker.Resolve<RootFolderService>().FreeSpaceOnDrives();
result.Should().HaveCount(1); result.Should().HaveCount(1);
} }
@ -59,7 +60,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
.Setup(s => s.FreeDiskSpace(new DirectoryInfo(@"C:\"))) .Setup(s => s.FreeDiskSpace(new DirectoryInfo(@"C:\")))
.Returns(123456); .Returns(123456);
var result = Mocker.Resolve<RootDirProvider>().FreeSpaceOnDrives(); var result = Mocker.Resolve<RootFolderService>().FreeSpaceOnDrives();
result.Should().HaveCount(1); result.Should().HaveCount(1);
} }
@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
.Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())) .Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>()))
.Returns(123456); .Returns(123456);
var result = Mocker.Resolve<RootDirProvider>().FreeSpaceOnDrives(); var result = Mocker.Resolve<RootFolderService>().FreeSpaceOnDrives();
result.Should().HaveCount(2); result.Should().HaveCount(2);
} }
@ -104,7 +105,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
.Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())) .Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>()))
.Throws(new DirectoryNotFoundException()); .Throws(new DirectoryNotFoundException());
var result = Mocker.Resolve<RootDirProvider>().FreeSpaceOnDrives(); var result = Mocker.Resolve<RootFolderService>().FreeSpaceOnDrives();
result.Should().HaveCount(0); result.Should().HaveCount(0);

@ -1,6 +1,7 @@
// ReSharper disable RedundantUsingDirective // ReSharper disable RedundantUsingDirective
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -11,6 +12,7 @@ using NzbDrone.Common;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
@ -18,7 +20,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
{ {
[TestFixture] [TestFixture]
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public class RootDirProviderFixture : SqlCeTest public class RootDirProviderFixture : CoreTest<RootFolderService>
{ {
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -35,32 +37,15 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
.Returns(false); .Returns(false);
} }
[Test]
public void GetRootDir_should_return_all_existing_roots()
{
WithRealDb();
Db.Insert(new RootDir { Path = @"C:\TV" });
Db.Insert(new RootDir { Path = @"C:\TV2" });
var result = Mocker.Resolve<RootDirProvider>().GetAll();
result.Should().HaveCount(2);
}
[TestCase("D:\\TV Shows\\")] [TestCase("D:\\TV Shows\\")]
[TestCase("//server//folder")] [TestCase("//server//folder")]
public void should_be_able_to_add_root_dir(string path) public void should_be_able_to_add_root_dir(string path)
{ {
WithRealDb(); var root = new RootDir { Path = path };
Subject.Add(root);
//Act Mocker.GetMock<IRootFolderRepository>().Verify(c => c.Add(root), Times.Once());
var rootDirProvider = Mocker.Resolve<RootDirProvider>();
rootDirProvider.Add(new RootDir { Path = path });
//Assert
var rootDirs = rootDirProvider.GetAll();
rootDirs.Should().HaveCount(1);
rootDirs.First().Path.Should().Be(path);
} }
[Test] [Test]
@ -68,34 +53,25 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
{ {
WithNoneExistingFolder(); WithNoneExistingFolder();
var rootDirProvider = Mocker.Resolve<RootDirProvider>(); Assert.Throws<DirectoryNotFoundException>(() => Subject.Add(new RootDir { Path = "C:\\TEST" }));
Assert.Throws<DirectoryNotFoundException>(() => rootDirProvider.Add(new RootDir { Path = "C:\\TEST" }));
} }
[Test] [Test]
public void should_be_able_to_remove_root_dir() public void should_be_able_to_remove_root_dir()
{ {
WithRealDb(); Subject.Remove(1);
Mocker.GetMock<IRootFolderRepository>().Verify(c => c.Delete(1), Times.Once());
//Act
var rootDirProvider = Mocker.Resolve<RootDirProvider>();
rootDirProvider.Add(new RootDir { Path = @"C:\TV" });
rootDirProvider.Add(new RootDir { Path = @"C:\TV2" });
rootDirProvider.Remove(1);
//Assert
var rootDirs = rootDirProvider.GetAll();
rootDirs.Should().HaveCount(1);
} }
[Test]
public void None_existing_folder_returns_empty_list() public void None_existing_folder_returns_empty_list()
{ {
WithNoneExistingFolder(); WithNoneExistingFolder();
Mocker.GetMock<IRootFolderRepository>().Setup(c => c.All()).Returns(new List<RootDir>());
const string path = "d:\\bad folder"; const string path = "d:\\bad folder";
var result = Mocker.Resolve<RootDirProvider>().GetUnmappedFolders(path); var result = Subject.GetUnmappedFolders(path);
result.Should().NotBeNull(); result.Should().NotBeNull();
result.Should().BeEmpty(); result.Should().BeEmpty();
@ -105,7 +81,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
[Test] [Test]
public void GetUnmappedFolders_throw_on_empty_folders() public void GetUnmappedFolders_throw_on_empty_folders()
{ {
Assert.Throws<ArgumentException>(() => Mocker.Resolve<RootDirProvider>().GetUnmappedFolders("")); Assert.Throws<ArgumentException>(() => Mocker.Resolve<RootFolderService>().GetUnmappedFolders(""));
} }
[TestCase("")] [TestCase("")]
@ -114,19 +90,17 @@ namespace NzbDrone.Core.Test.ProviderTests.RootDirProviderTests
public void invalid_folder_path_throws_on_add(string path) public void invalid_folder_path_throws_on_add(string path)
{ {
Assert.Throws<ArgumentException>(() => Assert.Throws<ArgumentException>(() =>
Mocker.Resolve<RootDirProvider>().Add(new RootDir { Id = 0, Path = path }) Mocker.Resolve<RootFolderService>().Add(new RootDir { Id = 0, Path = path })
); );
} }
[Test] [Test]
public void adding_duplicated_root_folder_should_throw() public void adding_duplicated_root_folder_should_throw()
{ {
WithRealDb(); Mocker.GetMock<IRootFolderRepository>().Setup(c => c.All()).Returns(new List<RootDir> { new RootDir { Path = "C:\\TV" } });
//Act Subject.Add(new RootDir { Path = @"C:\TV" });
var rootDirProvider = Mocker.Resolve<RootDirProvider>(); Assert.Throws<InvalidOperationException>(() => Subject.Add(new RootDir { Path = @"C:\TV" }));
rootDirProvider.Add(new RootDir { Path = @"C:\TV" });
Assert.Throws<InvalidOperationException>(() => rootDirProvider.Add(new RootDir { Path = @"C:\TV" }));
} }
} }
} }

@ -82,8 +82,7 @@ namespace NzbDrone.Core
container.Register(c => container.Register(c =>
{ {
var env = c.Resolve<EnvironmentProvider>(); return c.Resolve<EloqueraDbFactory>().Create();
return c.Resolve<EloqueraDbFactory>().Create(env.GetElqMainDbPath());
}).As<EloqueraDb>().SingleInstance(); }).As<EloqueraDb>().SingleInstance();
container.RegisterType<DatabaseTarget>().WithParameter(ResolvedParameter.ForNamed<IDatabase>("DatabaseTarget")); container.RegisterType<DatabaseTarget>().WithParameter(ResolvedParameter.ForNamed<IDatabase>("DatabaseTarget"));

@ -43,12 +43,12 @@ namespace NzbDrone.Core.Datastore
} }
public void Delete<T>(T obj) public void Delete<T>(T obj) where T : new()
{ {
_db.Delete(obj); _db.Delete(obj);
} }
public void DeleteMany<T>(IEnumerable<T> objects) public void DeleteMany<T>(IEnumerable<T> objects) where T: new()
{ {
foreach (var o in objects) foreach (var o in objects)
{ {

@ -2,27 +2,52 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Eloquera.Client; using Eloquera.Client;
using NzbDrone.Common;
namespace NzbDrone.Core.Datastore namespace NzbDrone.Core.Datastore
{ {
public class EloqueraDbFactory public class EloqueraDbFactory
{ {
private readonly EnvironmentProvider _environmentProvider;
private readonly string dllPath;
public EloqueraDbFactory(EnvironmentProvider environmentProvider)
{
_environmentProvider = environmentProvider;
dllPath = _environmentProvider.GetWebBinPath();// this is the path where Eloquera dlls live.
}
public EloqueraDb CreateMemoryDb() public EloqueraDb CreateMemoryDb()
{ {
return InternalCreate("server=(local);password=;options=inmemory;",Guid.NewGuid().ToString()); return InternalCreate("server=(local);password=;options=inmemory;uselocalpath=" + dllPath, Guid.NewGuid().ToString());
} }
public EloqueraDb Create(string dbPath) public EloqueraDb Create(string dbPath = null)
{ {
var file = new FileInfo(dbPath).Name; if (dbPath == null)
return InternalCreate(string.Format("server=(local);database={0};usedatapath={1};password=;", file, dbPath),file); {
dbPath = _environmentProvider.GetElqMainDbPath();
}
var file = new FileInfo(dbPath);
return InternalCreate(string.Format("server=(local);password=;usedatapath={0};uselocalpath={1}", file.Directory.FullName, dllPath), file.Name);
} }
private EloqueraDb InternalCreate(string connectionString, string databaseName) private EloqueraDb InternalCreate(string connectionString, string databaseName)
{ {
var db = new DB(connectionString); var db = new DB(connectionString);
db.CreateDatabase(databaseName); try
db.OpenDatabase(databaseName); {
db.OpenDatabase(databaseName);
}
catch (FileNotFoundException)
{
db.CreateDatabase(databaseName);
db.OpenDatabase(databaseName);
}
return new EloqueraDb(db); return new EloqueraDb(db);
} }

@ -0,0 +1,33 @@
using System.Collections.Generic;
using Migrator.Framework;
using NzbDrone.Core.Repository;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Core.Datastore.Migrations
{
[Migration(20130203)]
public class Migration20130203 : NzbDroneMigration
{
protected override void MainDbUpgrade()
{
var objectDb = GetObjectDb();
var rootFolderRepo = new RootFolderRepository(objectDb);
using (var dataReader = Database.ExecuteQuery("SELECT * from RootDirs"))
{
var dirs = new List<RootDir>();
while (dataReader.Read())
{
var rootFolder = new RootDir { Path = dataReader["Path"].ToString() };
dirs.Add(rootFolder);
}
objectDb.InsertMany(dirs);
}
//Database.RemoveTable("RootDirs");
}
}
}

@ -1,4 +1,5 @@
using System; using System;
using System.Data.SqlServerCe;
using System.Linq; using System.Linq;
using Migrator.Framework; using Migrator.Framework;
using NzbDrone.Common; using NzbDrone.Common;
@ -32,6 +33,14 @@ namespace NzbDrone.Core.Datastore.Migrations
} }
} }
protected EloqueraDb GetObjectDb()
{
var sqlCeConnection = new SqlCeConnection(Database.ConnectionString);
var eqPath = sqlCeConnection.Database.Replace(".sdf", ".eq");
return new EloqueraDbFactory(new EnvironmentProvider()).Create(eqPath);
}
public override void Down() public override void Down()
{ {

@ -9,37 +9,37 @@ namespace NzbDrone.Core.Datastore
{ {
public class MigrationsHelper public class MigrationsHelper
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public static void Run(string connetionString, bool trace) public static void Run(string connectionString, bool trace)
{ {
EnsureDatabase(connetionString); EnsureDatabase(connectionString);
Logger.Trace("Preparing to run database migration"); logger.Trace("Preparing to run database migration");
try try
{ {
Migrator.Migrator migrator; Migrator.Migrator migrator;
if (trace) if (trace)
{ {
migrator = new Migrator.Migrator("sqlserverce", connetionString, Assembly.GetAssembly(typeof(MigrationsHelper)), true, new MigrationLogger()); migrator = new Migrator.Migrator("sqlserverce", connectionString, Assembly.GetAssembly(typeof(MigrationsHelper)), true, new MigrationLogger());
} }
else else
{ {
migrator = new Migrator.Migrator("sqlserverce", connetionString, Assembly.GetAssembly(typeof(MigrationsHelper))); migrator = new Migrator.Migrator("sqlserverce", connectionString, Assembly.GetAssembly(typeof(MigrationsHelper)));
} }
migrator.MigrateToLastVersion(); migrator.MigrateToLastVersion();
Logger.Info("Database migration completed"); logger.Info("Database migration completed");
} }
catch (Exception e) catch (Exception e)
{ {
Logger.FatalException("An error has occurred while migrating database", e); logger.FatalException("An error has occurred while migrating database", e);
throw; throw;
} }
} }

@ -237,6 +237,7 @@
<Compile Include="Datastore\MigrationLogger.cs" /> <Compile Include="Datastore\MigrationLogger.cs" />
<Compile Include="Datastore\MigrationsHelper.cs" /> <Compile Include="Datastore\MigrationsHelper.cs" />
<Compile Include="Datastore\CustomeMapper.cs" /> <Compile Include="Datastore\CustomeMapper.cs" />
<Compile Include="Datastore\Migrations\Migration20130203.cs" />
<Compile Include="Datastore\Migrations\Migration20121226.cs" /> <Compile Include="Datastore\Migrations\Migration20121226.cs" />
<Compile Include="Datastore\Migrations\Migration20121218.cs" /> <Compile Include="Datastore\Migrations\Migration20121218.cs" />
<Compile Include="Datastore\Migrations\Migration20121209.cs" /> <Compile Include="Datastore\Migrations\Migration20121209.cs" />
@ -536,7 +537,7 @@
<Compile Include="Providers\ReferenceDataProvider.cs"> <Compile Include="Providers\ReferenceDataProvider.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Providers\RootDirProvider.cs"> <Compile Include="RootFolders\RootFolderService.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Providers\SceneMappingProvider.cs"> <Compile Include="Providers\SceneMappingProvider.cs">
@ -599,6 +600,7 @@
<Compile Include="Repository\Series.cs" /> <Compile Include="Repository\Series.cs" />
<Compile Include="CentralDispatch.cs" /> <Compile Include="CentralDispatch.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RootFolders\RootFolderRepository.cs" />
<Compile Include="Tvdb\Tvdb.cs" /> <Compile Include="Tvdb\Tvdb.cs" />
<Compile Include="Tvdb\Tvdb.Sync.cs" /> <Compile Include="Tvdb\Tvdb.Sync.cs" />
<Compile Include="Tvdb\TvdbActor.cs" /> <Compile Include="Tvdb\TvdbActor.cs" />

@ -3,9 +3,21 @@ using PetaPoco;
namespace NzbDrone.Core.Repository namespace NzbDrone.Core.Repository
{ {
public interface IRootDir
{
int Id { get; set; }
string Path { get; set; }
[ResultColumn]
ulong FreeSpace { get; set; }
[Ignore]
List<string> UnmappedFolders { get; set; }
}
[TableName("RootDirs")] [TableName("RootDirs")]
[PrimaryKey("Id", autoIncrement = true)] [PrimaryKey("Id", autoIncrement = true)]
public class RootDir public class RootDir : IRootDir
{ {
public virtual int Id { get; set; } public virtual int Id { get; set; }

@ -0,0 +1,46 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Repository;
using System.Linq;
namespace NzbDrone.Core.RootFolders
{
public interface IRootFolderRepository
{
List<RootDir> All();
RootDir Get(int rootFolderId);
RootDir Add(RootDir rootFolder);
void Delete(int rootFolderId);
}
public class RootFolderRepository : IRootFolderRepository
{
private readonly EloqueraDb _db;
public RootFolderRepository(EloqueraDb db)
{
_db = db;
}
public List<RootDir> All()
{
return _db.AsQueryable<RootDir>().ToList();
}
public RootDir Get(int rootFolderId)
{
return _db.AsQueryable<RootDir>().Single(c => c.Id == rootFolderId);
}
public RootDir Add(RootDir rootFolder)
{
return _db.Insert(rootFolder);
}
public void Delete(int rootFolderId)
{
_db.Delete(Get(rootFolderId));
}
}
}

@ -1,30 +1,40 @@
using System; using System.Linq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using PetaPoco;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.RootFolders
{ {
public class RootDirProvider public interface IRootFolderService
{
List<RootDir> All();
RootDir Add(RootDir rootDir);
void Remove(int rootDirId);
List<String> GetUnmappedFolders(string path);
Dictionary<string, ulong> FreeSpaceOnDrives();
}
public class RootFolderService : IRootFolderService
{ {
private readonly IDatabase _database;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IRootFolderRepository _rootFolderRepository;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
public RootDirProvider(IDatabase database, SeriesProvider seriesProvider, DiskProvider diskProvider) public RootFolderService(IRootFolderRepository rootFolderRepository, SeriesProvider seriesProvider, DiskProvider diskProvider)
{ {
_database = database; _rootFolderRepository = rootFolderRepository;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
} }
public virtual List<RootDir> GetAll() public virtual List<RootDir> All()
{ {
return _database.Fetch<RootDir>(); return _rootFolderRepository.All();
} }
public virtual RootDir Add(RootDir rootDir) public virtual RootDir Add(RootDir rootDir)
@ -35,20 +45,19 @@ namespace NzbDrone.Core.Providers
if (!_diskProvider.FolderExists(rootDir.Path)) if (!_diskProvider.FolderExists(rootDir.Path))
throw new DirectoryNotFoundException("Can't add root directory that doesn't exist."); throw new DirectoryNotFoundException("Can't add root directory that doesn't exist.");
if (GetAll().Exists(r => DiskProvider.PathEquals(r.Path, rootDir.Path))) if (All().Exists(r => DiskProvider.PathEquals(r.Path, rootDir.Path)))
throw new InvalidOperationException("Root directory already exist."); throw new InvalidOperationException("Root directory already exist.");
var id = _database.Insert(rootDir); _rootFolderRepository.Add(rootDir);
rootDir.Id = Convert.ToInt32(id);
rootDir.FreeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(rootDir.Path)); rootDir.FreeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(rootDir.Path));
rootDir.UnmappedFolders = GetUnmappedFolders(rootDir.Path); rootDir.UnmappedFolders = GetUnmappedFolders(rootDir.Path);
return rootDir; return rootDir;
} }
public virtual void Remove(int rootDirId) public virtual void Remove(int rootDirId)
{ {
_database.Delete<RootDir>(rootDirId); _rootFolderRepository.Delete(rootDirId);
} }
public virtual List<String> GetUnmappedFolders(string path) public virtual List<String> GetUnmappedFolders(string path)
@ -77,24 +86,12 @@ namespace NzbDrone.Core.Providers
return results; return results;
} }
public virtual List<RootDir> AllWithFreeSpace()
{
var rootDirs = GetAll();
foreach (var rootDir in rootDirs)
{
rootDir.FreeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(rootDir.Path));
rootDir.UnmappedFolders = GetUnmappedFolders(rootDir.Path);
}
return rootDirs;
}
public virtual Dictionary<string, ulong> FreeSpaceOnDrives() public virtual Dictionary<string, ulong> FreeSpaceOnDrives()
{ {
var freeSpace = new Dictionary<string, ulong>(); var freeSpace = new Dictionary<string, ulong>();
var rootDirs = GetAll(); var rootDirs = All();
foreach (var rootDir in rootDirs) foreach (var rootDir in rootDirs)
{ {

@ -14,7 +14,7 @@
<RootNamespace>NzbDrone.Services.Api</RootNamespace> <RootNamespace>NzbDrone.Services.Api</RootNamespace>
<AssemblyName>NzbDrone.Services.Api</AssemblyName> <AssemblyName>NzbDrone.Services.Api</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress> <UseIISExpress>false</UseIISExpress>
<IISExpressSSLPort /> <IISExpressSSLPort />
<IISExpressAnonymousAuthentication /> <IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication /> <IISExpressWindowsAuthentication />
@ -150,9 +150,9 @@
<WebProjectProperties> <WebProjectProperties>
<UseIIS>True</UseIIS> <UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort> <AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort> <DevelopmentServerPort>1306</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath> <DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:14615/</IISUrl> <IISUrl>http://localhost/NzbDrone.Services.Api</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication> <NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer> <UseCustomServer>False</UseCustomServer>
<CustomServerUrl> <CustomServerUrl>

Binary file not shown.

@ -1,188 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Web.Filters;
using NzbDrone.Web.Models;
using TvdbLib.Exceptions;
namespace NzbDrone.Web.Controllers
{
public class AddSeriesController : Controller
{
private readonly ConfigProvider _configProvider;
private readonly QualityProvider _qualityProvider;
private readonly RootDirProvider _rootFolderProvider;
private readonly SeriesProvider _seriesProvider;
private readonly JobProvider _jobProvider;
private readonly TvDbProvider _tvDbProvider;
private readonly DiskProvider _diskProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public AddSeriesController(RootDirProvider rootFolderProvider,
ConfigProvider configProvider,
QualityProvider qualityProvider, TvDbProvider tvDbProvider,
SeriesProvider seriesProvider, JobProvider jobProvider,
DiskProvider diskProvider)
{
_rootFolderProvider = rootFolderProvider;
_configProvider = configProvider;
_qualityProvider = qualityProvider;
_tvDbProvider = tvDbProvider;
_seriesProvider = seriesProvider;
_jobProvider = jobProvider;
_diskProvider = diskProvider;
}
public ActionResult Index()
{
return View();
}
public ActionResult AddNew()
{
ViewData["RootDirs"] = _rootFolderProvider.GetAll().Select(c => c.Path).OrderBy(e => e).ToList();
var defaultQuality = _configProvider.DefaultQualityProfile;
var qualityProfiles = _qualityProvider.All();
ViewData["qualityProfiles"] = new SelectList(
qualityProfiles,
"QualityProfileId",
"Name",
defaultQuality);
return View();
}
public ActionResult ExistingSeries()
{
var result = new ExistingSeriesModel();
var unmappedList = new List<String>();
foreach (var folder in _rootFolderProvider.GetAll())
{
unmappedList.AddRange(_rootFolderProvider.GetUnmappedFolders(folder.Path));
}
result.ExistingSeries = new List<Tuple<string, string, int>>();
foreach (var folder in unmappedList)
{
var foldername = new DirectoryInfo(folder).Name;
try
{
var tvdbResult = _tvDbProvider.SearchSeries(foldername).FirstOrDefault();
var title = String.Empty;
var seriesId = 0;
if (tvdbResult != null)
{
title = tvdbResult.SeriesName;
seriesId = tvdbResult.id;
}
result.ExistingSeries.Add(new Tuple<string, string, int>(folder, title, seriesId));
}
catch (Exception ex)
{
logger.WarnException("Failed to connect to TheTVDB to search for: " + foldername, ex);
return View();
}
}
var defaultQuality = Convert.ToInt32(_configProvider.DefaultQualityProfile);
result.Quality = new SelectList(_qualityProvider.All(), "QualityProfileId", "Name", defaultQuality);
return View(result);
}
[HttpPost]
[JsonErrorFilter]
public JsonResult AddNewSeries(string path, string seriesName, int seriesId, int qualityProfileId, string startDate)
{
if (string.IsNullOrWhiteSpace(path) || String.Equals(path, "null", StringComparison.InvariantCultureIgnoreCase))
return JsonNotificationResult.Error("Couldn't add " + seriesName, "You need a valid root folder");
path = Path.Combine(path, MediaFileProvider.CleanFilename(seriesName));
//Create the folder for the new series
//Use the created folder name when adding the series
path = _diskProvider.CreateDirectory(path);
return AddExistingSeries(path, seriesName, seriesId, qualityProfileId, startDate);
}
[HttpPost]
[JsonErrorFilter]
public JsonResult AddExistingSeries(string path, string seriesName, int seriesId, int qualityProfileId, string startDate)
{
if (seriesId == 0 || String.IsNullOrWhiteSpace(seriesName))
return JsonNotificationResult.Error("Add Existing series failed.", "Invalid Series information");
DateTime? date = null;
if (!String.IsNullOrWhiteSpace(startDate))
date = DateTime.Parse(startDate, null, DateTimeStyles.RoundtripKind);
_seriesProvider.AddSeries(seriesName, path, seriesId, qualityProfileId, date);
_jobProvider.QueueJob(typeof(ImportNewSeriesJob));
return JsonNotificationResult.Info(seriesName, "Was added successfully");
}
[HttpGet]
[JsonErrorFilter]
public JsonResult LookupSeries(string term)
{
return JsonNotificationResult.Info("Lookup Failed", "Unknown error while connecting to TheTVDB");
}
public ActionResult RootList()
{
IEnumerable<String> rootDir = _rootFolderProvider.GetAll().Select(c => c.Path).OrderBy(e => e);
return PartialView("RootList", rootDir);
}
public ActionResult RootDir()
{
return PartialView("RootDir");
}
[HttpPost]
[JsonErrorFilter]
public JsonResult SaveRootDir(string path)
{
if (String.IsNullOrWhiteSpace(path))
JsonNotificationResult.Error("Can't add root folder", "Path can not be empty");
_rootFolderProvider.Add(new RootDir { Path = path });
return JsonNotificationResult.Info("Root Folder saved", "Root folder saved successfully.");
}
[JsonErrorFilter]
public JsonResult DeleteRootDir(string path)
{
var id = _rootFolderProvider.GetAll().Where(c => c.Path == path).First().Id;
_rootFolderProvider.Remove(id);
return null;
}
}
}

@ -1,6 +1,7 @@
using System.Web.Mvc; using System.Web.Mvc;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.RootFolders;
using NzbDrone.Web.Models; using NzbDrone.Web.Models;
namespace NzbDrone.Web.Controllers namespace NzbDrone.Web.Controllers
@ -8,12 +9,12 @@ namespace NzbDrone.Web.Controllers
public class SharedController : Controller public class SharedController : Controller
{ {
private readonly EnvironmentProvider _environmentProvider; private readonly EnvironmentProvider _environmentProvider;
private readonly RootDirProvider _rootDirProvider; private readonly RootFolderService _rootFolderService;
public SharedController(EnvironmentProvider environmentProvider, RootDirProvider rootDirProvider) public SharedController(EnvironmentProvider environmentProvider, RootFolderService rootFolderService)
{ {
_environmentProvider = environmentProvider; _environmentProvider = environmentProvider;
_rootDirProvider = rootDirProvider; _rootFolderService = rootFolderService;
} }
public ActionResult Index() public ActionResult Index()
@ -32,7 +33,7 @@ namespace NzbDrone.Web.Controllers
[OutputCache(Duration = 600)] [OutputCache(Duration = 600)]
public ActionResult FreeSpace() public ActionResult FreeSpace()
{ {
var rootDirs = _rootDirProvider.FreeSpaceOnDrives(); var rootDirs = _rootFolderService.FreeSpaceOnDrives();
return PartialView(rootDirs); return PartialView(rootDirs);
} }

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<Eloquera xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cache IndexCacheSize="10000"
WriteThru="false"
IndexCleanerPeriod="100"
IndexCleanerBatchSize="100"
CleanerPeriod="100"
CleanerBatchSize="2000"
CommitSequenceMaxLength="2000"
ShallowReadThreshold="1000"
ShallowReadAhead="1000"
ReadAhead="20"
SaturationThreshold="10000"
MemoryFootPrint="60"/>
<MemoryManager Mode="1" />
<Server ServerPort="43962"
Trace="true"
InMemoryAllowed="true"
Secure="false"
AllowUsers=""
AllowGroups="Everyone"
SNMPAddress="net.tcp://localhost:8523/SNMP"
AutoRecovery="false"
NotificationsEnabled="false"
VarSectorSize="40"
IndexNodeSize="512"/>
<SmartRuntime Smart="true"
TypeUpdateAllowed="true"/>
<UserLogin Enabled="false"
PasswordHash="l+on1aCwDrcZ5bGlv+fyyIlYkbuFIOxZFlFwIGKlms0CCwoGn9TZvM0E3Uksjwx64+/yv8nsaUajWLz1kyKG7A==" />
</Eloquera>

@ -17,7 +17,7 @@
<MvcBuildViews>true</MvcBuildViews> <MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable> <EnableUpdateable>false</EnableUpdateable>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress> <UseIISExpress>true</UseIISExpress>
<FileUpgradeFlags> <FileUpgradeFlags>
</FileUpgradeFlags> </FileUpgradeFlags>
<UpgradeBackupLocation> <UpgradeBackupLocation>
@ -26,6 +26,10 @@
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages> <RestorePackages>true</RestorePackages>
<JSLintSkip>\_backboneApp\JsLibraries\|\Scripts\</JSLintSkip> <JSLintSkip>\_backboneApp\JsLibraries\|\Scripts\</JSLintSkip>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -88,6 +92,17 @@
<Reference Include="Dynamic"> <Reference Include="Dynamic">
<HintPath>..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll</HintPath> <HintPath>..\packages\DynamicQuery.1.0\lib\35\Dynamic.dll</HintPath>
</Reference> </Reference>
<Reference Include="Eloquera.Client, Version=5.0.0.0, Culture=neutral, PublicKeyToken=170cadcdba67be6c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\EloqueraDB.5.0.0\lib\net40\Eloquera.Client.dll</HintPath>
</Reference>
<Reference Include="Eloquera.Common, Version=5.0.0.0, Culture=neutral, PublicKeyToken=170cadcdba67be6c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\EloqueraDB.5.0.0\lib\net40\Eloquera.Common.dll</HintPath>
</Reference>
<Reference Include="Eloquera.Server">
<HintPath>..\packages\EloqueraDB.5.0.0\lib\net40\Eloquera.Server.exe</HintPath>
</Reference>
<Reference Include="LowercaseRoutesMVC"> <Reference Include="LowercaseRoutesMVC">
<HintPath>..\packages\LowercaseRoutesMVC.1.0.3\lib\LowercaseRoutesMVC.dll</HintPath> <HintPath>..\packages\LowercaseRoutesMVC.1.0.3\lib\LowercaseRoutesMVC.dll</HintPath>
</Reference> </Reference>
@ -341,7 +356,6 @@
<Compile Include="Controllers\HealthController.cs" /> <Compile Include="Controllers\HealthController.cs" />
<Compile Include="Controllers\HistoryController.cs" /> <Compile Include="Controllers\HistoryController.cs" />
<Compile Include="Controllers\LogController.cs" /> <Compile Include="Controllers\LogController.cs" />
<Compile Include="Controllers\AddSeriesController.cs" />
<Compile Include="Controllers\MissingController.cs" /> <Compile Include="Controllers\MissingController.cs" />
<Compile Include="Controllers\NotificationController.cs" /> <Compile Include="Controllers\NotificationController.cs" />
<Compile Include="Controllers\SeriesController.cs" /> <Compile Include="Controllers\SeriesController.cs" />
@ -401,6 +415,7 @@
<Content Include="_backboneApp\Shared\NotificationView.js" /> <Content Include="_backboneApp\Shared\NotificationView.js" />
<Content Include="_backboneApp\Shared\SpinnerTemplate.html" /> <Content Include="_backboneApp\Shared\SpinnerTemplate.html" />
<Content Include="_backboneApp\Shared\SpinnerView.js" /> <Content Include="_backboneApp\Shared\SpinnerView.js" />
<Content Include="Eloquera.config" />
<None Include="_backboneApp\JsLibraries\jquery-1.8.2.intellisense.js" /> <None Include="_backboneApp\JsLibraries\jquery-1.8.2.intellisense.js" />
<Content Include="_backboneApp\JsLibraries\backbone.collectionbinder.js" /> <Content Include="_backboneApp\JsLibraries\backbone.collectionbinder.js" />
<Content Include="_backboneApp\JsLibraries\backbone.debug.js" /> <Content Include="_backboneApp\JsLibraries\backbone.debug.js" />
@ -606,8 +621,6 @@
<Content Include="Scripts\jquery.watermark.min.js" /> <Content Include="Scripts\jquery.watermark.min.js" />
<Content Include="Scripts\underscore.js" /> <Content Include="Scripts\underscore.js" />
<Content Include="Scripts\underscore.min.js" /> <Content Include="Scripts\underscore.min.js" />
<Content Include="Views\AddSeries\Index.cshtml" />
<Content Include="Views\AddSeries\AddNew.cshtml" />
<Content Include="Web.config"> <Content Include="Web.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Content> </Content>
@ -624,7 +637,6 @@
<Content Include="Views\Settings\Naming.cshtml" /> <Content Include="Views\Settings\Naming.cshtml" />
<Content Include="Views\Settings\Notifications.cshtml" /> <Content Include="Views\Settings\Notifications.cshtml" />
<Content Include="Views\Settings\Quality.cshtml" /> <Content Include="Views\Settings\Quality.cshtml" />
<Content Include="Views\AddSeries\RootDir.cshtml" />
<Content Include="Views\Shared\_Layout.cshtml" /> <Content Include="Views\Shared\_Layout.cshtml" />
<Content Include="Views\Shared\Footer.cshtml" /> <Content Include="Views\Shared\Footer.cshtml" />
<Content Include="Views\_ViewStart.cshtml" /> <Content Include="Views\_ViewStart.cshtml" />
@ -666,15 +678,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Missing\Index.cshtml" /> <Content Include="Views\Missing\Index.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\AddSeries\ExistingSeries.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\System\PendingProcessing.cshtml" /> <Content Include="Views\System\PendingProcessing.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\AddSeries\RootList.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\Settings\System.cshtml" /> <Content Include="Views\Settings\System.cshtml" />
</ItemGroup> </ItemGroup>

@ -1,13 +0,0 @@
@using System.Collections
@{ Layout = null; }
<div>
<div>
<input id="newSeriesLookup" class="seriesLookup" type="text" style="width: 400px" />
@Html.Hidden("newSeriesId", 0, new { @class = "seriesId" })
</div>
@Html.DropDownList("newSeriesPath", new SelectList((IList)ViewData["RootDirs"]), new { style = "width: 406px; margin-left: 0px;" })
@Html.DropDownList("qualityList", (SelectList)ViewData["QualityProfiles"], new { @class = "qualitySelector" })
@Html.TextBox("newStartDate", "", new { type = "date", @class = "jQuery-datepicker start-date", placeholder = "Custom Start Date", title = "Only download episodes that aired after the specified date" })
<button id="saveNewSeries">
Add</button>
</div>

@ -1,44 +0,0 @@
@using System.Collections
@using NzbDrone.Web.Models
@using System.Web.Mvc.Html
@model ExistingSeriesModel
@{
Layout = null;
}
@if (Model == null)
{
<h2 style="color: tomato">
Error searching TheTVDB, please try again later.
</h2>
}
else if (!Model.ExistingSeries.Any())
{
<h2 style="color: tomato">
No series available. Try adding a new Root Folder.
</h2>
}
else
{
@Html.DropDownList(Guid.NewGuid().ToString(), Model.Quality, new { @class = "qualitySelector masterQualitySelector" })
@Html.TextBox(Guid.NewGuid().ToString(), "", new { type="date", @class = "jQuery-datepicker start-date-master", placeholder = "Custom Start Date", title = "Only download episodes that aired after the specified date" })
foreach (var series in Model.ExistingSeries)
{
<div class="existingSeries">
<span class="seriesPathValue">
<label>@series.Item1</label>
</span>
<div class="existingSeriesContainer">
<input class="seriesLookup" type="text" style="width: 400px;" value="@series.Item2" placeholder="Series Title" />
@Html.Hidden("seriesId", series.Item3, new { @class = "seriesId" })
@Html.DropDownList(Guid.NewGuid().ToString(), Model.Quality, new { @class = "qualitySelector" })
@Html.TextBox(Guid.NewGuid().ToString(), "", new { type="date", @class = "jQuery-datepicker start-date", placeholder = "Custom Start Date", title = "Only download episodes that aired after the specified date" })
<button class="addExistingButton">
Add</button>
</div>
</div>
}
}

@ -1,92 +0,0 @@
@using NzbDrone.Web.Helpers
@{ViewBag.Title = "Add Series";}
@section HeaderContent
{
<style>
.existingSeries
{
border-color: #f2f2f2;
border-width: 1px;
border-style: solid;
background-color: #f2f2f2;
margin: 0px 10px 10px 0px;
padding: 5px;
display: block;
}
.masterQualitySelector
{
left: 415px;
position: relative;
}
.start-date-master {
left: 430px;
position: relative;
}
.seriesPathValue
{
display: block;
}
.existingSeriesContainer
{
position: relative;
}
.ui-autocomplete.seriesLookupResults {
max-height: 180px;
overflow-y: auto;
/* prevent horizontal scrollbar */
overflow-x: hidden;
}
.tvDbLink {
float: right;
}
.tvDbLink:hover {
cursor: pointer;
text-decoration: underline;
}
input[type=date].start-date {
margin-left: 10px;
}
</style>
}
@{Html.RenderAction("RootDir");}
<div class="jquery-accordion jquery-accordion-collapse-all" id="addSeriesAccordion">
<h3>
<a href="#">Add New Series</a></h3>
<div id="addNewSeries">
@{ Html.RenderAction("AddNew", "AddSeries"); }
</div>
<h3>
<a href="#">Add Series Already on Disk</a></h3>
<div id="existingSeries">
<img src="../../Content/Images/ajax-loader.gif" />
@Ajax.RenderAction(
"ExistingSeries",
"AddSeries",
null,
new AjaxOptions { UpdateTargetId = "existingSeries", InsertionMode = InsertionMode.Replace }
)
</div>
</div>
@section Scripts
{
@Html.IncludeScript("NzbDrone/addSeries.js")
<script type="text/javascript">
$(document).on('click', '.tvDbLink', function (event) {
var url = $(this).attr('rel');
window.open(url, 'thetvdb');
event.preventDefault();
});
</script>
}

@ -1,10 +0,0 @@
<h2>
Manage Root Folders
</h2>
<span>
<input id="rootDirInput" class="folderLookup" type="text" style="width: 400px" />
<button id="saveDir">
Add</button>
</span><span id="rootDirs">
@{Html.RenderAction("RootList");}
</span>

@ -1,16 +0,0 @@
@model IEnumerable<String>
@if (!Model.Any())
{
<div class="actionButton delete">
<span>You have no root folders added.</span>
</div>
}
@foreach (var root in Model)
{
<div class="actionButton delete">
<div class="delete-root" data-path="@root">
<i class="icon-remove icon-large"></i>
</div>
<span>@root</span>
</div>
}

@ -5,16 +5,16 @@
{ {
@Html.IncludeCss("Settings.css") @Html.IncludeCss("Settings.css")
} }
<style> <style>
.progressbar { .progressbar {
position:relative; position: relative;
width: 125px; width: 125px;
height: 20px; height: 20px;
background-color: transparent; background-color: transparent;
border: 1px solid #065EFE; border: 1px solid #065EFE;
margin: 2px; margin: 2px;
} }
.progressbar-text { .progressbar-text {
position: absolute; position: absolute;
display: block; display: block;
@ -27,33 +27,29 @@
border: 1px solid #065EFE; border: 1px solid #065EFE;
} }
.ui-progressbar-value .progressbar-text { .ui-progressbar-value .progressbar-text {
position: relative; position: relative;
font-weight: normal; font-weight: normal;
color: white; color: white;
} }
/* Set the row height so it won't resize when the progress bar is created */ /* Set the row height so it won't resize when the progress bar is created */
.seriesTable tr .seriesTable tr {
{
height: 28px; height: 28px;
} }
.commandsColumn, .statusColumn .commandsColumn, .statusColumn {
{
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
} }
.ui-dialog-buttonpane .ui-dialog-buttonset .ui-delete-button .ui-dialog-buttonpane .ui-dialog-buttonset .ui-delete-button {
{
margin-right: 445px; margin-right: 445px;
} }
</style> </style>
@section ActionMenu{ @section ActionMenu{
<ul class="sub-menu"> <ul class="sub-menu">
<li>@Html.ActionLink("Add Series", "Index", "AddSeries", null, new { Title = "Add a new or existing series" })</li> <li><a href="/v2#series/add">Add Series</a></li>
<li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null, null, new { Title = "Check for newly released downloads" })</li> <li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null, null, new { Title = "Check for newly released downloads" })</li>
<li>@Html.ActionLink("Series Editor", "Editor", "Series", null, new { Title = "Edit multiple series" })</li> <li>@Html.ActionLink("Series Editor", "Editor", "Series", null, new { Title = "Edit multiple series" })</li>
</ul> </ul>
@ -61,7 +57,7 @@
<table id="seriesGrid" class="dataTablesGrid hidden-grid"> <table id="seriesGrid" class="dataTablesGrid hidden-grid">
<thead> <thead>
<tr> <tr>
<th style="width: 10px">Status</th> <th style="width: 10px">Status</th>
<th style="width: auto">Title</th> <th style="width: auto">Title</th>
@ -70,7 +66,7 @@
<th style="width: 100px">Network</th> <th style="width: 100px">Network</th>
<th style="width: 140px">Next Airing</th> <th style="width: 140px">Next Airing</th>
<th style="width: 100px">Episodes</th> <th style="width: 100px">Episodes</th>
@*Commands Column*@ @*Commands Column*@
<th style="width: 80px"></th> <th style="width: 80px"></th>
</tr> </tr>
@ -96,7 +92,7 @@
@Html.IncludeScript("NzbDrone/series.js") @Html.IncludeScript("NzbDrone/series.js")
<script type="text/javascript"> <script type="text/javascript">
var pauseImage = '<i class="icon-pause grid-icon" title="Not Monitored"></i>'; var pauseImage = '<i class="icon-pause grid-icon" title="Not Monitored"></i>';
var stopImage = '<i class="icon-stop grid-icon" title="Ended"></i>'; var stopImage = '<i class="icon-stop grid-icon" title="Ended"></i>';
var playImage = '<i class="icon-play grid-icon" title="Continuing"></i>'; var playImage = '<i class="icon-play grid-icon" title="Continuing"></i>';
@ -115,56 +111,60 @@
"bInfo": false, "bInfo": false,
"bAutoWidth": false, "bAutoWidth": false,
"bStateSave": true, "bStateSave": true,
"iCookieDuration": 60 * 60 *24 * 365, //1 year "iCookieDuration": 60 * 60 * 24 * 365, //1 year
"aoColumns": [ "aoColumns": [
{ sWidth: '70px', {
"sClass": "statusColumn", sWidth: '70px',
"mDataProp": function (source, type, val) { "sClass": "statusColumn",
// 'display' and 'filter' use our fancy naming "mDataProp": function (source, type, val) {
if (type === 'display' || type === 'filter') { // 'display' and 'filter' use our fancy naming
var monitored = source["Monitored"]; if (type === 'display' || type === 'filter') {
var status = source["Status"]; var monitored = source["Monitored"];
var status = source["Status"];
if (!monitored) {
return pauseImage; if (!monitored) {
} return pauseImage;
}
else {
if (status === "Ended"){ else {
return stopImage; if (status === "Ended") {
} return stopImage;
else {
return playImage;
}
}
}
// 'sort' and 'type' both just use the raw data
return source["Status"];
} }
else {
return playImage;
}
}
}
// 'sort' and 'type' both just use the raw data
return source["Status"];
}
}, //Status }, //Status
{ sWidth: 'auto', "mDataProp": function (source, type, val) { {
// 'display' and 'filter' use our fancy naming sWidth: 'auto', "mDataProp": function (source, type, val) {
if (type === 'display' || type === 'filter') { // 'display' and 'filter' use our fancy naming
return "<a href='/series/details/" + source["SeriesId"] + "'>" + source["Title"] + "</a>"; if (type === 'display' || type === 'filter') {
} return "<a href='/series/details/" + source["SeriesId"] + "'>" + source["Title"] + "</a>";
// 'sort' and 'type' both just use the raw data }
return source["TitleSorter"]; // 'sort' and 'type' both just use the raw data
} return source["TitleSorter"];
}
}, //Title }, //Title
{ sWidth: '100px', "mDataProp": "SeasonsCount" }, //Seasons { sWidth: '100px', "mDataProp": "SeasonsCount" }, //Seasons
{ sWidth: '100px', "mDataProp": "QualityProfileName", sClass: "qualityColumn" }, //Quality { sWidth: '100px', "mDataProp": "QualityProfileName", sClass: "qualityColumn" }, //Quality
{ sWidth: '120px', "mDataProp": "Network" }, //Network { sWidth: '120px', "mDataProp": "Network" }, //Network
{ sWidth: '120px', "mDataProp": function (source, type, val) { {
// 'display' and 'filter' use our fancy naming sWidth: '120px', "mDataProp": function (source, type, val) {
if (type === 'display' || type === 'filter') { // 'display' and 'filter' use our fancy naming
return source["NextAiring"]; if (type === 'display' || type === 'filter') {
} return source["NextAiring"];
// 'sort' and 'type' both just use the raw data }
return source["NextAiringSorter"]; // 'sort' and 'type' both just use the raw data
} return source["NextAiringSorter"];
}
}, //Next Airing }, //Next Airing
{ sWidth: '140px', "mDataProp": "Episodes", "bSortable": false, "fnRender": function (row) { {
sWidth: '140px', "mDataProp": "Episodes", "bSortable": false, "fnRender": function (row) {
var progress = 100; var progress = 100;
if (row.aData["EpisodeCount"] > 0) if (row.aData["EpisodeCount"] > 0)
progress = row.aData["EpisodeFileCount"] / row.aData["EpisodeCount"] * 100; progress = row.aData["EpisodeFileCount"] / row.aData["EpisodeCount"] * 100;
@ -188,16 +188,17 @@
"<i class='icon-remove deleteButton gridAction' title = 'Delete Series' data-series-id='" + row.aData["SeriesId"] + "' data-series-title='" + row.aData["Title"] + "'></i>"; "<i class='icon-remove deleteButton gridAction' title = 'Delete Series' data-series-id='" + row.aData["SeriesId"] + "' data-series-title='" + row.aData["Title"] + "'></i>";
} }
}, //Commands }, //Commands
{ sWidth: '60px', "mDataProp": "Details", "bSortable": false, "bVisible": false, "fnRender": function (row) { {
var result = "<b>Airs Day of Week: </b>" + row.aData["AirsDayOfWeek"] + "<br/>" + sWidth: '60px', "mDataProp": "Details", "bSortable": false, "bVisible": false, "fnRender": function (row) {
"<b>Air Time: </b>" + row.aData["AirTime"] + "<br/>" + var result = "<b>Airs Day of Week: </b>" + row.aData["AirsDayOfWeek"] + "<br/>" +
"<b>Overview: </b>" + row.aData["Overview"] + "<br/>"; "<b>Air Time: </b>" + row.aData["AirTime"] + "<br/>" +
"<b>Overview: </b>" + row.aData["Overview"] + "<br/>";
return result; return result;
} }
} //Details } //Details
], ],
"aaSorting": [[1, 'asc']], "aaSorting": [[1, 'asc']],
"fnCreatedRow": function( nRow, aData, iDataIndex ) { "fnCreatedRow": function (nRow, aData, iDataIndex) {
$(nRow).attr('data-series-id', aData["SeriesId"].toString()); $(nRow).attr('data-series-id', aData["SeriesId"].toString());
}, },
"oLanguage": { "oLanguage": {
@ -205,25 +206,25 @@
} }
}); });
}); });
//After we delete a series do this... //After we delete a series do this...
function afterDelete(seriesId) { function afterDelete(seriesId) {
var row = $('[data-series-id="' + seriesId + '"]'); var row = $('[data-series-id="' + seriesId + '"]');
if ($(row).hasClass('details-opened')) { if ($(row).hasClass('details-opened')) {
var nextRow = $(row).next('tr'); var nextRow = $(row).next('tr');
if($(nextRow).children('.Details').length > 0) { if ($(nextRow).children('.Details').length > 0) {
$(nextRow).hide(); $(nextRow).hide();
} }
} }
$(row).hide(); $(row).hide();
} }
//After we save do this... //After we save do this...
function afterSave() { function afterSave() {
updateStatus(); updateStatus();
//Update Quality //Update Quality
var seriesId = $('#SeriesId').val(); var seriesId = $('#SeriesId').val();
var quality = $('#QualityProfileId :selected').text(); var quality = $('#QualityProfileId :selected').text();

@ -1,2 +1 @@
 <div class="infoBox">It looks like you haven't added any series to NzbDrone. <a href="/v2#series/add">Click here to add one.</a></div>
<div class="infoBox">It looks like you haven't added any series to NzbDrone. @Html.ActionLink("Click here to add one.", "Index", "AddSeries")</div>

@ -12,6 +12,7 @@
<package id="DataTables.Mvc.Core" version="0.1.0.85" /> <package id="DataTables.Mvc.Core" version="0.1.0.85" />
<package id="DotlessClientOnly" version="1.3.1.0" targetFramework="net40" /> <package id="DotlessClientOnly" version="1.3.1.0" targetFramework="net40" />
<package id="DynamicQuery" version="1.0" /> <package id="DynamicQuery" version="1.0" />
<package id="EloqueraDB" version="5.0.0" targetFramework="net40" />
<package id="FontAwesome" version="2.0.2" targetFramework="net40" /> <package id="FontAwesome" version="2.0.2" targetFramework="net40" />
<package id="jQuery" version="1.8.2" targetFramework="net40" /> <package id="jQuery" version="1.8.2" targetFramework="net40" />
<package id="jQuery.Ajax.Unobtrusive" version="2.0.20710.0" targetFramework="net40" /> <package id="jQuery.Ajax.Unobtrusive" version="2.0.20710.0" targetFramework="net40" />

@ -1,7 +1,8 @@
<SolutionConfiguration> <SolutionConfiguration>
<FileVersion>1</FileVersion> <FileVersion>1</FileVersion>
<AutoEnableOnStartup>False</AutoEnableOnStartup> <AutoEnableOnStartup>True</AutoEnableOnStartup>
<AllowParallelTestExecution>true</AllowParallelTestExecution> <AllowParallelTestExecution>true</AllowParallelTestExecution>
<AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit> <FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>
<FrameworkUtilisationTypeForGallio>Disabled</FrameworkUtilisationTypeForGallio> <FrameworkUtilisationTypeForGallio>Disabled</FrameworkUtilisationTypeForGallio>
<FrameworkUtilisationTypeForMSpec>Disabled</FrameworkUtilisationTypeForMSpec> <FrameworkUtilisationTypeForMSpec>Disabled</FrameworkUtilisationTypeForMSpec>

Loading…
Cancel
Save