/
SeriesService.cs
230 lines (195 loc) · 8.09 KB
/
SeriesService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Tv
{
public interface ISeriesService
{
Series GetSeries(int seriesId);
List<Series> GetSeries(IEnumerable<int> seriesIds);
Series AddSeries(Series newSeries);
List<Series> AddSeries(List<Series> newSeries);
Series FindByTvdbId(int tvdbId);
Series FindByTvRageId(int tvRageId);
Series FindByTitle(string title);
Series FindByTitle(string title, int year);
Series FindByTitleInexact(string title);
Series FindByPath(string path);
void DeleteSeries(int seriesId, bool deleteFiles);
List<Series> GetAllSeries();
List<Series> AllForTag(int tagId);
Series UpdateSeries(Series series, bool updateEpisodesToMatchSeason = true);
List<Series> UpdateSeries(List<Series> series, bool useExistingRelativeFolder);
bool SeriesPathExists(string folder);
void RemoveAddOptions(Series series);
}
public class SeriesService : ISeriesService
{
private readonly ISeriesRepository _seriesRepository;
private readonly IEventAggregator _eventAggregator;
private readonly IEpisodeService _episodeService;
private readonly IBuildSeriesPaths _seriesPathBuilder;
private readonly Logger _logger;
public SeriesService(ISeriesRepository seriesRepository,
IEventAggregator eventAggregator,
IEpisodeService episodeService,
IBuildSeriesPaths seriesPathBuilder,
Logger logger)
{
_seriesRepository = seriesRepository;
_eventAggregator = eventAggregator;
_episodeService = episodeService;
_seriesPathBuilder = seriesPathBuilder;
_logger = logger;
}
public Series GetSeries(int seriesId)
{
return _seriesRepository.Get(seriesId);
}
public List<Series> GetSeries(IEnumerable<int> seriesIds)
{
return _seriesRepository.Get(seriesIds).ToList();
}
public Series AddSeries(Series newSeries)
{
_seriesRepository.Insert(newSeries);
_eventAggregator.PublishEvent(new SeriesAddedEvent(GetSeries(newSeries.Id)));
return newSeries;
}
public List<Series> AddSeries(List<Series> newSeries)
{
_seriesRepository.InsertMany(newSeries);
_eventAggregator.PublishEvent(new SeriesImportedEvent(newSeries.Select(s => s.Id).ToList()));
return newSeries;
}
public Series FindByTvdbId(int tvRageId)
{
return _seriesRepository.FindByTvdbId(tvRageId);
}
public Series FindByTvRageId(int tvRageId)
{
return _seriesRepository.FindByTvRageId(tvRageId);
}
public Series FindByTitle(string title)
{
return _seriesRepository.FindByTitle(title.CleanSeriesTitle());
}
public Series FindByTitleInexact(string title)
{
// find any series clean title within the provided release title
string cleanTitle = title.CleanSeriesTitle();
var list = _seriesRepository.All().Where(s => cleanTitle.Contains(s.CleanTitle)).ToList();
if (!list.Any())
{
// no series matched
return null;
}
if (list.Count == 1)
{
// return the first series if there is only one
return list.Single();
}
// build ordered list of series by position in the search string
var query =
list.Select(series => new
{
position = cleanTitle.IndexOf(series.CleanTitle),
length = series.CleanTitle.Length,
series = series
})
.Where(s => (s.position>=0))
.ToList()
.OrderBy(s => s.position)
.ThenByDescending(s => s.length)
.ToList();
// get the leftmost series that is the longest
// series are usually the first thing in release title, so we select the leftmost and longest match
var match = query.First().series;
_logger.Debug("Multiple series matched {0} from title {1}", match.Title, title);
foreach (var entry in list)
{
_logger.Debug("Multiple series match candidate: {0} cleantitle: {1}", entry.Title, entry.CleanTitle);
}
return match;
}
public Series FindByPath(string path)
{
return _seriesRepository.FindByPath(path);
}
public Series FindByTitle(string title, int year)
{
return _seriesRepository.FindByTitle(title.CleanSeriesTitle(), year);
}
public void DeleteSeries(int seriesId, bool deleteFiles)
{
var series = _seriesRepository.Get(seriesId);
_seriesRepository.Delete(seriesId);
_eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles));
}
public List<Series> GetAllSeries()
{
return _seriesRepository.All().ToList();
}
public List<Series> AllForTag(int tagId)
{
return GetAllSeries().Where(s => s.Tags.Contains(tagId))
.ToList();
}
// updateEpisodesToMatchSeason is an override for EpisodeMonitoredService to use so a change via Season pass doesn't get nuked by the seasons loop.
// TODO: Remove when seasons are split from series (or we come up with a better way to address this)
public Series UpdateSeries(Series series, bool updateEpisodesToMatchSeason = true)
{
var storedSeries = GetSeries(series.Id);
if (updateEpisodesToMatchSeason)
{
foreach (var season in series.Seasons)
{
var storedSeason = storedSeries.Seasons.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber);
if (storedSeason != null && season.Monitored != storedSeason.Monitored)
{
_episodeService.SetEpisodeMonitoredBySeason(series.Id, season.SeasonNumber, season.Monitored);
}
}
}
// Never update AddOptions when updating a series, keep it the same as the existing stored series.
series.AddOptions = storedSeries.AddOptions;
var updatedSeries = _seriesRepository.Update(series);
_eventAggregator.PublishEvent(new SeriesEditedEvent(updatedSeries, storedSeries));
return updatedSeries;
}
public List<Series> UpdateSeries(List<Series> series, bool useExistingRelativeFolder)
{
_logger.Debug("Updating {0} series", series.Count);
foreach (var s in series)
{
_logger.Trace("Updating: {0}", s.Title);
if (!s.RootFolderPath.IsNullOrWhiteSpace())
{
s.Path = _seriesPathBuilder.BuildPath(s, useExistingRelativeFolder);
_logger.Trace("Changing path for {0} to {1}", s.Title, s.Path);
}
else
{
_logger.Trace("Not changing path for: {0}", s.Title);
}
}
_seriesRepository.UpdateMany(series);
_logger.Debug("{0} series updated", series.Count);
return series;
}
public bool SeriesPathExists(string folder)
{
return _seriesRepository.SeriesPathExists(folder);
}
public void RemoveAddOptions(Series series)
{
_seriesRepository.SetFields(series, s => s.AddOptions);
}
}
}