2013-09-15 06:49:58 +03:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2013-04-15 04:41:39 +03:00
|
|
|
using System.Linq;
|
|
|
|
using NLog;
|
2013-09-14 03:41:14 +03:00
|
|
|
using NzbDrone.Common;
|
2014-01-06 09:20:08 +03:00
|
|
|
using NzbDrone.Common.Disk;
|
2013-09-15 06:49:58 +03:00
|
|
|
using NzbDrone.Core.DataAugmentation.Scene;
|
|
|
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
2013-04-15 04:41:39 +03:00
|
|
|
using NzbDrone.Core.Parser.Model;
|
|
|
|
using NzbDrone.Core.Tv;
|
|
|
|
|
|
|
|
namespace NzbDrone.Core.Parser
|
|
|
|
{
|
|
|
|
public interface IParsingService
|
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
2014-01-07 11:24:50 +03:00
|
|
|
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series);
|
2013-07-23 08:50:32 +03:00
|
|
|
LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource);
|
2013-04-15 04:41:39 +03:00
|
|
|
Series GetSeries(string title);
|
2013-09-15 06:49:58 +03:00
|
|
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
2013-09-15 11:23:54 +03:00
|
|
|
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public class ParsingService : IParsingService
|
|
|
|
{
|
|
|
|
private readonly IEpisodeService _episodeService;
|
|
|
|
private readonly ISeriesService _seriesService;
|
2013-09-14 03:41:14 +03:00
|
|
|
private readonly IDiskProvider _diskProvider;
|
2013-09-15 06:49:58 +03:00
|
|
|
private readonly ISceneMappingService _sceneMappingService;
|
2013-04-15 04:41:39 +03:00
|
|
|
private readonly Logger _logger;
|
|
|
|
|
2013-09-14 03:41:14 +03:00
|
|
|
public ParsingService(IEpisodeService episodeService,
|
|
|
|
ISeriesService seriesService,
|
|
|
|
IDiskProvider diskProvider,
|
2013-09-15 06:49:58 +03:00
|
|
|
ISceneMappingService sceneMappingService,
|
2013-09-14 03:41:14 +03:00
|
|
|
Logger logger)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
|
|
|
_episodeService = episodeService;
|
|
|
|
_seriesService = seriesService;
|
2013-09-14 03:41:14 +03:00
|
|
|
_diskProvider = diskProvider;
|
2013-09-15 06:49:58 +03:00
|
|
|
_sceneMappingService = sceneMappingService;
|
2013-04-15 04:41:39 +03:00
|
|
|
_logger = logger;
|
|
|
|
}
|
|
|
|
|
2014-01-08 08:54:23 +03:00
|
|
|
public ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
if (searchCriteria != null)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
var tvdbId = _sceneMappingService.GetTvDbId(title);
|
|
|
|
if (tvdbId.HasValue)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
if (searchCriteria.Series.TvdbId == tvdbId)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
return ParseSpecialEpisodeTitle(title, searchCriteria.Series);
|
2014-01-07 11:24:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-08 08:54:23 +03:00
|
|
|
if (tvRageId == searchCriteria.Series.TvRageId)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
return ParseSpecialEpisodeTitle(title, searchCriteria.Series);
|
2014-01-07 11:24:50 +03:00
|
|
|
}
|
|
|
|
}
|
2014-01-08 08:54:23 +03:00
|
|
|
|
|
|
|
var series = _seriesService.FindByTitleInexact(title);
|
|
|
|
if (series == null && tvRageId > 0)
|
2014-01-07 11:24:50 +03:00
|
|
|
{
|
2014-01-08 08:54:23 +03:00
|
|
|
series = _seriesService.FindByTvRageId(tvRageId);
|
2014-01-07 11:24:50 +03:00
|
|
|
}
|
|
|
|
|
2014-01-08 08:54:23 +03:00
|
|
|
if (series == null)
|
|
|
|
{
|
|
|
|
_logger.Trace("No matching series {0}", title);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ParseSpecialEpisodeTitle(title, series);
|
2014-01-07 11:24:50 +03:00
|
|
|
}
|
|
|
|
|
2014-01-08 08:54:23 +03:00
|
|
|
public ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series)
|
|
|
|
{
|
|
|
|
// find special episode in series season 0
|
|
|
|
var episode = _episodeService.FindEpisodeByName(series.Id, 0, title);
|
|
|
|
if (episode != null)
|
|
|
|
{
|
|
|
|
// create parsed info from tv episode
|
|
|
|
var info = new ParsedEpisodeInfo();
|
|
|
|
info.SeriesTitle = series.Title;
|
|
|
|
info.SeriesTitleInfo = new SeriesTitleInfo();
|
|
|
|
info.SeriesTitleInfo.Title = info.SeriesTitle;
|
|
|
|
info.SeasonNumber = episode.SeasonNumber;
|
|
|
|
info.EpisodeNumbers = new int[1] { episode.EpisodeNumber };
|
|
|
|
info.FullSeason = false;
|
|
|
|
info.Quality = QualityParser.ParseQuality(title);
|
|
|
|
info.ReleaseGroup = Parser.ParseReleaseGroup(title);
|
|
|
|
|
|
|
|
_logger.Info("Found special episode {0} for title '{1}'", info, title);
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2014-01-07 11:24:50 +03:00
|
|
|
|
|
|
|
|
2013-07-23 08:50:32 +03:00
|
|
|
public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
2013-07-13 11:11:04 +03:00
|
|
|
var parsedEpisodeInfo = Parser.ParsePath(filename);
|
2013-04-15 04:41:39 +03:00
|
|
|
|
2014-01-07 11:24:50 +03:00
|
|
|
// do we have a possible special episode?
|
|
|
|
if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode())
|
|
|
|
{
|
|
|
|
// try to parse as a special episode
|
|
|
|
var title = System.IO.Path.GetFileNameWithoutExtension(filename);
|
|
|
|
var specialEpisodeInfo = ParseSpecialEpisodeTitle(title, series);
|
|
|
|
if (specialEpisodeInfo != null)
|
|
|
|
{
|
|
|
|
// use special episode
|
|
|
|
parsedEpisodeInfo = specialEpisodeInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-23 09:14:55 +03:00
|
|
|
if (parsedEpisodeInfo == null)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2013-07-23 08:50:32 +03:00
|
|
|
var episodes = GetEpisodes(parsedEpisodeInfo, series, sceneSource);
|
2013-04-15 04:41:39 +03:00
|
|
|
|
|
|
|
if (!episodes.Any())
|
|
|
|
{
|
2013-11-28 23:53:30 +03:00
|
|
|
_logger.Trace("No matching episodes found for: {0}", parsedEpisodeInfo);
|
2013-04-15 04:41:39 +03:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new LocalEpisode
|
2013-07-23 08:50:32 +03:00
|
|
|
{
|
|
|
|
Series = series,
|
|
|
|
Quality = parsedEpisodeInfo.Quality,
|
|
|
|
Episodes = episodes,
|
|
|
|
Path = filename,
|
2013-09-14 03:41:14 +03:00
|
|
|
ParsedEpisodeInfo = parsedEpisodeInfo,
|
2014-01-27 03:09:44 +03:00
|
|
|
ExistingFile = DiskProviderBase.IsParent(series.Path, filename)
|
2013-07-23 08:50:32 +03:00
|
|
|
};
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public Series GetSeries(string title)
|
|
|
|
{
|
2013-04-23 09:14:55 +03:00
|
|
|
var parsedEpisodeInfo = Parser.ParseTitle(title);
|
2013-04-18 02:32:53 +03:00
|
|
|
|
2013-11-01 02:50:39 +03:00
|
|
|
if (parsedEpisodeInfo == null)
|
|
|
|
{
|
|
|
|
return _seriesService.FindByTitle(title);
|
|
|
|
}
|
|
|
|
|
|
|
|
var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
|
|
|
|
|
|
|
|
if (series == null)
|
2013-04-18 02:32:53 +03:00
|
|
|
{
|
2013-11-01 02:50:39 +03:00
|
|
|
series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitleInfo.TitleWithoutYear,
|
|
|
|
parsedEpisodeInfo.SeriesTitleInfo.Year);
|
2013-04-18 02:32:53 +03:00
|
|
|
}
|
|
|
|
|
2013-11-01 02:50:39 +03:00
|
|
|
return series;
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
|
2013-09-15 06:49:58 +03:00
|
|
|
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
2013-04-28 22:46:13 +03:00
|
|
|
var remoteEpisode = new RemoteEpisode
|
|
|
|
{
|
|
|
|
ParsedEpisodeInfo = parsedEpisodeInfo,
|
|
|
|
};
|
2013-04-28 03:25:28 +03:00
|
|
|
|
2013-09-15 06:49:58 +03:00
|
|
|
var series = searchCriteria == null ? GetSeries(parsedEpisodeInfo, tvRageId) :
|
|
|
|
GetSeries(parsedEpisodeInfo, tvRageId, searchCriteria);
|
|
|
|
|
|
|
|
if (series == null)
|
|
|
|
{
|
|
|
|
return remoteEpisode;
|
|
|
|
}
|
|
|
|
|
|
|
|
remoteEpisode.Series = series;
|
|
|
|
remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true, searchCriteria);
|
|
|
|
|
|
|
|
return remoteEpisode;
|
|
|
|
}
|
|
|
|
|
2013-09-15 11:23:54 +03:00
|
|
|
public List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
|
|
|
var result = new List<Episode>();
|
|
|
|
|
2013-12-23 04:32:50 +03:00
|
|
|
if (parsedEpisodeInfo.FullSeason)
|
|
|
|
{
|
|
|
|
return _episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber);
|
|
|
|
}
|
|
|
|
|
2013-10-23 08:17:02 +03:00
|
|
|
if (parsedEpisodeInfo.IsDaily())
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
|
|
|
if (series.SeriesType == SeriesTypes.Standard)
|
|
|
|
{
|
2013-07-05 08:17:25 +03:00
|
|
|
_logger.Warn("Found daily-style episode for non-daily series: {0}.", series);
|
2013-11-27 10:48:57 +03:00
|
|
|
return result;
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
2013-09-15 06:49:58 +03:00
|
|
|
|
2013-10-23 08:17:02 +03:00
|
|
|
var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, searchCriteria);
|
2013-04-15 04:41:39 +03:00
|
|
|
|
|
|
|
if (episodeInfo != null)
|
|
|
|
{
|
|
|
|
result.Add(episodeInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-11-08 03:24:09 +03:00
|
|
|
if (parsedEpisodeInfo.IsAbsoluteNumbering())
|
|
|
|
{
|
|
|
|
foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers)
|
|
|
|
{
|
|
|
|
var episodeInfo = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber);
|
|
|
|
|
|
|
|
if (episodeInfo != null)
|
|
|
|
{
|
2013-11-08 22:03:01 +03:00
|
|
|
_logger.Info("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}",
|
2013-11-08 03:24:09 +03:00
|
|
|
absoluteEpisodeNumber,
|
|
|
|
series.Title,
|
|
|
|
episodeInfo.SeasonNumber,
|
|
|
|
episodeInfo.EpisodeNumber);
|
|
|
|
result.Add(episodeInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-04-23 09:14:55 +03:00
|
|
|
if (parsedEpisodeInfo.EpisodeNumbers == null)
|
2013-04-15 04:41:39 +03:00
|
|
|
return result;
|
|
|
|
|
2013-04-23 09:14:55 +03:00
|
|
|
foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
|
|
|
Episode episodeInfo = null;
|
|
|
|
|
2013-07-23 08:50:32 +03:00
|
|
|
if (series.UseSceneNumbering && sceneSource)
|
2013-04-15 04:41:39 +03:00
|
|
|
{
|
2013-09-15 06:49:58 +03:00
|
|
|
if (searchCriteria != null)
|
|
|
|
{
|
|
|
|
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber &&
|
|
|
|
e.SceneEpisodeNumber == episodeNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (episodeInfo == null)
|
|
|
|
{
|
|
|
|
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true);
|
|
|
|
}
|
2013-07-23 08:50:32 +03:00
|
|
|
|
|
|
|
if (episodeInfo != null)
|
|
|
|
{
|
|
|
|
_logger.Info("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}x{4:00}",
|
|
|
|
series.Title,
|
|
|
|
episodeInfo.SceneSeasonNumber,
|
|
|
|
episodeInfo.SceneEpisodeNumber,
|
|
|
|
episodeInfo.SeasonNumber,
|
|
|
|
episodeInfo.EpisodeNumber);
|
|
|
|
}
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
|
2013-09-15 06:49:58 +03:00
|
|
|
if (episodeInfo == null && searchCriteria != null)
|
|
|
|
{
|
|
|
|
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber &&
|
|
|
|
e.EpisodeNumber == episodeNumber);
|
|
|
|
}
|
|
|
|
|
2013-04-15 04:41:39 +03:00
|
|
|
if (episodeInfo == null)
|
|
|
|
{
|
2013-09-15 11:23:54 +03:00
|
|
|
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (episodeInfo != null)
|
|
|
|
{
|
|
|
|
result.Add(episodeInfo);
|
|
|
|
}
|
2013-09-15 06:49:58 +03:00
|
|
|
|
2013-04-15 04:41:39 +03:00
|
|
|
else
|
|
|
|
{
|
2013-04-23 09:14:55 +03:00
|
|
|
_logger.Debug("Unable to find {0}", parsedEpisodeInfo);
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2013-09-15 06:49:58 +03:00
|
|
|
|
2013-09-15 11:23:54 +03:00
|
|
|
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria)
|
2013-11-27 10:48:57 +03:00
|
|
|
{
|
2013-09-15 11:23:54 +03:00
|
|
|
var tvdbId = _sceneMappingService.GetTvDbId(parsedEpisodeInfo.SeriesTitle);
|
|
|
|
|
|
|
|
if (tvdbId.HasValue)
|
|
|
|
{
|
|
|
|
if (searchCriteria.Series.TvdbId == tvdbId)
|
|
|
|
{
|
|
|
|
return searchCriteria.Series;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle)
|
|
|
|
{
|
|
|
|
return searchCriteria.Series;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tvRageId == searchCriteria.Series.TvRageId)
|
|
|
|
{
|
|
|
|
//TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import
|
|
|
|
return searchCriteria.Series;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetSeries(parsedEpisodeInfo, tvRageId);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId)
|
|
|
|
{
|
|
|
|
var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
|
|
|
|
|
|
|
|
if (series == null && tvRageId > 0)
|
|
|
|
{
|
|
|
|
//TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import
|
|
|
|
series = _seriesService.FindByTvRageId(tvRageId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (series == null)
|
|
|
|
{
|
|
|
|
_logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return series;
|
|
|
|
}
|
|
|
|
|
2013-10-23 08:17:02 +03:00
|
|
|
private Episode GetDailyEpisode(Series series, String airDate, SearchCriteriaBase searchCriteria)
|
2013-09-15 06:49:58 +03:00
|
|
|
{
|
|
|
|
Episode episodeInfo = null;
|
|
|
|
|
|
|
|
if (searchCriteria != null)
|
|
|
|
{
|
|
|
|
episodeInfo = searchCriteria.Episodes.SingleOrDefault(
|
2013-10-23 08:17:02 +03:00
|
|
|
e => e.AirDate == airDate);
|
2013-09-15 06:49:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (episodeInfo == null)
|
|
|
|
{
|
2013-09-15 11:23:54 +03:00
|
|
|
episodeInfo = _episodeService.FindEpisode(series.Id, airDate);
|
2013-09-15 06:49:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return episodeInfo;
|
|
|
|
}
|
2013-04-15 04:41:39 +03:00
|
|
|
}
|
|
|
|
}
|