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.Core.Repository.Quality; using NzbDrone.Web.Models; using Telerik.Web.Mvc; using TvdbLib.Data; using EpisodeModel = NzbDrone.Web.Models.EpisodeModel; namespace NzbDrone.Web.Controllers { [HandleError] public class SeriesController : Controller { private readonly ISeriesProvider _seriesProvider; private readonly IEpisodeProvider _episodeProvider; private readonly ISyncProvider _syncProvider; private readonly IRssSyncProvider _rssSyncProvider; private readonly IQualityProvider _qualityProvider; private readonly IMediaFileProvider _mediaFileProvider; private readonly IRenameProvider _renameProvider; private readonly IRootDirProvider _rootDirProvider; private readonly ITvDbProvider _tvDbProvider; private readonly IDiskProvider _diskProvider; private readonly IConfigProvider _configProvider; // // GET: /Series/ public SeriesController(ISyncProvider syncProvider, ISeriesProvider seriesProvider, IEpisodeProvider episodeProvider, IRssSyncProvider rssSyncProvider, IQualityProvider qualityProvider, IMediaFileProvider mediaFileProvider, IRenameProvider renameProvider, IRootDirProvider rootDirProvider, ITvDbProvider tvDbProvider, IDiskProvider diskProvider, IConfigProvider configProvider) { _seriesProvider = seriesProvider; _episodeProvider = episodeProvider; _syncProvider = syncProvider; _rssSyncProvider = rssSyncProvider; _qualityProvider = qualityProvider; _mediaFileProvider = mediaFileProvider; _renameProvider = renameProvider; _rootDirProvider = rootDirProvider; _tvDbProvider = tvDbProvider; _diskProvider = diskProvider; _configProvider = configProvider; } public ActionResult Index() { ViewData.Model = _seriesProvider.GetAllSeries().ToList(); 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() { _rssSyncProvider.Begin(); return RedirectToAction("Index"); } public ActionResult UnMapped(string path) { return View(_syncProvider.GetUnmappedFolders(path).Select(c => new MappingModel() { Id = 1, Path = c }).ToList()); } public ActionResult LoadEpisodes(int seriesId) { _episodeProvider.RefreshEpisodeInfo(seriesId); return RedirectToAction("Details", new { seriesId = seriesId }); } [GridAction] public ActionResult _AjaxSeasonGrid(int seasonId) { var episodes = _episodeProvider.GetEpisodeBySeason(seasonId).Select(c => new EpisodeModel() { EpisodeId = c.EpisodeId, EpisodeNumber = c.EpisodeNumber, SeasonNumber = c.SeasonNumber, Title = c.Title, Overview = c.Overview, AirDate = c.AirDate, Path = GetEpisodePath(c.EpisodeFile), Quality = c.EpisodeFile == null ? String.Empty : c.EpisodeFile.Quality.ToString() }); return View(new GridModel(episodes)); } [GridAction] public ActionResult _CustomBinding(GridCommand command, int seasonId) { IEnumerable data = GetData(command); return View(new GridModel { Data = data, Total = data.Count() }); } [GridAction] public ActionResult _AjaxUnmappedFoldersGrid() { var unmappedList = new List(); foreach (var folder in _rootDirProvider.GetAll()) { 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, PathEncoded = Url.Encode(unmappedFolder), TvDbId = tvDbSeries.Id, TvDbName = tvDbSeries.SeriesName }); } } return View(new GridModel(unmappedList)); } public ActionResult SyncSelectedSeries(List checkedRecords) { 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"])); var qualityProfileId = Convert.ToInt32(HttpUtility.UrlDecode(nvc["qualityProfileId"])); //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, QualityProfileId = qualityProfileId }); } if (_syncProvider.BeginSyncUnmappedFolders(unmappedList)) return Content("Sync Started for Selected Series"); return Content("Sync already in progress, please wait for it to complete before retrying."); } 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) { var model = new List(); //Get Results from TvDb and convert them to something we can use. foreach (var tvdbSearchResult in _tvDbProvider.SearchSeries(seriesName)) { model.Add(new SeriesSearchResultModel { TvDbId = tvdbSearchResult.Id, TvDbName = tvdbSearchResult.SeriesName, FirstAired = tvdbSearchResult.FirstAired }); } //model.Add(new SeriesSearchResultModel{ TvDbId = 12345, TvDbName = "30 Rock", FirstAired = DateTime.Today }); //model.Add(new SeriesSearchResultModel { TvDbId = 65432, TvDbName = "The Office (US)", FirstAired = DateTime.Today.AddDays(-100) }); return PartialView("SeriesSearchResults", model); } private IEnumerable GetData(GridCommand command) { return null; /* IQueryable data = .Orders; //Apply filtering if (command.FilterDescriptors.Any()) { data = data.Where(ExpressionBuilder.Expression(command.FilterDescriptors)); } // Apply sorting foreach (SortDescriptor sortDescriptor in command.SortDescriptors) { if (sortDescriptor.SortDirection == ListSortDirection.Ascending) { switch (sortDescriptor.Member) { case "OrderID": data = data.OrderBy(ExpressionBuilder.Expression(sortDescriptor.Member)); break; case "Customer.ContactName": data = data.OrderBy(order => order.Customer.ContactName); break; case "ShipAddress": data = data.OrderBy(order => order.ShipAddress); break; case "OrderDate": data = data.OrderBy(order => order.OrderDate); break; } } else { switch (sortDescriptor.Member) { case "OrderID": data = data.OrderByDescending(order => order.OrderID); break; case "Customer.ContactName": data = data.OrderByDescending(order => order.Customer.ContactName); break; case "ShipAddress": data = data.OrderByDescending(order => order.ShipAddress); break; case "OrderDate": data = data.OrderByDescending(order => order.OrderDate); break; } } } count = data.Count(); // ... and paging if (command.PageSize > 0) { data = data.Skip((command.Page - 1) * command.PageSize); } data = data.Take(command.PageSize); return data;*/ } [AcceptVerbs(HttpVerbs.Post)] [GridAction] public ActionResult _SaveAjaxEditing(string id) { return RedirectToAction("UnMapped"); } public ActionResult Details(int seriesId) { var series = _seriesProvider.GetSeries(seriesId); return View(series); } public ActionResult Edit(int seriesId) { var profiles = _qualityProvider.GetAllProfiles(); ViewData["SelectList"] = new SelectList(profiles, "QualityProfileId", "Name"); var series = _seriesProvider.GetSeries(seriesId); return View(series); } [HttpPost] public ActionResult Edit(Series series) { //Need to add seriesProvider.Update _seriesProvider.UpdateSeries(series); return Content("Series Updated Successfully"); } public ActionResult Delete(int seriesId) { //Need to add seriesProvider.Update _seriesProvider.DeleteSeries(seriesId); return RedirectToAction("Index", "Series"); } public ActionResult SyncEpisodesOnDisk(int seriesId) { //Syncs the episodes on disk for the specified series var series = _seriesProvider.GetSeries(seriesId); _mediaFileProvider.Scan(series); return RedirectToAction("Details", new { seriesId }); } public ActionResult RenameAll() { _renameProvider.RenameAll(); return RedirectToAction("Index"); } public ActionResult RenameSeries(int seriesId) { _renameProvider.RenameSeries(seriesId); return RedirectToAction("Details", new { seriesId }); } public ActionResult RenameSeason(int seasonId) { //Todo: Stay of Series Detail... AJAX? _renameProvider.RenameSeason(seasonId); return RedirectToAction("Index"); } public ActionResult RenameEpisode(int episodeId) { //Todo: Stay of Series Detail... AJAX? _renameProvider.RenameEpisode(episodeId); return RedirectToAction("Index"); } //Local Helpers private string GetEpisodePath(EpisodeFile file) { if (file == null) return String.Empty; //Return the path relative to the Series' Folder return file.Path.Replace(file.Series.Path, "").Trim(Path.DirectorySeparatorChar); } } }