Manage multiple Tv Root Folders in Settings/General.

Start of AddExisting.
This commit is contained in:
Mark McDowall 2011-03-08 23:40:48 -08:00
parent 2e9dd7f1ff
commit 2871723bfe
21 changed files with 381 additions and 90 deletions

View File

@ -66,25 +66,25 @@ namespace NzbDrone.Core.Test
//Assert.AreEqual(title, result, postTitle); //Assert.AreEqual(title, result, postTitle);
} }
[Test] //[Test]
public void get_unmapped() //public void get_unmapped()
{ //{
//Setup // //Setup
var kernel = new MockingKernel(); // var kernel = new MockingKernel();
kernel.Bind<ISeriesProvider>().To<SeriesProvider>(); // kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
kernel.Bind<IDiskProvider>().ToConstant(MockLib.GetStandardDisk(0, 0)); // kernel.Bind<IDiskProvider>().ToConstant(MockLib.GetStandardDisk(0, 0));
kernel.Bind<IConfigProvider>().ToConstant(MockLib.StandardConfig); // kernel.Bind<IConfigProvider>().ToConstant(MockLib.StandardConfig);
var seriesController = kernel.Get<ISeriesProvider>(); // var seriesController = kernel.Get<ISeriesProvider>();
//Act // //Act
var unmappedFolder = seriesController.GetUnmappedFolders(); // var unmappedFolder = seriesController.GetUnmappedFolders();
//Assert // //Assert
Assert.AreElementsEqualIgnoringOrder(MockLib.StandardSeries, unmappedFolder.Values); // Assert.AreElementsEqualIgnoringOrder(MockLib.StandardSeries, unmappedFolder.Values);
} //}
} }

View File

@ -62,6 +62,7 @@ namespace NzbDrone.Core
_kernel.Bind<IHttpProvider>().To<HttpProvider>(); _kernel.Bind<IHttpProvider>().To<HttpProvider>();
_kernel.Bind<IHistoryProvider>().To<HistoryProvider>(); _kernel.Bind<IHistoryProvider>().To<HistoryProvider>();
_kernel.Bind<IQualityProvider>().To<QualityProvider>(); _kernel.Bind<IQualityProvider>().To<QualityProvider>();
_kernel.Bind<IRootDirProvider>().To<RootDirProvider>();
_kernel.Bind<IExtenalNotificationProvider>().To<ExternalNotificationProvider>(); _kernel.Bind<IExtenalNotificationProvider>().To<ExternalNotificationProvider>();
_kernel.Bind<IXbmcProvider>().To<XbmcProvider>(); _kernel.Bind<IXbmcProvider>().To<XbmcProvider>();
_kernel.Bind<IConfigProvider>().To<ConfigProvider>().InSingletonScope(); _kernel.Bind<IConfigProvider>().To<ConfigProvider>().InSingletonScope();

View File

@ -183,6 +183,7 @@
<Compile Include="Providers\IPostProcessingProvider.cs" /> <Compile Include="Providers\IPostProcessingProvider.cs" />
<Compile Include="Providers\IQualityProvider.cs" /> <Compile Include="Providers\IQualityProvider.cs" />
<Compile Include="Providers\IRenameProvider.cs" /> <Compile Include="Providers\IRenameProvider.cs" />
<Compile Include="Providers\IRootDirProvider.cs" />
<Compile Include="Providers\IRssSyncProvider.cs" /> <Compile Include="Providers\IRssSyncProvider.cs" />
<Compile Include="Providers\IRssProvider.cs" /> <Compile Include="Providers\IRssProvider.cs" />
<Compile Include="Providers\ITimerProvider.cs" /> <Compile Include="Providers\ITimerProvider.cs" />
@ -190,6 +191,7 @@
<Compile Include="Providers\PostProcessingProvider.cs" /> <Compile Include="Providers\PostProcessingProvider.cs" />
<Compile Include="Providers\QualityProvider.cs" /> <Compile Include="Providers\QualityProvider.cs" />
<Compile Include="Providers\RenameProvider.cs" /> <Compile Include="Providers\RenameProvider.cs" />
<Compile Include="Providers\RootDirProvider.cs" />
<Compile Include="Providers\RssSyncProvider.cs" /> <Compile Include="Providers\RssSyncProvider.cs" />
<Compile Include="Providers\RssProvider.cs" /> <Compile Include="Providers\RssProvider.cs" />
<Compile Include="Providers\TimerProvider.cs" /> <Compile Include="Providers\TimerProvider.cs" />
@ -229,6 +231,7 @@
<Compile Include="Repository\Indexer.cs" /> <Compile Include="Repository\Indexer.cs" />
<Compile Include="Repository\Config.cs" /> <Compile Include="Repository\Config.cs" />
<Compile Include="Repository\Quality\QualityProfile.cs" /> <Compile Include="Repository\Quality\QualityProfile.cs" />
<Compile Include="Repository\RootDir.cs" />
<Compile Include="Repository\Season.cs" /> <Compile Include="Repository\Season.cs" />
<Compile Include="Repository\Quality\QualityTypes.cs" /> <Compile Include="Repository\Quality\QualityTypes.cs" />
<Compile Include="Repository\Series.cs" /> <Compile Include="Repository\Series.cs" />

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers
{
public interface IRootDirProvider
{
List<RootDir> GetAll();
void Add(RootDir rootDir);
void Remove(int rootDirId);
void Update(RootDir rootDir);
}
}

View File

@ -18,13 +18,12 @@ namespace NzbDrone.Core.Providers
/// <param name="id">The TVDB ID of the series</param> /// <param name="id">The TVDB ID of the series</param>
/// <returns>Whether or not the show is monitored</returns> /// <returns>Whether or not the show is monitored</returns>
bool IsMonitored(long id); bool IsMonitored(long id);
TvdbSeries MapPathToSeries(string path); TvdbSeries MapPathToSeries(string path);
void AddSeries(string path, TvdbSeries series); void AddSeries(string path, TvdbSeries series);
Dictionary<Guid, String> GetUnmappedFolders();
Series FindSeries(string cleanTitle); Series FindSeries(string cleanTitle);
bool QualityWanted(int seriesId, QualityTypes quality); bool QualityWanted(int seriesId, QualityTypes quality);
void UpdateSeries(Series series); void UpdateSeries(Series series);
void DeleteSeries(int seriesId); void DeleteSeries(int seriesId);
bool SeriesPathExists(string cleanPath);
} }
} }

View File

@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
public interface ISyncProvider public interface ISyncProvider
{ {
void SyncUnmappedFolders(); bool BeginSyncUnmappedFolders(List<string> paths);
void BeginSyncUnmappedFolders(); List<String> GetUnmappedFolders(string path);
} }
} }

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Providers
{
public class RootDirProvider : IRootDirProvider
{
private readonly IRepository _sonioRepo;
public RootDirProvider(IRepository sonicRepo)
{
_sonioRepo = sonicRepo;
}
#region IRootDirProvider
public List<RootDir> GetAll()
{
return _sonioRepo.All<RootDir>().ToList();
}
public void Add(RootDir rootDir)
{
_sonioRepo.Add(rootDir);
}
public void Remove(int rootDirId)
{
_sonioRepo.Delete<RootDir>(rootDirId);
}
public void Update(RootDir rootDir)
{
_sonioRepo.Update(rootDir);
}
#endregion
}
}

View File

@ -65,26 +65,6 @@ namespace NzbDrone.Core.Providers
return profile.Allowed.Contains(quality); return profile.Allowed.Contains(quality);
} }
public Dictionary<Guid, String> GetUnmappedFolders()
{
Logger.Debug("Generating list of unmapped folders");
if (String.IsNullOrEmpty(_config.SeriesRoot))
throw new InvalidOperationException("TV Series folder is not configured yet.");
var results = new Dictionary<Guid, String>();
foreach (string seriesFolder in _diskProvider.GetDirectories(_config.SeriesRoot))
{
var cleanPath = Parser.NormalizePath(new DirectoryInfo(seriesFolder).FullName);
if (!_sonioRepo.Exists<Series>(s => s.Path == cleanPath))
{
results.Add(Guid.NewGuid(), cleanPath);
}
}
Logger.Debug("{0} unmapped folders detected.", results.Count);
return results;
}
public TvdbSeries MapPathToSeries(string path) public TvdbSeries MapPathToSeries(string path)
{ {
var seriesPath = new DirectoryInfo(path); var seriesPath = new DirectoryInfo(path);
@ -149,6 +129,14 @@ namespace NzbDrone.Core.Providers
_sonioRepo.Delete<Series>(seriesId); _sonioRepo.Delete<Series>(seriesId);
} }
public bool SeriesPathExists(string cleanPath)
{
if (_sonioRepo.Exists<Series>(s => s.Path == cleanPath))
return true;
return false;
}
#endregion #endregion
#region Static Helpers #region Static Helpers

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using NLog; using NLog;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
@ -16,21 +15,54 @@ namespace NzbDrone.Core.Providers
private readonly IEpisodeProvider _episodeProvider; private readonly IEpisodeProvider _episodeProvider;
private readonly IMediaFileProvider _mediaFileProvider; private readonly IMediaFileProvider _mediaFileProvider;
private readonly INotificationProvider _notificationProvider; private readonly INotificationProvider _notificationProvider;
private readonly IDiskProvider _diskProvider;
private ProgressNotification _seriesSyncNotification; private ProgressNotification _seriesSyncNotification;
private Thread _seriesSyncThread; private Thread _seriesSyncThread;
private List<string> _syncList;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public SyncProvider(ISeriesProvider seriesProvider, IEpisodeProvider episodeProvider, IMediaFileProvider mediaFileProvider, INotificationProvider notificationProvider) public SyncProvider(ISeriesProvider seriesProvider, IEpisodeProvider episodeProvider,
IMediaFileProvider mediaFileProvider, INotificationProvider notificationProvider,
IDiskProvider diskProvider)
{ {
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_notificationProvider = notificationProvider; _notificationProvider = notificationProvider;
_diskProvider = diskProvider;
} }
public void BeginSyncUnmappedFolders() #region ISyncProvider Members
public List<String> GetUnmappedFolders(string path)
{
Logger.Debug("Generating list of unmapped folders");
if (String.IsNullOrEmpty(path))
throw new InvalidOperationException("Invalid path provided");
if (!_diskProvider.FolderExists(path))
{
Logger.Debug("Path supplied does not exist: {0}", path);
}
var results = new List<String>();
foreach (string seriesFolder in _diskProvider.GetDirectories(path))
{
var cleanPath = Parser.NormalizePath(new DirectoryInfo(seriesFolder).FullName);
if (!_seriesProvider.SeriesPathExists(cleanPath))
results.Add(cleanPath);
}
Logger.Debug("{0} unmapped folders detected.", results.Count);
return results;
}
#endregion
public bool BeginSyncUnmappedFolders(List<string> paths)
{ {
Logger.Debug("User has request series folder scan"); Logger.Debug("User has request series folder scan");
if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive) if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive)
@ -42,15 +74,22 @@ namespace NzbDrone.Core.Providers
Priority = ThreadPriority.Lowest Priority = ThreadPriority.Lowest
}; };
_syncList = paths;
_seriesSyncThread.Start(); _seriesSyncThread.Start();
} }
else else
{ {
Logger.Warn("Series folder scan already in progress. Ignoring request."); Logger.Warn("Series folder scan already in progress. Ignoring request.");
}
//return false if sync was already running, then we can tell the user to try again later
return false;
} }
public void SyncUnmappedFolders() //return true if sync has started
return true;
}
private void SyncUnmappedFolders()
{ {
Logger.Info("Starting Series folder scan"); Logger.Info("Starting Series folder scan");
@ -60,15 +99,20 @@ namespace NzbDrone.Core.Providers
{ {
_notificationProvider.Register(_seriesSyncNotification); _notificationProvider.Register(_seriesSyncNotification);
_seriesSyncNotification.CurrentStatus = "Analysing Folder"; _seriesSyncNotification.CurrentStatus = "Analysing Folder";
var unmappedFolders = _seriesProvider.GetUnmappedFolders(); _seriesSyncNotification.ProgressMax = _syncList.Count;
_seriesSyncNotification.ProgressMax = unmappedFolders.Count;
foreach (string seriesFolder in unmappedFolders.Values) foreach (var seriesFolder in _syncList)
{ {
try try
{ {
_seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(new DirectoryInfo(seriesFolder).Name)); _seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(new DirectoryInfo(seriesFolder).Name));
if (_seriesProvider.SeriesPathExists(Parser.NormalizePath(seriesFolder)))
{
Logger.Debug("Folder '{0}' is mapped in the database. Skipping.'", seriesFolder);
continue;
}
Logger.Debug("Folder '{0}' isn't mapped in the database. Trying to map it.'", seriesFolder); Logger.Debug("Folder '{0}' isn't mapped in the database. Trying to map it.'", seriesFolder);
var mappedSeries = _seriesProvider.MapPathToSeries(seriesFolder); var mappedSeries = _seriesProvider.MapPathToSeries(seriesFolder);
@ -86,7 +130,6 @@ namespace NzbDrone.Core.Providers
_episodeProvider.RefreshEpisodeInfo(mappedSeries.Id); _episodeProvider.RefreshEpisodeInfo(mappedSeries.Id);
_seriesSyncNotification.CurrentStatus = String.Format("{0}: finding episodes on disk...", mappedSeries.SeriesName); _seriesSyncNotification.CurrentStatus = String.Format("{0}: finding episodes on disk...", mappedSeries.SeriesName);
_mediaFileProvider.Scan(_seriesProvider.GetSeries(mappedSeries.Id)); _mediaFileProvider.Scan(_seriesProvider.GetSeries(mappedSeries.Id));
} }
else else
{ {

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository
{
public class RootDir
{
[SubSonicPrimaryKey(true)]
public int RootDirId { get; set; }
public string Path { get; set; }
public bool Default { get; set; }
}
}

View File

@ -22,6 +22,7 @@ namespace NzbDrone.Web.Controllers
private readonly IQualityProvider _qualityProvider; private readonly IQualityProvider _qualityProvider;
private readonly IMediaFileProvider _mediaFileProvider; private readonly IMediaFileProvider _mediaFileProvider;
private readonly IRenameProvider _renameProvider; private readonly IRenameProvider _renameProvider;
private readonly IRootDirProvider _rootDirProvider;
// //
// GET: /Series/ // GET: /Series/
@ -29,7 +30,7 @@ namespace NzbDrone.Web.Controllers
public SeriesController(ISyncProvider syncProvider, ISeriesProvider seriesProvider, public SeriesController(ISyncProvider syncProvider, ISeriesProvider seriesProvider,
IEpisodeProvider episodeProvider, IRssSyncProvider rssSyncProvider, IEpisodeProvider episodeProvider, IRssSyncProvider rssSyncProvider,
IQualityProvider qualityProvider, IMediaFileProvider mediaFileProvider, IQualityProvider qualityProvider, IMediaFileProvider mediaFileProvider,
IRenameProvider renameProvider) IRenameProvider renameProvider, IRootDirProvider rootDirProvider)
{ {
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
@ -38,6 +39,7 @@ namespace NzbDrone.Web.Controllers
_qualityProvider = qualityProvider; _qualityProvider = qualityProvider;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_renameProvider = renameProvider; _renameProvider = renameProvider;
_rootDirProvider = rootDirProvider;
} }
public ActionResult Index() public ActionResult Index()
@ -51,9 +53,15 @@ namespace NzbDrone.Web.Controllers
return View(new AddSeriesModel()); return View(new AddSeriesModel());
} }
public ActionResult Sync() public ActionResult AddExisting()
{ {
_syncProvider.BeginSyncUnmappedFolders(); return View();
}
public ActionResult Sync(List<String> paths)
{
//Todo: Make this do something...
_syncProvider.BeginSyncUnmappedFolders(paths);
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
@ -63,9 +71,9 @@ namespace NzbDrone.Web.Controllers
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
public ActionResult UnMapped() public ActionResult UnMapped(string path)
{ {
return View(_seriesProvider.GetUnmappedFolders().Select(c => new MappingModel() { Id = 1, Path = c.Value }).ToList()); return View(_syncProvider.GetUnmappedFolders(path).Select(c => new MappingModel() { Id = 1, Path = c }).ToList());
} }
public ActionResult LoadEpisodes(int seriesId) public ActionResult LoadEpisodes(int seriesId)
@ -103,6 +111,24 @@ namespace NzbDrone.Web.Controllers
Total = data.Count() Total = data.Count()
}); });
} }
[GridAction]
public ActionResult _AjaxUnmappedFoldersGrid()
{
var unmappedList = new List<String>();
foreach (var folder in _rootDirProvider.GetAll())
unmappedList.AddRange(_syncProvider.GetUnmappedFolders(folder.Path));
var seriesPaths = unmappedList.Select(c => new AddExistingSeriesModel
{
IsWanted = true,
Path = c
});
return View(new GridModel(seriesPaths));
}
private IEnumerable<Episode> GetData(GridCommand command) private IEnumerable<Episode> GetData(GridCommand command)
{ {

View File

@ -9,6 +9,7 @@ using NzbDrone.Core;
using NzbDrone.Core.Helpers; using NzbDrone.Core.Helpers;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Web.Models; using NzbDrone.Web.Models;
@ -20,15 +21,19 @@ namespace NzbDrone.Web.Controllers
private IConfigProvider _configProvider; private IConfigProvider _configProvider;
private IIndexerProvider _indexerProvider; private IIndexerProvider _indexerProvider;
private IQualityProvider _qualityProvider; private IQualityProvider _qualityProvider;
private IRootDirProvider _rootDirProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private string _settingsSaved = "Settings Saved."; private string _settingsSaved = "Settings Saved.";
private string _settingsFailed = "Error Saving Settings, please fix any errors"; private string _settingsFailed = "Error Saving Settings, please fix any errors";
public SettingsController(IConfigProvider configProvider, IIndexerProvider indexerProvider, IQualityProvider qualityProvider) public SettingsController(IConfigProvider configProvider, IIndexerProvider indexerProvider,
IQualityProvider qualityProvider, IRootDirProvider rootDirProvider)
{ {
_configProvider = configProvider; _configProvider = configProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_qualityProvider = qualityProvider; _qualityProvider = qualityProvider;
_rootDirProvider = rootDirProvider;
} }
public ActionResult Index(string viewName) public ActionResult Index(string viewName)
@ -37,21 +42,18 @@ namespace NzbDrone.Web.Controllers
ViewData["viewName"] = viewName; ViewData["viewName"] = viewName;
else else
ViewData["viewName"] = "General"; return RedirectToAction("General");
return View("Index", new SettingsModel return View("Index");
{
TvFolder = _configProvider.SeriesRoot
});
} }
public ActionResult General() public ActionResult General()
{ {
ViewData["viewName"] = "General"; ViewData["viewName"] = "General";
return View("Index", new SettingsModel return View("Index", new SettingsModel
{ {
TvFolder = _configProvider.SeriesRoot, Directories = new List<RootDir>()
Quality = Convert.ToInt32(_configProvider.GetValue("Quality", "1", true)),
}); });
} }
@ -185,6 +187,11 @@ namespace NzbDrone.Web.Controllers
return View("UserProfileSection", new QualityProfile { Name = "New Profile", UserProfile = true }); return View("UserProfileSection", new QualityProfile { Name = "New Profile", UserProfile = true });
} }
public ViewResult AddRootDir()
{
return View("RootDir", new RootDir { Default = false });
}
public ActionResult SubMenu() public ActionResult SubMenu()
{ {
return PartialView(); return PartialView();
@ -202,9 +209,30 @@ namespace NzbDrone.Web.Controllers
[HttpPost] [HttpPost]
public ActionResult SaveGeneral(SettingsModel data) public ActionResult SaveGeneral(SettingsModel data)
{ {
if (ModelState.IsValid) if (data.Directories.Count > 0)
{ {
_configProvider.SeriesRoot = data.TvFolder; //If the Javascript was beaten we need to return an error
if (!data.Directories.Exists(d => d.Default))
return Content(_settingsFailed);
var currentRootDirs = _rootDirProvider.GetAll();
foreach (var currentRootDir in currentRootDirs)
{
var closureRootDir = currentRootDir;
if (!data.Directories.Exists(d => d.RootDirId == closureRootDir.RootDirId))
_rootDirProvider.Remove(closureRootDir.RootDirId);
}
foreach (var dir in data.Directories)
{
if (dir.RootDirId == 0)
_rootDirProvider.Add(dir);
else
_rootDirProvider.Update(dir);
}
return Content(_settingsSaved); return Content(_settingsSaved);
} }

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace NzbDrone.Web.Models
{
public class AddExistingSeriesModel
{
public bool IsWanted { get; set; }
public string Path { get; set; }
}
}

View File

@ -10,22 +10,7 @@ namespace NzbDrone.Web.Models
public class SettingsModel public class SettingsModel
{ {
[DisplayName("TV Series Root Folder(s)")]
[DataType(DataType.Text)] public List<RootDir> Directories { get; set; }
[Required(ErrorMessage = "Please enter a valid TV path")]
[DisplayName("TV Folder")]
public String TvFolder
{
get;
set;
}
[DataType(DataType.Text)]
[DisplayName("Initial Quality")]
public int Quality
{
get;
set;
}
} }
} }

View File

@ -86,6 +86,7 @@
<Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" /> <Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" />
<Compile Include="Helpers\IsCurrentActionHelper.cs" /> <Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Models\AccountModels.cs" /> <Compile Include="Models\AccountModels.cs" />
<Compile Include="Models\AddExistingSeriesModel.cs" />
<Compile Include="Models\AddSeriesModel.cs" /> <Compile Include="Models\AddSeriesModel.cs" />
<Compile Include="Models\DownloadSettingsModel.cs" /> <Compile Include="Models\DownloadSettingsModel.cs" />
<Compile Include="Models\EpisodeSortingModel.cs" /> <Compile Include="Models\EpisodeSortingModel.cs" />
@ -271,6 +272,7 @@
<Content Include="Scripts\Notification.js" /> <Content Include="Scripts\Notification.js" />
<Content Include="Views\Home\Test.aspx" /> <Content Include="Views\Home\Test.aspx" />
<Content Include="Views\Log\Index.aspx" /> <Content Include="Views\Log\Index.aspx" />
<Content Include="Views\Series\AddExisting.aspx" />
<Content Include="Views\Series\Details.aspx" /> <Content Include="Views\Series\Details.aspx" />
<Content Include="Views\Series\Edit.aspx" /> <Content Include="Views\Series\Edit.aspx" />
<Content Include="Views\Series\EpisodeDetail.ascx" /> <Content Include="Views\Series\EpisodeDetail.ascx" />
@ -278,6 +280,7 @@
<Content Include="Views\Series\SubMenu.ascx" /> <Content Include="Views\Series\SubMenu.ascx" />
<Content Include="Views\Series\Unmapped.aspx" /> <Content Include="Views\Series\Unmapped.aspx" />
<Content Include="Views\Series\Add.aspx" /> <Content Include="Views\Series\Add.aspx" />
<Content Include="Views\Settings\RootDir.ascx" />
<Content Include="Views\Settings\Notifications.ascx" /> <Content Include="Views\Settings\Notifications.ascx" />
<Content Include="Views\Settings\Downloads.ascx" /> <Content Include="Views\Settings\Downloads.ascx" />
<Content Include="Views\Settings\EpisodeSorting.ascx" /> <Content Include="Views\Settings\EpisodeSorting.ascx" />

View File

@ -15,4 +15,8 @@
//Add Existing //Add Existing
//Ask user for existing TV Root Folder...
//Get list of unmapped folders and allow the user to check off the ones they want to add...
//
</asp:Content> </asp:Content>

View File

@ -0,0 +1,35 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<%@ Import Namespace="NzbDrone.Web.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Add Existing Series
</asp:Content>
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
<%
Html.RenderPartial("SubMenu");
%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
//Get AJAX listing of unmapped directories
<%
Html.Telerik().Grid<AddExistingSeriesModel>().Name("Unmapped Series Folders")
.Columns(columns =>
{
columns.Bound(c => c.IsWanted).Width(0).Title("Is Wanted?");
columns.Bound(c => c.Path);
})
//.DetailView(detailView => detailView.Template(e => Html.RenderPartial("EpisodeDetail", e)))
//.DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #></div>"))
//.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(true))
.Footer(false)
.DataBinding(d => d.Ajax().Select("_AjaxUnmappedFoldersGrid", "Series"))
//.EnableCustomBinding(true)
//.ClientEvents(e => e.OnDetailViewExpand("episodeDetailExpanded")) //Causes issues displaying the episode detail multiple times...
.Render();
%>`
</asp:Content>

View File

@ -5,7 +5,7 @@
<% Html.Telerik().Menu().Name("telerikGrid").Items(items => <% Html.Telerik().Menu().Name("telerikGrid").Items(items =>
{ {
items.Add().Text("View Unmapped Folders").Action("Unmapped", "Series"); items.Add().Text("View Unmapped Folders").Action("Unmapped", "Series");
items.Add().Text("Sync With Disk").Action("Sync", "Series"); items.Add().Text("Sync With Disk").Action("Sync", "Series", new { paths = "test" });
items.Add().Text("Start RSS Sync").Action("RssSync", "Series"); items.Add().Text("Start RSS Sync").Action("RssSync", "Series");
items.Add().Text("Rename All").Action("RenameAll", "Series"); items.Add().Text("Rename All").Action("RenameAll", "Series");
items.Add().Text("Add Series").Action("Add", "Series"); items.Add().Text("Add Series").Action("Add", "Series");

View File

@ -27,15 +27,21 @@
<% using (Html.BeginForm("SaveGeneral", "Settings", FormMethod.Post, new { id = "form", name = "form" })) <% using (Html.BeginForm("SaveGeneral", "Settings", FormMethod.Post, new { id = "form", name = "form" }))
{%> {%>
<%: Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.") %> <%: Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.") %>
<fieldset> <fieldset>
<legend>General</legend> <legend>General</legend>
<div class="editor-label"> <div style="padding-top: 10px;">
<%= Html.LabelFor(model => model.TvFolder) %> <div style="padding-left: 7px; margin-bottom: 5px;">
<a id="addItem" style="text-decoration:none;" href="<%: Url.Action("AddRootDir", "Settings") %>">
<img src="../../Content/Images/Plus.png" alt="Add New Profile" />
<h4 style="margin-left: 3px; display: inline; color: Black;">Add New Root Directory</h4></a>
</div>
<div id="root-dirs">
<%foreach (var item in Model.Directories) { %>
<% Html.RenderPartial("RootDir", item); %>
<% } %>
</div> </div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.TvFolder) %>
<%= Html.ValidationMessageFor(model => model.TvFolder) %>
</div> </div>
<p> <p>
@ -44,3 +50,35 @@
</fieldset> </fieldset>
<% } Html.EndForm();%> <% } Html.EndForm();%>
<div id="result"></div> <div id="result"></div>
<script type="text/javascript">
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#root-dirs").append(html); }
});
return false;
});
$("a.deleteRow").live("click", function () {
$(this).parents("div.rootDirSection:first").remove();
return false;
});
$(".defaultCheckbox").live("change", function () {
var checked = $(this).attr('checked');
if (checked) {
var thisOne = this;
$(".defaultCheckbox").attr('checked', false);
$(this).attr('checked', true);
}
//Don't let the user uncheck a checkbox (Like a radio button)
else {
$(this).attr('checked', true);
}
});
</script>

View File

@ -0,0 +1,36 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NzbDrone.Core.Repository.RootDir>" %>
<%@ Import Namespace="NzbDrone.Web.Helpers" %>
<% using (Html.BeginCollectionItem("Directories"))
{ %>
<%
var idClean = ViewData.TemplateInfo.HtmlFieldPrefix.Replace('[', '_').Replace(']', '_');
//string sortable1 = String.Format("{0}_sortable1", idClean);
%>
<style type="text/css">
.root_dir_text { width:300px; }
</style>
<div class="rootDirSection">
<fieldset style="width:350px; height:16px; margin:0px; margin-top: 0px; border-color:#CCCCCD; -khtml-border-radius:8px; border-radius:8px; -moz-border-radius:8px; -webkit-border-radius:8px;">
<div>
<%: Html.CheckBoxFor(m => m.Default, new { @class = "defaultCheckbox" }) %>
<%: Html.TextBoxFor(m => m.Path, new { @class="root_dir_text" }) %>
<a href="#" class="deleteRow"><img src="../../Content/Images/X.png" alt="Delete"/></a>
</div>
<div>
<%: Html.ValidationMessageFor(m => m.Path) %>
</div>
<div class="hiddenProfileDetails">
<%= Html.TextBoxFor(x => x.RootDirId, new { @style = "display:none" })%>
</div>
</fieldset>
</div>
<% } %>

View File

@ -12,6 +12,8 @@
string sortable2 = String.Format("{0}_sortable2", idClean); string sortable2 = String.Format("{0}_sortable2", idClean);
string allowedStringName = String.Format("{0}_AllowedString", idClean); string allowedStringName = String.Format("{0}_AllowedString", idClean);
string connectedSortable = String.Format("connected{0}", idClean); string connectedSortable = String.Format("connected{0}", idClean);
string title = String.Format("{0}_Title", idClean);
string nameBox = String.Format("{0}_Name", idClean);
%> %>
<style type="text/css"> <style type="text/css">
@ -24,7 +26,6 @@
.sortable1 li.ui-state-highlight, .sortable2 li.ui-state-highlight { background: #fbf5d0; border-color: #065EFE; } .sortable1 li.ui-state-highlight, .sortable2 li.ui-state-highlight { background: #fbf5d0; border-color: #065EFE; }
.removeDiv { float: left; display:block; } .removeDiv { float: left; display:block; }
.ui-state-highlight { height: 1.5em; line-height: 1.2em; } .ui-state-highlight { height: 1.5em; line-height: 1.2em; }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
@ -53,7 +54,7 @@
<fieldset style="width:275px; margin:5px; margin-top: 0px; border-color:#CCCCCD"> <fieldset style="width:275px; margin:5px; margin-top: 0px; border-color:#CCCCCD">
<div id="qualityHeader" style="padding-bottom: 5px; margin: 0px;"> <div id="qualityHeader" style="padding-bottom: 5px; margin: 0px;">
<h2 style="display:inline; padding-right: 4px; margin-left: 4px;"><%= Html.DisplayTextFor(m => m.Name) %></h2> <h2 style="display:inline; padding-right: 4px; margin-left: 4px;" id="<%= title %>"><%= Html.DisplayTextFor(m => m.Name) %></h2>
<a href="#" class="deleteRow"><img src="../../Content/Images/X.png" alt="Delete" /></a> <a href="#" class="deleteRow"><img src="../../Content/Images/X.png" alt="Delete" /></a>
</div> </div>
@ -115,4 +116,12 @@
</div> </div>
</fieldset> </fieldset>
</div> </div>
<script type="text/javascript">
$("#<%: nameBox %>").keyup(function () {
var value = $(this).val();
$("#<%= title %>").text(value);
}).keyup();
</script>
<% } %> <% } %>