much nicer add new series.

pull/3113/head
kay.one 12 years ago
parent a052a9389e
commit 4be637edff

@ -21,7 +21,7 @@ namespace NzbDrone.Api.Series
private readonly ISeriesRepository _seriesRepository; private readonly ISeriesRepository _seriesRepository;
private readonly IJobController _jobProvider; private readonly IJobController _jobProvider;
public SeriesModule(ISeriesService seriesService,ISeriesRepository seriesRepository, IJobController jobProvider) public SeriesModule(ISeriesService seriesService, ISeriesRepository seriesRepository, IJobController jobProvider)
: base("/Series") : base("/Series")
{ {
_seriesService = seriesService; _seriesService = seriesService;
@ -53,14 +53,14 @@ namespace NzbDrone.Api.Series
private Response AddSeries() private Response AddSeries()
{ {
var request = Request.Body.FromJson<Core.Tv.Series>(); var newSeries = Request.Body.FromJson<Core.Tv.Series>();
//Todo: Alert the user if this series already exists //Todo: Alert the user if this series already exists
//Todo: We need to create the folder if the user is adding a new series //Todo: We need to create the folder if the user is adding a new series
//(we can just create the folder and it won't blow up if it already exists) //(we can just create the folder and it won't blow up if it already exists)
//We also need to remove any special characters from the filename before attempting to create it //We also need to remove any special characters from the filename before attempting to create it
_seriesService.AddSeries(request.Title, request.Path, request.TvDbId, request.QualityProfileId, null); _seriesService.AddSeries(newSeries);
return new Response { StatusCode = HttpStatusCode.Created }; return new Response { StatusCode = HttpStatusCode.Created };
} }

@ -118,12 +118,12 @@ namespace NzbDrone.Core.Datastore.Migration
.WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("TvdbId").AsInt32().NotNullable() .WithColumn("TvdbId").AsInt32().NotNullable()
.WithColumn("Title").AsString().NotNullable() .WithColumn("Title").AsString().NotNullable()
.WithColumn("TitleSlug").AsString().NotNullable()
.WithColumn("CleanTitle").AsString().NotNullable() .WithColumn("CleanTitle").AsString().NotNullable()
.WithColumn("Status").AsInt32().NotNullable() .WithColumn("Status").AsInt32().NotNullable()
.WithColumn("Overview").AsString().Nullable() .WithColumn("Overview").AsString().Nullable()
.WithColumn("AirTime").AsString().Nullable() .WithColumn("AirTime").AsString().Nullable()
.WithColumn("Images").AsString() .WithColumn("Images").AsString()
.WithColumn("Language").AsString().NotNullable()
.WithColumn("Path").AsString().NotNullable() .WithColumn("Path").AsString().NotNullable()
.WithColumn("Monitored").AsBoolean().NotNullable() .WithColumn("Monitored").AsBoolean().NotNullable()
.WithColumn("QualityProfileId").AsString().NotNullable() .WithColumn("QualityProfileId").AsString().NotNullable()

@ -5,9 +5,10 @@ namespace NzbDrone.Core.MediaCover
public enum MediaCoverTypes public enum MediaCoverTypes
{ {
Poster = 0, Unknown = 0,
Banner = 1, Poster = 1,
Fanart = 2 Banner = 2,
Fanart = 3
} }
public class MediaCover : IEmbeddedDocument public class MediaCover : IEmbeddedDocument

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Eventing; using NzbDrone.Common.Eventing;
@ -19,8 +18,6 @@ namespace NzbDrone.Core.MediaCover
private readonly string _coverRootFolder; private readonly string _coverRootFolder;
private const string COVER_URL_PREFIX = "http://www.thetvdb.com/banners/";
public MediaCoverService(HttpProvider httpProvider, DiskProvider diskProvider, EnvironmentProvider environmentProvider, Logger logger) public MediaCoverService(HttpProvider httpProvider, DiskProvider diskProvider, EnvironmentProvider environmentProvider, Logger logger)
{ {
_httpProvider = httpProvider; _httpProvider = httpProvider;
@ -54,7 +51,7 @@ namespace NzbDrone.Core.MediaCover
var fileName = GetCoverPath(series.Id, cover.CoverType); var fileName = GetCoverPath(series.Id, cover.CoverType);
_logger.Info("Downloading {0} for {1}", cover.CoverType, series.Title); _logger.Info("Downloading {0} for {1}", cover.CoverType, series.Title);
_httpProvider.DownloadFile(COVER_URL_PREFIX + cover.Url, fileName); _httpProvider.DownloadFile(cover.Url, fileName);
} }
catch (Exception e) catch (Exception e)
{ {

@ -56,6 +56,7 @@ namespace NzbDrone.Core.MetadataSource
series.Runtime = show.runtime; series.Runtime = show.runtime;
series.Network = show.network; series.Network = show.network;
series.AirTime = show.air_time; series.AirTime = show.air_time;
series.TitleSlug = show.url.ToLower().Replace("http://trakt.tv/show/", "");
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner, Url = show.images.banner }); series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner, Url = show.images.banner });
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Poster, Url = show.images.poster }); series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Poster, Url = show.images.poster });

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Tv
{ {
public Series() public Series()
{ {
Images =new List<MediaCover.MediaCover>(); Images = new List<MediaCover.MediaCover>();
} }
public int TvDbId { get; set; } public int TvDbId { get; set; }
@ -28,7 +28,6 @@ namespace NzbDrone.Core.Tv
public SeriesStatusType Status { get; set; } public SeriesStatusType Status { get; set; }
public string Overview { get; set; } public string Overview { get; set; }
public String AirTime { get; set; } public String AirTime { get; set; }
public string Language { get; set; }
public string Path { get; set; } public string Path { get; set; }
public bool Monitored { get; set; } public bool Monitored { get; set; }
public int QualityProfileId { get; set; } public int QualityProfileId { get; set; }
@ -43,7 +42,7 @@ namespace NzbDrone.Core.Tv
public DateTime? CustomStartDate { get; set; } public DateTime? CustomStartDate { get; set; }
public bool UseSceneNumbering { get; set; } public bool UseSceneNumbering { get; set; }
public int TvRageId { get; set; } public int TvRageId { get; set; }
public string TvRageTitle { get; set; } public string TitleSlug { get; set; }
//Todo: This should be a double since there are timezones that aren't on a full hour offset //Todo: This should be a double since there are timezones that aren't on a full hour offset
public int UtcOffset { get; set; } public int UtcOffset { get; set; }

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Tv
bool IsMonitored(int id); bool IsMonitored(int id);
Series UpdateSeriesInfo(int seriesId); Series UpdateSeriesInfo(int seriesId);
Series FindSeries(string title); Series FindSeries(string title);
void AddSeries(string title, string path, int tvDbSeriesId, int qualityProfileId, DateTime? airedAfter); void AddSeries(Series newSeries);
void UpdateFromSeriesEditor(IList<Series> editedSeries); void UpdateFromSeriesEditor(IList<Series> editedSeries);
Series FindByTvdbId(int tvdbId); Series FindByTvdbId(int tvdbId);
void SetSeriesType(int seriesId, SeriesTypes seriesTypes); void SetSeriesType(int seriesId, SeriesTypes seriesTypes);
@ -67,8 +67,7 @@ namespace NzbDrone.Core.Tv
series.AirTime = tvDbSeries.AirTime; series.AirTime = tvDbSeries.AirTime;
series.Overview = tvDbSeries.Overview; series.Overview = tvDbSeries.Overview;
series.Status = tvDbSeries.Status; series.Status = tvDbSeries.Status;
series.Language = tvDbSeries.Language; series.CleanTitle = Parser.NormalizeTitle(tvDbSeries.Title);
series.CleanTitle = tvDbSeries.CleanTitle;
series.LastInfoSync = DateTime.Now; series.LastInfoSync = DateTime.Now;
series.Runtime = tvDbSeries.Runtime; series.Runtime = tvDbSeries.Runtime;
series.Images = tvDbSeries.Images; series.Images = tvDbSeries.Images;
@ -95,37 +94,22 @@ namespace NzbDrone.Core.Tv
return _seriesRepository.GetByTitle(normalizeTitle); return _seriesRepository.GetByTitle(normalizeTitle);
} }
public void AddSeries(string title, string path, int tvDbSeriesId, int qualityProfileId, DateTime? airedAfter) public void AddSeries(Series newSeries)
{ {
_logger.Info("Adding Series [{0}] Path: [{1}]", tvDbSeriesId, path); Ensure.That(() => newSeries).IsNotNull();
Ensure.That(() => tvDbSeriesId).IsGreaterThan(0); _logger.Info("Adding Series [{0}] Path: [{1}]", newSeries.Title, newSeries.Path);
Ensure.That(() => title).IsNotNullOrWhiteSpace();
Ensure.That(() => path).IsNotNullOrWhiteSpace();
var repoSeries = new Series(); newSeries.Monitored = true;
repoSeries.TvDbId = tvDbSeriesId; newSeries.CleanTitle = Parser.NormalizeTitle(newSeries.Title);
repoSeries.Path = path; if (newSeries.QualityProfileId == 0)
repoSeries.Monitored = true; newSeries.QualityProfileId = _configService.DefaultQualityProfile;
repoSeries.QualityProfileId = qualityProfileId;
repoSeries.Title = title;
repoSeries.CleanTitle = Parser.NormalizeTitle(title);
if (qualityProfileId == 0)
repoSeries.QualityProfileId = _configService.DefaultQualityProfile;
repoSeries.QualityProfile = _qualityProfileService.Get(repoSeries.QualityProfileId); newSeries.SeasonFolder = _configService.UseSeasonFolder;
repoSeries.SeasonFolder = _configService.UseSeasonFolder; newSeries.BacklogSetting = BacklogSettingType.Inherit;
repoSeries.BacklogSetting = BacklogSettingType.Inherit;
if (airedAfter.HasValue) _seriesRepository.Insert(newSeries);
repoSeries.CustomStartDate = airedAfter; _eventAggregator.Publish(new SeriesAddedEvent(newSeries));
//Todo: Allow the user to set this as part of the addition process.
repoSeries.Language = "en";
_seriesRepository.Insert(repoSeries);
_eventAggregator.Publish(new SeriesAddedEvent(repoSeries));
} }
public void UpdateFromSeriesEditor(IList<Series> editedSeries) public void UpdateFromSeriesEditor(IList<Series> editedSeries)

@ -8,6 +8,7 @@
<w>rootfolder</w> <w>rootfolder</w>
<w>rootfolders</w> <w>rootfolders</w>
<w>thetvdb</w> <w>thetvdb</w>
<w>trakt</w>
<w>tvdb</w> <w>tvdb</w>
<w>xlarge</w> <w>xlarge</w>
<w>yyyy</w> <w>yyyy</w>

@ -58,6 +58,7 @@
<option name="m_limit" value="3" /> <option name="m_limit" value="3" />
</inspection_tool> </inspection_tool>
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" /> <inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedGlobalSymbols" enabled="false" level="INFO" enabled_by_default="false" /> <inspection_tool class="JSUnusedGlobalSymbols" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="LabeledStatementJS" enabled="true" level="ERROR" enabled_by_default="true" /> <inspection_tool class="LabeledStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">

@ -12,7 +12,7 @@ define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'AddSeries/New/Sear
searchResult: '#search-result' searchResult: '#search-result'
}, },
collection: new NzbDrone.AddSeries.SearchResultCollection(), collection: new NzbDrone.Series.SeriesCollection(),
onRender: function () { onRender: function () {
console.log('binding auto complete'); console.log('binding auto complete');
@ -25,6 +25,7 @@ define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'AddSeries/New/Sear
self.$el.data('timeout', window.setTimeout(self.search, 500, self)); self.$el.data('timeout', window.setTimeout(self.search, 500, self));
}); });
this.collection.url = NzbDrone.Constants.ApiRoot + '/series/lookup';
this.resultView = new NzbDrone.AddSeries.SearchResultView({ collection: this.collection }); this.resultView = new NzbDrone.AddSeries.SearchResultView({ collection: this.collection });
}, },

@ -1,11 +1,13 @@
<div class="accordion-group"> <div class="row">
<div class="accordion-heading"> <div class="span2">
<a href="http://thetvdb.com/?tab=series&id={{tvDbId}}" target="_blank" class="icon-info-sign pull-left"></a> <a href="{{traktUrl}}" target="_blank">
<a class="accordion-toggle" data-toggle="collapse" href="#{{tvDbId}}">{{title}} {{seriesYear}}</a> <img class="series-poster img-polaroid" src="{{banner}}">
</a>
</div> </div>
<div id="{{tvDbId}}" class="accordion-body collapse"> <div class="span9">
<div class="accordion-inner">
<select class="span7 x-root-folder"> <div class="row">
<select class="span6 x-root-folder">
{{#each rootFolders.models}} {{#each rootFolders.models}}
<option value="{{id}}">{{attributes.path}}</option> <option value="{{id}}">{{attributes.path}}</option>
{{/each}} {{/each}}
@ -16,8 +18,13 @@
{{/each}} {{/each}}
</select> </select>
<div class="btn btn-success pull-right icon-plus x-add"> <div class="btn btn-success icon-plus x-add pull-right"/>
</div> </div>
<div class="row">
<h2>{{title}}</h2>
</div>
<div class="row">
{{overview}}
</div> </div>
</div> </div>
</div> </div>

@ -22,32 +22,22 @@ define(['app', 'Shared/NotificationCollection', 'AddSeries/SearchResultCollectio
add: function () { add: function () {
var seriesId = this.model.get('tvDbId');
var title = this.model.get('title');
var quality = this.ui.qualityProfile.val(); var quality = this.ui.qualityProfile.val();
var rootFolderId = this.ui.rootFolder.val();
//Todo: This will create an invalid path on linux... //Todo: This will create an invalid path on linux...
var rootPath = this.model.get('rootFolders').get(rootFolderId).get('path'); var rootPath = this.ui.rootFolder.find(":selected").text();
var path = rootPath + "\\" + title; var path = rootPath + "\\" + this.model.get('title');
var model = new NzbDrone.Series.SeriesModel({ this.model.set('qualityProfileId', quality);
tvdbId : seriesId, this.model.set('path', path);
title : title,
qualityProfileId: quality,
path : path
});
var self = this; var self = this;
var seriesCollection = new NzbDrone.Series.SeriesCollection(); this.model.save(undefined, {
seriesCollection.push(model);
model.save(undefined, {
success: function () { success: function () {
var notificationModel = new NzbDrone.Shared.NotificationModel({ var notificationModel = new NzbDrone.Shared.NotificationModel({
title : 'Added', title : 'Added',
message: title, message: self.model.get('title'),
level : 'success' level : 'success'
}); });

@ -13,6 +13,16 @@ define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'Quality/QualityPro
} else { } else {
return date; return date;
} }
},
banner : function () {
var banner = _.find(this.get('images'), function (image) {
return image.coverType === 1;
});
return banner.url;
},
traktUrl : function () {
return "http://trakt.tv/show/" + this.get('titleSlug');
} }
}, },

@ -16,26 +16,14 @@
} }
.result-list { .result-list {
font-size: 18px; font-size: 14px;
text-align: left; text-align: left;
padding-bottom: 30px;
} }
.result-list .accordion-heading:hover { .search-item {
background-color: #fcf8e3;
}
.search-item .accordion-heading *[class*='icon-'] {
padding-right: 5px; padding-right: 5px;
padding-top: 10px; padding-bottom: 20px;
padding-left: 10px;
}
.search-item .accordion-body .in {
background-color: #fcf8e3;
}
.result-list .result-item a {
font-size: 20px;
} }
.search-item a:hover { .search-item a:hover {
@ -79,3 +67,7 @@
padding-left: 20px; padding-left: 20px;
padding-top: 10px; padding-top: 10px;
} }
.series-poster {
height: 200px;
}

@ -1,4 +1,4 @@
define(['app', 'Quality/QualityProfileCollection'], function (app, qualityProfileCollection) { define(['app', 'Quality/QualityProfileCollection', 'AddSeries/RootFolders/RootFolderCollection'], function (app, qualityProfileCollection, rootFolders) {
NzbDrone.Series.SeriesModel = Backbone.Model.extend({ NzbDrone.Series.SeriesModel = Backbone.Model.extend({
urlRoot: NzbDrone.Constants.ApiRoot + '/series', urlRoot: NzbDrone.Constants.ApiRoot + '/series',
@ -19,13 +19,29 @@
} }
return percent; return percent;
},
banner : function () {
var banner = _.find(this.get('images'), function (image) {
return image.coverType === 1;
});
if (banner) {
return banner.url;
}
return undefined;
},
traktUrl : function () {
return "http://trakt.tv/show/" + this.get('titleSlug');
} }
}, },
defaults: { defaults: {
episodeFileCount: 0, episodeFileCount: 0,
episodeCount : 0, episodeCount : 0,
qualityProfiles : qualityProfileCollection qualityProfiles : qualityProfileCollection,
rootFolders : rootFolders
} }
}); });

Loading…
Cancel
Save