Fixed add existing. needs more polish around quality and manual mapping.

pull/2/head
kay.one 13 years ago
parent 4000387419
commit 0556c31846

@ -54,6 +54,36 @@ namespace NzbDrone.Core.Test
Assert.AreEqual(fakeSeries, mappedSeries);
}
[Test]
public void Add_new_series()
{
var repo = MockLib.GetEmptyRepository();
var kernel = new MockingKernel();
kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
kernel.Bind<IRepository>().ToConstant(repo);
string path = "C:\\Test\\";
int tvDbId = 1234;
int qualityProfileId = 2;
//Act
var seriesProvider = kernel.Get<ISeriesProvider>();
seriesProvider.AddSeries(path, tvDbId, qualityProfileId);
//Assert
var series = seriesProvider.GetAllSeries();
Assert.IsNotEmpty(series);
Assert.Count(1, series);
Assert.AreEqual(path, series.First().Path);
Assert.AreEqual(tvDbId, series.First().SeriesId);
Assert.AreEqual(qualityProfileId, series.First().QualityProfileId);
}
[Test]
[Row(new object[] { "That's Life - 2x03 -The Devil and Miss DeLucca", "That's Life" })]
[Row(new object[] { "Van.Duin.Op.Zn.Best.S02E05.DUTCH.WS.PDTV.XViD-DiFFERENT", "Van Duin Op Zn Best" })]

@ -163,11 +163,36 @@ namespace NzbDrone.Core.Providers
set { SetValue("BlackholeDirectory", value); }
}
public bool UseSeasonFolder
{
get { return GetValueBoolean("Sorting_SeasonFolder", true); }
set { SetValue("Sorting_SeasonFolder", value); }
}
public int DefaultQualityProfile
{
get { return GetValueInt("DefaultQualityProfile", 1); }
set { SetValue("DefaultQualityProfile", value); }
}
private string GetValue(string key)
{
return GetValue(key, String.Empty, false);
}
private bool GetValueBoolean(string key, bool defaultValue = false)
{
return Convert.ToBoolean(GetValue(key, defaultValue, false));
}
private int GetValueInt(string key, int defaultValue = 0)
{
return Convert.ToInt16(GetValue(key, defaultValue, false));
}
public string GetValue(string key, object defaultValue, bool makePermanent)
{
string value;
@ -185,6 +210,16 @@ namespace NzbDrone.Core.Providers
return value;
}
public void SetValue(string key, Boolean value)
{
SetValue(key, value.ToString());
}
public void SetValue(string key, int value)
{
SetValue(key, value.ToString());
}
public void SetValue(string key, string value)
{
if (String.IsNullOrEmpty(key))

@ -49,12 +49,6 @@ namespace NzbDrone.Core.Providers
File.Move(sourcePath, destinationPath);
}
public string GetFolderName(string path)
{
var di = new DirectoryInfo(path);
return di.Name;
}
#endregion
}
}

@ -25,6 +25,8 @@ namespace NzbDrone.Core.Providers
String SyncFrequency { get; set; }
String SabTvPriority { get; set; }
String ApiKey { get; set; }
bool UseSeasonFolder { get; set; }
int DefaultQualityProfile { get; set; }
string GetValue(string key, object defaultValue, bool makePermanent);
void SetValue(string key, string value);

@ -13,6 +13,5 @@ namespace NzbDrone.Core.Providers
long GetSize(string path);
void DeleteFile(string path);
void RenameFile(string sourcePath, string destinationPath);
string GetFolderName(string path);
}
}

@ -19,12 +19,12 @@ namespace NzbDrone.Core.Providers
/// <returns>Whether or not the show is monitored</returns>
bool IsMonitored(long id);
TvdbSeries MapPathToSeries(string path);
TvdbSeries MapPathToSeries(int tvDbId);
void AddSeries(string path, TvdbSeries series, int qualityProfileId);
void AddSeries(string path, int tvDbSeriesId, int qualityProfileId);
Series FindSeries(string cleanTitle);
bool QualityWanted(int seriesId, QualityTypes quality);
void UpdateSeries(Series series);
void DeleteSeries(int seriesId);
bool SeriesPathExists(string cleanPath);
Series UpdateSeriesInfo(int seriesId);
}
}

@ -6,9 +6,8 @@ namespace NzbDrone.Core.Providers
{
public interface ISyncProvider
{
bool BeginSyncUnmappedFolders(List<SeriesMappingModel> unmapped);
bool BeginAddNewSeries(string dir, int seriesId, string seriesName, int qualityProfileId);
bool BeginAddExistingSeries(string path, int seriesId, int qualityProfileId);
List<String> GetUnmappedFolders(string path);
bool BeginUpdateNewSeries();
}
}

@ -18,17 +18,15 @@ namespace NzbDrone.Core.Providers
//Trims all white spaces and separators from the end of the title.
private readonly IConfigProvider _config;
private readonly IDiskProvider _diskProvider;
private readonly IRepository _sonioRepo;
private readonly ITvDbProvider _tvDb;
private readonly IQualityProvider _quality;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public SeriesProvider(IDiskProvider diskProvider, IConfigProvider configProvider,
public SeriesProvider(IConfigProvider configProvider,
IRepository dataRepository, ITvDbProvider tvDbProvider, IQualityProvider quality)
{
_diskProvider = diskProvider;
_config = configProvider;
_sonioRepo = dataRepository;
_tvDb = tvDbProvider;
@ -76,33 +74,38 @@ namespace NzbDrone.Core.Providers
return _tvDb.GetSeries(searchResults.Id, false);
}
public TvdbSeries MapPathToSeries(int tvDbId)
public Series UpdateSeriesInfo(int seriesId)
{
return _tvDb.GetSeries(tvDbId, false);
var tvDbSeries = _tvDb.GetSeries(seriesId, true);
var series = GetSeries(seriesId);
series.SeriesId = tvDbSeries.Id;
series.Title = tvDbSeries.SeriesName;
series.AirTimes = tvDbSeries.AirsTime;
series.AirsDayOfWeek = tvDbSeries.AirsDayOfWeek;
series.Overview = tvDbSeries.Overview;
series.Status = tvDbSeries.Status;
series.Language = tvDbSeries.Language != null ? tvDbSeries.Language.Abbriviation : string.Empty;
series.CleanTitle = Parser.NormalizeTitle(tvDbSeries.SeriesName);
series.LastInfoSync = DateTime.Now;
UpdateSeries(series);
return series;
}
public void AddSeries(string path, TvdbSeries series, int qualityProfileId)
public void AddSeries(string path, int tvDbSeriesId, int qualityProfileId)
{
Logger.Info("Adding Series [{0}]:{1} Path: {2}", series.Id, series.SeriesName, path);
Logger.Info("Adding Series [{0}] Path: [{1}]", tvDbSeriesId, path);
var repoSeries = new Series();
repoSeries.SeriesId = series.Id;
repoSeries.Title = series.SeriesName;
repoSeries.AirTimes = series.AirsTime;
repoSeries.AirsDayOfWeek = series.AirsDayOfWeek;
repoSeries.Overview = series.Overview;
repoSeries.Status = series.Status;
repoSeries.Language = series.Language != null ? series.Language.Abbriviation : string.Empty;
repoSeries.SeriesId = tvDbSeriesId;
repoSeries.Path = path;
repoSeries.CleanTitle = Parser.NormalizeTitle(series.SeriesName);
repoSeries.Monitored = true; //New shows should be monitored
repoSeries.QualityProfileId = qualityProfileId;
if (qualityProfileId == 0)
repoSeries.QualityProfileId = Convert.ToInt32(_config.GetValue("DefaultQualityProfile", "1", true));
repoSeries.SeasonFolder = true;
if (!Convert.ToBoolean(_config.GetValue("Sorting_SeasonFolder", true, true)))
repoSeries.SeasonFolder = false;
repoSeries.SeasonFolder = _config.UseSeasonFolder;
_sonioRepo.Add(repoSeries);
}
@ -119,22 +122,35 @@ namespace NzbDrone.Core.Providers
public void DeleteSeries(int seriesId)
{
var series = _sonioRepo.Single<Series>(seriesId);
Logger.Warn("Deleting Series [{0}]", seriesId);
try
{
var series = _sonioRepo.Single<Series>(seriesId);
//Delete Files, Episdes, Seasons then the Series
//Can't use providers because episode provider needs series provider - Cyclic Dependency Injection, this will work
Logger.Debug("Deleting EpisodeFiles from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.EpisodeFiles);
//Delete Files, Episdes, Seasons then the Series
//Can't use providers because episode provider needs series provider - Cyclic Dependency Injection, this will work
Logger.Debug("Deleting Episodes from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.Episodes);
Logger.Debug("Deleting EpisodeFiles from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.EpisodeFiles);
Logger.Debug("Deleting Seasons from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.Seasons);
Logger.Debug("Deleting Episodes from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.Episodes);
Logger.Debug("Deleting Series from DB {0}", series.Title);
_sonioRepo.Delete<Series>(seriesId);
Logger.Debug("Deleting Seasons from DB for Series: {0}", series.SeriesId);
_sonioRepo.DeleteMany(series.Seasons);
Logger.Info("Successfully deleted Series [{0}]", seriesId);
Logger.Debug("Deleting Series from DB {0}", series.Title);
_sonioRepo.Delete<Series>(seriesId);
}
catch (Exception e)
{
Logger.Error("An error has occurred while deleting series.", e);
throw;
}
}
public bool SeriesPathExists(string cleanPath)
@ -147,10 +163,5 @@ namespace NzbDrone.Core.Providers
#endregion
#region Static Helpers
#endregion
}
}

@ -20,7 +20,6 @@ namespace NzbDrone.Core.Providers
private ProgressNotification _seriesSyncNotification;
private Thread _seriesSyncThread;
private List<SeriesMappingModel> _syncList;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -46,6 +45,7 @@ namespace NzbDrone.Core.Providers
if (!_diskProvider.FolderExists(path))
{
Logger.Debug("Path supplied does not exist: {0}", path);
return null;
}
var results = new List<String>();
@ -63,19 +63,18 @@ namespace NzbDrone.Core.Providers
#endregion
public bool BeginSyncUnmappedFolders(List<SeriesMappingModel> unmapped)
public bool BeginUpdateNewSeries()
{
Logger.Debug("User has requested series folder scan");
Logger.Debug("User has requested a scan of new series");
if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive)
{
Logger.Debug("Initializing background scan of series folder.");
_seriesSyncThread = new Thread(SyncUnmappedFolders)
Logger.Debug("Initializing background scan thread");
_seriesSyncThread = new Thread(SyncNewSeries)
{
Name = "SyncUnmappedFolders",
Name = "SyncNewSeries",
Priority = ThreadPriority.Lowest
};
_syncList = unmapped;
_seriesSyncThread.Start();
}
else
@ -90,139 +89,66 @@ namespace NzbDrone.Core.Providers
return true;
}
public bool BeginAddNewSeries(string dir, int seriesId, string seriesName, int qualityProfileId)
private void SyncNewSeries()
{
Logger.Debug("User has requested adding of new series");
if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive)
Logger.Info("Syncing new series");
try
{
Logger.Debug("Initializing background add of of series folder.");
_seriesSyncThread = new Thread(SyncUnmappedFolders)
using (_seriesSyncNotification = new ProgressNotification("Series Scan"))
{
Name = "SyncUnmappedFolders",
Priority = ThreadPriority.Lowest
};
_syncList = new List<SeriesMappingModel>();
var path = dir + Path.DirectorySeparatorChar + seriesName;
//Create a directory for this new series
_diskProvider.CreateDirectory(path);
_notificationProvider.Register(_seriesSyncNotification);
//Add it to the list so it will be processed
_syncList.Add(new SeriesMappingModel { Path = path, TvDbId = seriesId, QualityProfileId = qualityProfileId });
_seriesSyncNotification.CurrentStatus = "Finding New Series";
ScanSeries();
_seriesSyncThread.Start();
_seriesSyncNotification.CurrentStatus = "Series Scan Completed";
Logger.Info("Series folders scan has successfully completed.");
Thread.Sleep(3000);
_seriesSyncNotification.Status = ProgressNotificationStatus.Completed;
}
}
else
catch (Exception e)
{
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;
Logger.ErrorException(e.Message, e);
}
//return true if sync has started
return true;
}
public bool BeginAddExistingSeries(string path, int seriesId, int qualityProfileId)
private void ScanSeries()
{
Logger.Debug("User has requested adding of new series");
if (_seriesSyncThread == null || !_seriesSyncThread.IsAlive)
{
Logger.Debug("Initializing background add of of series folder.");
_seriesSyncThread = new Thread(SyncUnmappedFolders)
{
Name = "SyncUnmappedFolders",
Priority = ThreadPriority.Lowest
};
_syncList = new List<SeriesMappingModel>();
//Add it to the list so it will be processed
_syncList.Add(new SeriesMappingModel { Path = path, TvDbId = seriesId, QualityProfileId = qualityProfileId });
var syncList = _seriesProvider.GetAllSeries().Where(s => s.LastInfoSync == null).ToList();
if (syncList.Count == 0) return;
_seriesSyncThread.Start();
}
else
_seriesSyncNotification.ProgressMax = syncList.Count;
foreach (var currentSeries in syncList)
{
Logger.Warn("Series folder scan already in progress. Ignoring request.");
try
{
_seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", new DirectoryInfo(currentSeries.Path).Name);
var updatedSeries = _seriesProvider.UpdateSeriesInfo(currentSeries.SeriesId);
//return false if sync was already running, then we can tell the user to try again later
return false;
}
_seriesSyncNotification.CurrentStatus = String.Format("Downloading episode info For: {0}", updatedSeries.Title);
_episodeProvider.RefreshEpisodeInfo(updatedSeries.SeriesId);
//return true if sync has started
return true;
}
_seriesSyncNotification.CurrentStatus = String.Format("Scanning series folder {0}", updatedSeries.Path);
_mediaFileProvider.Scan(_seriesProvider.GetSeries(updatedSeries.SeriesId));
private void SyncUnmappedFolders()
{
Logger.Info("Starting Series folder scan");
//Todo: Launch Backlog search for this series _backlogProvider.StartSearch(mappedSeries.Id);
}
try
{
using (_seriesSyncNotification = new ProgressNotification("Series Scan"))
catch (Exception e)
{
_notificationProvider.Register(_seriesSyncNotification);
_seriesSyncNotification.CurrentStatus = "Analysing Folder";
_seriesSyncNotification.ProgressMax = _syncList.Count;
foreach (var seriesFolder in _syncList)
{
try
{
_seriesSyncNotification.CurrentStatus = String.Format("Searching For: {0}", new DirectoryInfo(seriesFolder.Path).Name);
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.TvDbId);
if (mappedSeries == null)
{
Logger.Warn("Invalid TVDB ID '{0}' Unable to map: '{1}'", seriesFolder.TvDbId, seriesFolder.Path);
}
else
{
//Check if series is mapped to another folder
if (_seriesProvider.GetSeries(mappedSeries.Id) == null)
{
_seriesSyncNotification.CurrentStatus = String.Format("{0}: downloading series info...", mappedSeries.SeriesName);
_seriesProvider.AddSeries(seriesFolder.Path, mappedSeries, seriesFolder.QualityProfileId);
_episodeProvider.RefreshEpisodeInfo(mappedSeries.Id);
_seriesSyncNotification.CurrentStatus = String.Format("{0}: finding episodes on disk...", mappedSeries.SeriesName);
_mediaFileProvider.Scan(_seriesProvider.GetSeries(mappedSeries.Id));
//Todo: Launch Backlog search for this series _backlogProvider.StartSearch(mappedSeries.Id);
}
else
{
Logger.Warn("Folder '{0}' mapped to '{1}' which is already another folder assigned to it.'", seriesFolder, mappedSeries.SeriesName);
}
}
}
catch (Exception e)
{
Logger.ErrorException(e.Message, e);
}
_seriesSyncNotification.ProgressValue++;
}
_seriesSyncNotification.CurrentStatus = "Series Scan Completed";
Logger.Info("Series folders scan has successfully completed.");
Thread.Sleep(3000);
_seriesSyncNotification.Status = ProgressNotificationStatus.Completed;
Logger.ErrorException(e.Message, e);
}
_seriesSyncNotification.ProgressValue++;
}
catch (Exception e)
{
Logger.ErrorException(e.Message, e);
}
//Keep scanning until there no more shows left.
ScanSeries();
}
}
}

@ -21,6 +21,10 @@ namespace NzbDrone.Core.Repository
public string Language { get; set; }
public EpisodeStatusType Status { get; set; }
public DayOfWeek? LastInfoSync { get; set; }
public DayOfWeek? LastDiskSync { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Season Season { get; set; }

@ -11,6 +11,10 @@ namespace NzbDrone.Core.Repository
public virtual int SeriesId { get; set; }
public virtual int SeasonNumber { get; set; }
public bool Monitored { get; set; }
public DayOfWeek? LastInfoSync { get; set; }
public DayOfWeek? LastDiskSync { get; set; }
[SubSonicToManyRelation]
public virtual List<Episode> Episodes { get; private set; }

@ -11,21 +11,25 @@ namespace NzbDrone.Core.Repository
[SubSonicPrimaryKey(false)]
public virtual int SeriesId { get; set; }
[SubSonicNullString]
public string Title { get; set; }
[SubSonicNullString]
public string CleanTitle { get; set; }
[SubSonicNullString]
public string Status { get; set; }
[SubSonicLongString]
[SubSonicNullString]
public string Overview { get; set; }
[DisplayName("Air on")]
public DayOfWeek? AirsDayOfWeek { get; set; }
[SubSonicNullString]
public String AirTimes { get; set; }
[SubSonicNullString]
public string Language { get; set; }
public string Path { get; set; }
@ -36,6 +40,10 @@ namespace NzbDrone.Core.Repository
public bool SeasonFolder { get; set; }
public DateTime? LastInfoSync { get; set; }
public DateTime? LastDiskSync { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true, JoinKeyName = "QualityProfileId")]
public virtual QualityProfile QualityProfile { get; private set; }

@ -214,17 +214,7 @@ hr
margin-bottom: 10px;
}
input[type="text"]
{
border: 1px solid #006;
background: #CBE8FF;
}
input[type="text"]:hover
{
border: 1px solid #065EFE;
background: #C0D6FF;
}
input[type="button"], input[type="submit"], input[type="reset"], input[type="file"]::-webkit-file-upload-button, button
{
@ -236,6 +226,12 @@ input[type="button"], input[type="submit"], input[type="reset"], input[type="fil
margin: 10px;
}
.listButton
{
padding: 1px 10px 2px 10px;
margin: 0px;
}
#save_button
{
}

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NzbDrone.Core.Providers;
using NzbDrone.Web.Models;
namespace NzbDrone.Web.Controllers
{
public class AddSeriesController : Controller
{
public IConfigProvider ConfigProvider { get; set; }
private readonly ISyncProvider _syncProvider;
private readonly IRootDirProvider _rootFolderProvider;
private readonly IConfigProvider _configProvider;
private readonly IQualityProvider _qualityProvider;
private readonly ITvDbProvider _tvDbProvider;
private readonly ISeriesProvider _seriesProvider;
public AddSeriesController(ISyncProvider syncProvider, IRootDirProvider rootFolderProvider, IConfigProvider configProvider,
IQualityProvider qualityProvider, ITvDbProvider tvDbProvider, ISeriesProvider seriesProvider)
{
ConfigProvider = configProvider;
_syncProvider = syncProvider;
_rootFolderProvider = rootFolderProvider;
_configProvider = configProvider;
_qualityProvider = qualityProvider;
_tvDbProvider = tvDbProvider;
_seriesProvider = seriesProvider;
}
[HttpPost]
public JsonResult ScanNewSeries()
{
_syncProvider.BeginUpdateNewSeries();
return new JsonResult();
}
public ActionResult AddNew()
{
ViewData["RootDirs"] = _rootFolderProvider.GetAll();
ViewData["DirSep"] = Path.DirectorySeparatorChar;
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
var defaultQuality = Convert.ToInt32(_configProvider.DefaultQualityProfile);
var model = new AddNewSeriesModel
{
DirectorySeparatorChar = Path.DirectorySeparatorChar.ToString(),
RootDirectories = _rootFolderProvider.GetAll(),
QualityProfileId = defaultQuality,
QualitySelectList = selectList
};
return View(model);
}
public ActionResult AddExisting()
{
var defaultQuality = _configProvider.DefaultQualityProfile;
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
ViewData["QualityProfileId"] = defaultQuality;
ViewData["QualitySelectList"] = selectList;
var unmappedList = new List<String>();
foreach (var folder in _rootFolderProvider.GetAll())
{
unmappedList.AddRange(_syncProvider.GetUnmappedFolders(folder.Path));
}
return View(unmappedList);
}
public ActionResult AddExistingManual(string path)
{
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
var defaultQuality = _configProvider.DefaultQualityProfile;
var model = new AddExistingManualModel();
model.Path = path;
model.FolderName = new DirectoryInfo(path).Name;
model.QualityProfileId = defaultQuality;
model.QualitySelectList = selectList;
return View(model);
}
public ActionResult RenderPartial(string path)
{
var dataVal = _tvDbProvider.SearchSeries(new DirectoryInfo(path).Name);
var names = dataVal.Select(tvdb => tvdb.SeriesName).ToList();
if (dataVal.Count == 0) return null;
var ids = dataVal.Select(tvdb => tvdb.Id).ToList();
var list = new SelectList(dataVal, "Id", "SeriesName");
ViewData["guid"] = Guid.NewGuid();
ViewData["path"] = path;
ViewData["javaPath"] = path.Replace(Path.DirectorySeparatorChar, '|').Replace(Path.VolumeSeparatorChar, '^');
return PartialView("AddSeriesItem", list);
}
public JsonResult AddSeries(string path, int seriesId, int qualityProfileId)
{
//Get TVDB Series Name
//Create new folder for series
//Add the new series to the Database
_seriesProvider.AddSeries(path.Replace('|', Path.DirectorySeparatorChar).Replace('^', Path.VolumeSeparatorChar), seriesId, qualityProfileId);
ScanNewSeries();
return new JsonResult() { Data = "ok" };
}
}
}

@ -61,52 +61,9 @@ namespace NzbDrone.Web.Controllers
return View();
}
public ActionResult AddExisting()
{
var defaultQuality = Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", "1", true));
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
ViewData["QualityProfileId"] = defaultQuality;
ViewData["QualitySelectList"] = selectList;
return View();
}
public ActionResult AddNew()
{
ViewData["RootDirs"] = _rootDirProvider.GetAll();
ViewData["DirSep"] = Path.DirectorySeparatorChar;
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
var defaultQuality = Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", "1", true));
var model = new AddNewSeriesModel
{
DirectorySeparatorChar = Path.DirectorySeparatorChar.ToString(),
RootDirectories = _rootDirProvider.GetAll(),
QualityProfileId = defaultQuality,
QualitySelectList = selectList
};
return View(model);
}
public ActionResult AddExistingManual(string path)
{
var profiles = _qualityProvider.GetAllProfiles();
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
var defaultQuality = Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", "1", true));
var model = new AddExistingManualModel();
model.Path = path;
model.FolderName = _diskProvider.GetFolderName(path);
model.QualityProfileId = defaultQuality;
model.QualitySelectList = selectList;
return View(model);
}
public ActionResult RssSync()
{
@ -187,41 +144,6 @@ namespace NzbDrone.Web.Controllers
return View(new GridModel(unmappedList));
}
public ActionResult SyncSelectedSeries(string path, int tvdbId, int qualityProfileId)
{
var unmappedList = new List<SeriesMappingModel>();
//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) throw new ArgumentException("Invalid tvdb id", "tvdbId");
unmappedList.Add(new SeriesMappingModel { Path = path, TvDbId = tvdbId, QualityProfileId = qualityProfileId });
return Content("Ok");
}
public ActionResult AddNewSeries(string dir, int seriesId, string seriesName, int qualityProfileId)
{
//Get TVDB Series Name
//Create new folder for series
//Add the new series to the Database
if (_syncProvider.BeginAddNewSeries(dir, seriesId, seriesName, qualityProfileId))
return Content("Adding new series has started.");
return Content("Unable to add new series, please wait for previous scans to complete first.");
}
public ActionResult AddExistingSeries(string path, int seriesId, int qualityProfileId)
{
//Get TVDB Series Name
//Create new folder for series
//Add the new series to the Database
if (_syncProvider.BeginAddExistingSeries(path, seriesId, qualityProfileId))
return Content("Manual adding of existing series has started");
return Content("Unable to add existing series, please wait for previous scans to complete first.");
}
public ActionResult SearchForSeries(string seriesName)
{

@ -162,7 +162,7 @@ namespace NzbDrone.Web.Controllers
model.ReplaceSpaces = Convert.ToBoolean(_configProvider.GetValue("Sorting_ReplaceSpaces", false, true));
model.AppendQuality = Convert.ToBoolean(_configProvider.GetValue("Sorting_AppendQuality", false, true));
model.UseAirByDate = Convert.ToBoolean(_configProvider.GetValue("Sorting_UseAirByDate", true, true));
model.SeasonFolders = Convert.ToBoolean(_configProvider.GetValue("Sorting_SeasonFolder", true, true));
model.SeasonFolders = _configProvider.UseSeasonFolder;
model.SeasonFolderFormat = _configProvider.GetValue("Sorting_SeasonFolderFormat", "Season %s", true);
model.SeparatorStyle = Convert.ToInt32(_configProvider.GetValue("Sorting_SeparatorStyle", 0, true));
model.NumberStyle = Convert.ToInt32(_configProvider.GetValue("Sorting_NumberStyle", 2, true));

@ -14,6 +14,7 @@
<AssemblyName>NzbDrone.Web</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
<TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress>
</PropertyGroup>
@ -27,6 +28,8 @@
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<PublishDatabases>false</PublishDatabases>
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -35,6 +38,8 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
</PropertyGroup>
<ItemGroup>
<Reference Include="Ninject, Version=2.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
@ -186,6 +191,7 @@
<Compile Include="Controllers\ApiController.cs" />
<Compile Include="Controllers\HistoryController.cs" />
<Compile Include="Controllers\LogController.cs" />
<Compile Include="Controllers\AddSeriesController.cs" />
<Compile Include="Controllers\NotificationController.cs" />
<Compile Include="Controllers\SeriesController.cs" />
<Compile Include="Controllers\SettingsController.cs" />
@ -600,19 +606,17 @@
<Content Include="Scripts\jquery-tgc-countdown-1.0.js" />
<Content Include="Scripts\jquery.simpledropdown.js" />
<Content Include="Scripts\Notification.js" />
<Content Include="Views\AddSeries\AddSeriesItem.ascx" />
<Content Include="Views\History\Index.aspx" />
<Content Include="Views\Home\Test.aspx" />
<Content Include="Views\Log\Index.aspx" />
<Content Include="Views\Series\AddExisting.aspx" />
<Content Include="Views\Series\AddExistingManual.aspx" />
<Content Include="Views\Series\AddNew.aspx" />
<Content Include="Views\AddSeries\AddExisting.aspx" />
<Content Include="Views\AddSeries\AddExistingManual.aspx" />
<Content Include="Views\AddSeries\AddNew.aspx" />
<Content Include="Views\Series\Details.aspx" />
<Content Include="Views\Series\Edit.aspx" />
<Content Include="Views\Series\EpisodeDetail.ascx" />
<Content Include="Views\Series\index.aspx" />
<Content Include="Views\Series\SeriesSearchResults.ascx" />
<Content Include="Views\Series\SubMenu.ascx" />
<Content Include="Views\Series\Unmapped.aspx" />
<Content Include="Views\Settings\RootDir.ascx" />
<Content Include="Views\Settings\Notifications.ascx" />
<Content Include="Views\Settings\Downloads.ascx" />
@ -642,14 +646,11 @@
<Content Include="Scripts\MicrosoftMvcAjax.debug.js" />
<Content Include="Scripts\MicrosoftMvcValidation.js" />
<Content Include="Scripts\MicrosoftMvcValidation.debug.js" />
<Content Include="Views\Home\About.aspx" />
<Content Include="Views\Home\Index.aspx" />
<Content Include="Views\Shared\Error.aspx" />
<Content Include="Views\Shared\Site.Master" />
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
<Folder Include="Views\Series\DisplayTemplates\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Core\NzbDrone.Core.csproj">

@ -0,0 +1,17 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<String>>" %>
<%@ 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="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%: Html.DropDownList("qualityProfileId", (SelectList)ViewData["QualitySelectList"], ViewData["QualityProfileId"])%>
<%
foreach (var path in Model)
{
Html.RenderAction("RenderPartial", "AddSeries", new { path });
}
%>
</asp:Content>

@ -11,11 +11,6 @@
</script>
</asp:Content>
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
<%
Html.RenderPartial("SubMenu");
%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div>
@ -86,7 +81,7 @@
var pathTest = $('#series_path').val();
$('#tester').text(pathTest);
$("#addResult").load('<%=Url.Action("AddExistingSeries", "Series") %>', {
$("#addResult").load('<%=Url.Action("AddExistingManual", "AddSeries") %>', {
path: pathTest,
seriesId: checkedSeries,
qualityProfileId: qualityProfileId

@ -11,11 +11,6 @@
});
</script>
</asp:Content>
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
<%
Html.RenderPartial("SubMenu");
%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div style="width: 60%">
<div style="display: inline">
@ -83,7 +78,7 @@
var seriesName = $(id).val();
var qualityProfileId = $("#QualityProfileId").val();
$("#addResult").load('<%=Url.Action("AddNewSeries", "Series") %>', {
$("#addResult").load('<%=Url.Action("AddSeries", "Series") %>', {
dir: checkedDir,
seriesId: checkedSeries,
seriesName: seriesName,

@ -0,0 +1,47 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SelectList>" %>
<div style="position: relative; padding: 10px" id="div_<%:ViewData["guid"] %>">
<div style="position: relative; left: 0; width: 50%;">
<%=ViewData["path"].ToString() %>
</div>
<div style="position: relative; right: 0; width: 50%;">
<%
Html.Telerik().ComboBox()
.Name(ViewData["guid"].ToString())
// .AutoFill(true)
.BindTo(Model)
// .DataBinding(b => b.Ajax().Select("TvDbLookup", "AddSeries"))
.Filterable(f => f.FilterMode(AutoCompleteFilterMode.Contains))
.HighlightFirstMatch(true)
.HtmlAttributes(new { style = "width:70%; align:right" })
.SelectedIndex(0)
.Render();
%>
<button class="listButton" onclick="addSeries('<%:ViewData["guid"] %>','<%= ViewData["javaPath"].ToString()%>' )">
Add</button>
</div>
</div>
<script type="text/javascript" language="javascript">
var addSeriesUrl = '<%= Url.Action("AddSeries", "AddSeries") %>';
function addSeries(guid, path) {
var qualityProfileId = $("#qualityProfileId").val();
var comboBox = $("#" + guid).data("tComboBox");
sendToServer(comboBox.value(), path, qualityProfileId);
$("#div_" + guid).hide();
}
function sendToServer(id, path, quality) {
$.ajax({
type: "POST",
url: addSeriesUrl,
data: jQuery.param({ path: path, seriesId: id, qualityProfileId: quality }),
error: function (req, status, error) {
alert("Sorry! We could not add " + this.name + "at this time. " + error);
}
});
}
</script>

@ -1,12 +0,0 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="aboutTitle" ContentPlaceHolderID="TitleContent" runat="server">
About Us
</asp:Content>
<asp:Content ID="aboutContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>About</h2>
<p>
Put content here.
</p>
</asp:Content>

@ -1,19 +0,0 @@
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Logs
</asp:Content>
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
<%
Html.Telerik().Menu().Name("logMenu").Items(items => items.Add().Text("Clear Logs").Action("Clear", "Log")).Render();
%>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
<%: ViewData["Message"] %></h2>
<p>
To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
http://asp.net/mvc</a>.
</p>
</asp:Content>

@ -1,31 +0,0 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NzbDrone.Web.Models.TestModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Test
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Test</h2>
<% using (Html.BeginForm("Test", "Home", FormMethod.Post, new { id = "form", name = "form" }))
{%>
<div>One: <%=Html.RadioButtonFor(t => t.Number, 1)%></div>
<div>Two: <%=Html.RadioButtonFor(t => t.Number, 2)%></div>
<div>Three: <%=Html.RadioButtonFor(t => t.Number, 3)%></div>
<div>Four: <%=Html.RadioButtonFor(t => t.Number, 4)%></div>
<input type="submit" class="button" value="Save" />
<% } %>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="headerContent" runat="server">
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ActionMenu" runat="server">
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="Scripts" runat="server">
</asp:Content>

@ -1,133 +0,0 @@
<%@ 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">
<script type="text/javascript">
$(document).ready(function () {
$('#mastercheckbox').attr("checked", "checked");
document.getElementById('unmappedGrid').style.display = 'block';
});
function Grid_onRowDataBound(e) {
//the DOM element (<tr>) representing the row which is being databound
var row = e.row;
//the data item - JavaScript object.
var tvDbId = e.dataItem.TvDbId;
$(row).attr('id', 'row_' + tvDbId);
//var info = row.cells[1].text();
//row.cells[1].innerHTML = '<strong>' + dataItem + '</strong>';
//You can use the OnRowDataBound event to customize the way data is presented on the client-side
};
function Grid_onLoad(e) {
$('.t-no-data').text("Loading...");
};
</script>
<%= Html.Label("Quality Profile")%>
<%: Html.DropDownList("qualityProfileId", (SelectList)ViewData["QualitySelectList"], ViewData["QualityProfileId"])%>
<div id="unmappedGrid" style="display: none">
<%
Html.Telerik().Grid<AddExistingSeriesModel>().Name("Unmapped_Series_Folders")
.TableHtmlAttributes(new { id = "UnmappedSeriesGrid" })
.Columns(columns =>
{
columns.Bound(c => c.IsWanted).ClientTemplate("<input type='checkbox' name='<#= Path #>' class='checkedSeries' value='<#= TvDbId #>' checked='true'/>")
.Width(20).Title("<input id='mastercheckbox' type='checkbox' style='margin-left:5px'/>")
.HtmlAttributes(new { style = "text-align:center" });
columns.Bound(c => c.Path).ClientTemplate("<a href=" + Url.Action("AddExistingManual", "Series", new { path = "<#= PathEncoded #>" }) + "><#= Path #></a>")
.Template(c =>
{ %>
<%:Html.ActionLink(c.Path, "AddExistingManual", new { path = c.Path })%>
<% }).Title("Path");
columns.Bound(c => c.TvDbName);
})
.DataBinding(d => d.Ajax().Select("_AjaxUnmappedFoldersGrid", "Series"))
.ClientEvents(events => events.OnRowDataBound("Grid_onRowDataBound"))
.ClientEvents(events => events.OnLoad("Grid_onLoad"))
.Footer(false)
.Render();
%>
<p>
<button class="t.button" onclick="syncSelected ()">
Sync Selected Series</button>
</p>
</div>
<div id="result">
</div>
<script type="text/javascript" language="javascript">
// MasterCheckBox functionality
$('#mastercheckbox').click(function () {
if ($(this).attr('checked')) {
$('.checkedSeries').attr('checked', true);
} else {
$('.checkedSeries').attr('checked', false);
}
});
//Unchecking a 'normal' checkbox should clear the mastercheckbox as well
$(".checkedSeries").live("click", function () {
var numChkBoxes = $('.checkedSeries').length;
var numChkBoxesChecked = $('.checkedSeries:checked').length;
if (numChkBoxes == numChkBoxesChecked & numChkBoxes > 0) {
$('#mastercheckbox').attr('checked', true);
}
else {
$('#mastercheckbox').attr('checked', false);
}
});
//Sync for selected series
function syncSelected() {
var $checkedRecords = $('.checkedSeries:checked');
if ($checkedRecords.length < 1) {
alert("Check one or more series first");
return;
}
var qualityProfileId = $("#qualityProfileId").val();
$checkedRecords.each(function () {
$.ajax(
{
type: "POST",
url: "/Series/SyncSelectedSeries",
data: jQuery.param({ path: this.name, tvdbid: this.value, qualityProfileId: qualityProfileId }),
error: function (req, status, error) {
alert("Sorry! We could not add " + this.name + "at this time");
}
}
);
});
$checkedRecords.each(function () {
var id = "#row_" + this.value;
$(this).attr("checked", false);
$(id).hide();
});
}
</script>
</asp:Content>

@ -1,14 +0,0 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NzbDrone.Core.Repository.Episode>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<%: Model.Overview %>
<%--<%:
Html.Telerik().Grid(Model.Files)
.Name("files_" + Model.EpisodeId)
.Columns(columns =>
{
columns.Bound(c => c.Path);
columns.Bound(c => c.Quality);
columns.Bound(c => c.DateAdded);
})
.Footer(false)
%>--%>

@ -1,12 +1,12 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<%@ Import Namespace="NzbDrone.Web.Controllers" %>
<% Html.Telerik().Menu().Name("telerikGrid").Items(items =>
{
items.Add().Text("Add Series")
.Items(subItem => subItem.Add().Text("New Series").Action("AddNew", "Series"))
.Items(subItem => subItem.Add().Text("Existing Series").Action("AddExisting", "Series"));
.Items(subItem => subItem.Add().Text("New Series").Action<AddSeriesController>(c => c.AddNew()))
.Items(subItem => subItem.Add().Text("Existing Series").Action<AddSeriesController>(c => c.AddExisting()));
items.Add().Text("Start RSS Sync").Action("RssSync", "Series");
items.Add().Text("Rename All").Action("RenameAll", "Series");
items.Add().Text("Start RSS Sync").Action<SeriesController>(c => c.RssSync());
items.Add().Text("Rename All").Action<SeriesController>(c => c.RenameAll());
}).Render();
%>

@ -1,28 +0,0 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<NzbDrone.Web.Models.MappingModel>>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Unmapped
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
The following folders aren't currently mapped to any Series.</h2>
<h3>
Please Re-sync your folders. If the problem persists try renaming your folders to
something more similar to the name of series they contain.</h3>
<%= Html.Telerik().Grid(Model)
.Name("Grid")
.DataKeys(dataKeys => dataKeys.Add(c => c.Id))
.DataBinding(dataBinding => dataBinding
//Server binding
.Ajax()
.Select("UnMapped", "Series")
.Update("Update", "Home")
)
.Columns(columns =>
{
columns.Bound(c => c.Path);
columns.Command(commands => commands.Edit());
})
%>
</asp:Content>

@ -19,14 +19,14 @@
columns.Template(c =>
{
%>
<%:Html.ActionLink(c.Title, "Details", new {seriesId =c.SeriesId}) %>
<%:Html.ActionLink(c.Title??"New Series", "Details", new {seriesId =c.SeriesId}) %>
<%
}).Title("Title");
columns.Bound(o => o.Seasons.Count).Title("Seasons");
columns.Bound(o => o.QualityProfile.Name).Title("Quality");
columns.Bound(o => o.Status);
columns.Bound(o => o.AirsDayOfWeek);
columns.Bound(o => o.Path);
}).Title("Title");
columns.Bound(o => o.Seasons.Count).Title("Seasons");
columns.Bound(o => o.QualityProfile.Name).Title("Quality");
columns.Bound(o => o.Status);
columns.Bound(o => o.AirsDayOfWeek);
columns.Bound(o => o.Path);
})
.Sortable(sort => sort.OrderBy(order => order.Add(o => o.Title).Ascending()).Enabled(false))

Loading…
Cancel
Save