diff --git a/NzbDrone.Core/Model/SeriesMappingModel.cs b/NzbDrone.Core/Model/SeriesMappingModel.cs new file mode 100644 index 000000000..30632266a --- /dev/null +++ b/NzbDrone.Core/Model/SeriesMappingModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model +{ + public class SeriesMappingModel + { + public string Path { get; set; } + public int TvDbId { get; set; } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 08ac3e453..fa7378821 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -174,6 +174,7 @@ + diff --git a/NzbDrone.Core/Providers/ISeriesProvider.cs b/NzbDrone.Core/Providers/ISeriesProvider.cs index 88ea1fd12..f9d09e7d3 100644 --- a/NzbDrone.Core/Providers/ISeriesProvider.cs +++ b/NzbDrone.Core/Providers/ISeriesProvider.cs @@ -19,6 +19,7 @@ namespace NzbDrone.Core.Providers /// Whether or not the show is monitored bool IsMonitored(long id); TvdbSeries MapPathToSeries(string path); + TvdbSeries MapPathToSeries(int tvDbId); void AddSeries(string path, TvdbSeries series); Series FindSeries(string cleanTitle); bool QualityWanted(int seriesId, QualityTypes quality); diff --git a/NzbDrone.Core/Providers/ISyncProvider.cs b/NzbDrone.Core/Providers/ISyncProvider.cs index ceec4ea3d..6a0018460 100644 --- a/NzbDrone.Core/Providers/ISyncProvider.cs +++ b/NzbDrone.Core/Providers/ISyncProvider.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using NzbDrone.Core.Model; namespace NzbDrone.Core.Providers { public interface ISyncProvider { - bool BeginSyncUnmappedFolders(List paths); + bool BeginSyncUnmappedFolders(List unmapped); List GetUnmappedFolders(string path); } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index c38aea801..4e8dbcc56 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -76,6 +76,11 @@ namespace NzbDrone.Core.Providers return _tvDb.GetSeries(searchResults.Id, false); } + public TvdbSeries MapPathToSeries(int tvDbId) + { + return _tvDb.GetSeries(tvDbId, false); + } + public void AddSeries(string path, TvdbSeries series) { Logger.Info("Adding Series [{0}]:{1} Path: {2}", series.Id, series.SeriesName, path); diff --git a/NzbDrone.Core/Providers/SyncProvider.cs b/NzbDrone.Core/Providers/SyncProvider.cs index 00c9b0959..7dba12904 100644 --- a/NzbDrone.Core/Providers/SyncProvider.cs +++ b/NzbDrone.Core/Providers/SyncProvider.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Threading; using NLog; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; namespace NzbDrone.Core.Providers @@ -19,7 +20,7 @@ namespace NzbDrone.Core.Providers private ProgressNotification _seriesSyncNotification; private Thread _seriesSyncThread; - private List _syncList; + private List _syncList; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -62,7 +63,7 @@ namespace NzbDrone.Core.Providers #endregion - public bool BeginSyncUnmappedFolders(List paths) + public bool BeginSyncUnmappedFolders(List unmapped) { Logger.Debug("User has request series folder scan"); if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive) @@ -74,7 +75,7 @@ namespace NzbDrone.Core.Providers Priority = ThreadPriority.Lowest }; - _syncList = paths; + _syncList = unmapped; _seriesSyncThread.Start(); } else @@ -105,20 +106,20 @@ namespace NzbDrone.Core.Providers { try { - _seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(new DirectoryInfo(seriesFolder).Name)); + _seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", new DirectoryInfo(seriesFolder.Path).Name); - if (_seriesProvider.SeriesPathExists(Parser.NormalizePath(seriesFolder))) + if (_seriesProvider.SeriesPathExists(Parser.NormalizePath(seriesFolder.Path))) { 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); - var mappedSeries = _seriesProvider.MapPathToSeries(seriesFolder); + var mappedSeries = _seriesProvider.MapPathToSeries(seriesFolder.TvDbId); if (mappedSeries == null) { - Logger.Warn("Unable to find a matching series for '{0}'", seriesFolder); + Logger.Warn("Invalid TVDB ID '{0}' Unable to map: '{1}'", seriesFolder.TvDbId, seriesFolder.Path); } else { @@ -126,7 +127,7 @@ namespace NzbDrone.Core.Providers if (_seriesProvider.GetSeries(mappedSeries.Id) == null) { _seriesSyncNotification.CurrentStatus = String.Format("{0}: downloading series info...", mappedSeries.SeriesName); - _seriesProvider.AddSeries(seriesFolder, mappedSeries); + _seriesProvider.AddSeries(seriesFolder.Path, mappedSeries); _episodeProvider.RefreshEpisodeInfo(mappedSeries.Id); _seriesSyncNotification.CurrentStatus = String.Format("{0}: finding episodes on disk...", mappedSeries.SeriesName); _mediaFileProvider.Scan(_seriesProvider.GetSeries(mappedSeries.Id)); diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index a429649cb..ea6256f65 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Threading; using System.Web; using System.Web.Mvc; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Repository; using NzbDrone.Web.Models; using Telerik.Web.Mvc; +using TvdbLib.Data; +using EpisodeModel = NzbDrone.Web.Models.EpisodeModel; namespace NzbDrone.Web.Controllers { @@ -50,7 +54,7 @@ namespace NzbDrone.Web.Controllers public ActionResult Add() { - return View(new AddSeriesModel()); + return View(new AddNewSeriesModel()); } public ActionResult AddExisting() @@ -58,13 +62,6 @@ namespace NzbDrone.Web.Controllers return View(); } - public ActionResult Sync(List paths) - { - //Todo: Make this do something... - _syncProvider.BeginSyncUnmappedFolders(paths); - return RedirectToAction("Index"); - } - public ActionResult RssSync() { _rssSyncProvider.Begin(); @@ -115,18 +112,52 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxUnmappedFoldersGrid() { - var unmappedList = new List(); + var unmappedList = new List(); foreach (var folder in _rootDirProvider.GetAll()) - unmappedList.AddRange(_syncProvider.GetUnmappedFolders(folder.Path)); + { + foreach (var unmappedFolder in _syncProvider.GetUnmappedFolders(folder.Path)) + { + var tvDbSeries = _seriesProvider.MapPathToSeries(unmappedFolder); + + //We still want to show this series as unmapped, but we don't know what it will be when mapped + //Todo: Provide the user with a way to manually map a folder to a TvDb series (or make them rename the folder...) + if (tvDbSeries == null) + tvDbSeries = new TvdbSeries {Id = 0, SeriesName = String.Empty}; + + unmappedList.Add(new AddExistingSeriesModel + { + IsWanted = true, + Path = unmappedFolder, + TvDbId = tvDbSeries.Id, + TvDbName = tvDbSeries.SeriesName + }); + } + } + + return View(new GridModel(unmappedList)); + } + + public ActionResult SyncSelectedSeries(List checkedRecords) + { - var seriesPaths = unmappedList.Select(c => new AddExistingSeriesModel - { - IsWanted = true, - Path = c - }); + var unmappedList = new List(); + + foreach (var checkedRecord in checkedRecords) + { + NameValueCollection nvc = HttpUtility.ParseQueryString(checkedRecord); + + var path = HttpUtility.UrlDecode(nvc["path"]); + var tvDbId = Convert.ToInt32(HttpUtility.UrlDecode(nvc["tvdbid"])); + + //If the TvDbId for this show is 0 then skip it... User made a mistake... They will have to manually map it + if (tvDbId < 1) continue; + + unmappedList.Add(new SeriesMappingModel{Path = path, TvDbId = tvDbId}); + } - return View(new GridModel(seriesPaths)); + _syncProvider.BeginSyncUnmappedFolders(unmappedList); + return Content("Sync Started for Selected Series"); } private IEnumerable GetData(GridCommand command) diff --git a/NzbDrone.Web/Models/AddExistingSeriesModel.cs b/NzbDrone.Web/Models/AddExistingSeriesModel.cs index ecb0b0b46..bde01e0f1 100644 --- a/NzbDrone.Web/Models/AddExistingSeriesModel.cs +++ b/NzbDrone.Web/Models/AddExistingSeriesModel.cs @@ -9,5 +9,7 @@ namespace NzbDrone.Web.Models { public bool IsWanted { get; set; } public string Path { get; set; } + public int TvDbId { get; set; } + public string TvDbName { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/AddSeriesModel.cs b/NzbDrone.Web/Models/AddNewSeriesModel.cs similarity index 56% rename from NzbDrone.Web/Models/AddSeriesModel.cs rename to NzbDrone.Web/Models/AddNewSeriesModel.cs index dd46101b6..e2da0d96b 100644 --- a/NzbDrone.Web/Models/AddSeriesModel.cs +++ b/NzbDrone.Web/Models/AddNewSeriesModel.cs @@ -7,16 +7,12 @@ using System.Web; namespace NzbDrone.Web.Models { - public class AddSeriesModel + public class AddNewSeriesModel { + [Required(ErrorMessage = "Please enter a series name")] [DataType(DataType.Text)] [DisplayName("Single Series Path")] [DisplayFormat(ConvertEmptyStringToNull = false)] - public string SingleSeries { get; set; } - - [DataType(DataType.Text)] - [DisplayName("Series Root Path")] - [DisplayFormat(ConvertEmptyStringToNull = false)] - public string SeriesRoot { get; set; } + public string SeriesName { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 30317fca1..9d015b340 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -70,6 +70,10 @@ + + False + ..\NzbDrone.Core\Libraries\TvdbLib.dll + @@ -87,7 +91,7 @@ - + @@ -273,6 +277,7 @@ + diff --git a/NzbDrone.Web/Views/Series/Add.aspx b/NzbDrone.Web/Views/Series/Add.aspx index 66535023f..3df4ce31b 100644 --- a/NzbDrone.Web/Views/Series/Add.aspx +++ b/NzbDrone.Web/Views/Series/Add.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> +<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> Add Series diff --git a/NzbDrone.Web/Views/Series/AddExisting.aspx b/NzbDrone.Web/Views/Series/AddExisting.aspx index 5da2116a1..53984e81e 100644 --- a/NzbDrone.Web/Views/Series/AddExisting.aspx +++ b/NzbDrone.Web/Views/Series/AddExisting.aspx @@ -12,24 +12,103 @@ %> + + //Get AJAX listing of unmapped directories + //When getting unmapped, also do a quick lookup on TVDB to see which series we would map this to... Don't do the mapping though... + //ITvDbProvider.GetSeries(string title); <% - Html.Telerik().Grid().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("
<#= Overview #>
")) - //.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... + Html.Telerik().Grid().Name("Unmapped_Series_Folders") + .TableHtmlAttributes(new { id = "UnmappedSeriesGrid" }) + .Columns(columns => + { + columns.Bound(c => c.IsWanted).ClientTemplate("") + .Width(20).Title("") + .HtmlAttributes(new { style = "text-align:center" }); + + columns.Bound(c => c.Path); + columns.Bound(c => c.TvDbName); + }) + .DataBinding(d => d.Ajax().Select("_AjaxUnmappedFoldersGrid", "Series")) + .ClientEvents(events => events.OnRowDataBound("Grid_onRowDataBound")) + .Footer(false) .Render(); - %>` + %> + +

+ +

+ +
+
+ +
diff --git a/NzbDrone.Web/Views/Series/AddNew.aspx b/NzbDrone.Web/Views/Series/AddNew.aspx new file mode 100644 index 000000000..16b2eb32b --- /dev/null +++ b/NzbDrone.Web/Views/Series/AddNew.aspx @@ -0,0 +1,32 @@ +<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> +<%@ Import Namespace="NzbDrone.Web.Models" %> +<%@ Import Namespace="Telerik.Web.Mvc.UI" %> + + + Add New Series + + + <% + Html.RenderPartial("SubMenu"); + %> + + + + //Add a new series + + <%= Html.Label("Enter a Series Name") %> + <%= Html.TextBox("new_series_name", new { id="new_series_id" }) %> + + //Browse Button?? + //Auto-Complete? + + //Search Button - Perform AJAX search for this Series on TVDB + + //Return results with Radio Box + First Aired information, (link to TVDB too?) + Hidden ID text + + User selects radio button and then presses add (or skips which clears results and #new_series_id) + + Add, ask user to choose where to save the show in (used when sorting) then add the show... Possibly ask user to choose Quality Profile + + + diff --git a/NzbDrone.Web/Views/Series/SubMenu.ascx b/NzbDrone.Web/Views/Series/SubMenu.ascx index 887e9cf00..1fd8646df 100644 --- a/NzbDrone.Web/Views/Series/SubMenu.ascx +++ b/NzbDrone.Web/Views/Series/SubMenu.ascx @@ -5,7 +5,6 @@ <% Html.Telerik().Menu().Name("telerikGrid").Items(items => { items.Add().Text("View Unmapped Folders").Action("Unmapped", "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("Rename All").Action("RenameAll", "Series"); items.Add().Text("Add Series").Action("Add", "Series");