mirror of
https://github.com/Radarr/Radarr
synced 2025-12-20 23:33:15 +01:00
ParseSpecialEpisode now follows similar pattern to Map() method and accepts TvRageId and SearchCriteria Fixed normalize episode title to handle punctuation separately from spaces and removed special episode words Removed comments
351 lines
No EOL
13 KiB
C#
351 lines
No EOL
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using NLog;
|
|
using NzbDrone.Common;
|
|
using NzbDrone.Core.DataAugmentation.Scene;
|
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
|
using NzbDrone.Core.Parser.Model;
|
|
using NzbDrone.Core.Tv;
|
|
|
|
namespace NzbDrone.Core.Parser
|
|
{
|
|
public interface IParsingService
|
|
{
|
|
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
|
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series);
|
|
LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource);
|
|
Series GetSeries(string title);
|
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
|
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
|
|
}
|
|
|
|
public class ParsingService : IParsingService
|
|
{
|
|
private readonly IEpisodeService _episodeService;
|
|
private readonly ISeriesService _seriesService;
|
|
private readonly IDiskProvider _diskProvider;
|
|
private readonly ISceneMappingService _sceneMappingService;
|
|
private readonly Logger _logger;
|
|
|
|
public ParsingService(IEpisodeService episodeService,
|
|
ISeriesService seriesService,
|
|
IDiskProvider diskProvider,
|
|
ISceneMappingService sceneMappingService,
|
|
Logger logger)
|
|
{
|
|
_episodeService = episodeService;
|
|
_seriesService = seriesService;
|
|
_diskProvider = diskProvider;
|
|
_sceneMappingService = sceneMappingService;
|
|
_logger = logger;
|
|
}
|
|
|
|
public ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
|
{
|
|
if (searchCriteria != null)
|
|
{
|
|
var tvdbId = _sceneMappingService.GetTvDbId(title);
|
|
if (tvdbId.HasValue)
|
|
{
|
|
if (searchCriteria.Series.TvdbId == tvdbId)
|
|
{
|
|
return ParseSpecialEpisodeTitle(title, searchCriteria.Series);
|
|
}
|
|
}
|
|
|
|
if (tvRageId == searchCriteria.Series.TvRageId)
|
|
{
|
|
return ParseSpecialEpisodeTitle(title, searchCriteria.Series);
|
|
}
|
|
}
|
|
|
|
var series = _seriesService.FindByTitleInexact(title);
|
|
if (series == null && tvRageId > 0)
|
|
{
|
|
series = _seriesService.FindByTvRageId(tvRageId);
|
|
}
|
|
|
|
if (series == null)
|
|
{
|
|
_logger.Trace("No matching series {0}", title);
|
|
return null;
|
|
}
|
|
|
|
return ParseSpecialEpisodeTitle(title, series);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource)
|
|
{
|
|
var parsedEpisodeInfo = Parser.ParsePath(filename);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
if (parsedEpisodeInfo == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var episodes = GetEpisodes(parsedEpisodeInfo, series, sceneSource);
|
|
|
|
if (!episodes.Any())
|
|
{
|
|
_logger.Trace("No matching episodes found for: {0}", parsedEpisodeInfo);
|
|
return null;
|
|
}
|
|
|
|
return new LocalEpisode
|
|
{
|
|
Series = series,
|
|
Quality = parsedEpisodeInfo.Quality,
|
|
Episodes = episodes,
|
|
Path = filename,
|
|
ParsedEpisodeInfo = parsedEpisodeInfo,
|
|
ExistingFile = _diskProvider.IsParent(series.Path, filename)
|
|
};
|
|
}
|
|
|
|
public Series GetSeries(string title)
|
|
{
|
|
var parsedEpisodeInfo = Parser.ParseTitle(title);
|
|
|
|
if (parsedEpisodeInfo == null)
|
|
{
|
|
return _seriesService.FindByTitle(title);
|
|
}
|
|
|
|
var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle);
|
|
|
|
if (series == null)
|
|
{
|
|
series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitleInfo.TitleWithoutYear,
|
|
parsedEpisodeInfo.SeriesTitleInfo.Year);
|
|
}
|
|
|
|
return series;
|
|
}
|
|
|
|
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
|
{
|
|
var remoteEpisode = new RemoteEpisode
|
|
{
|
|
ParsedEpisodeInfo = parsedEpisodeInfo,
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
public List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
|
|
{
|
|
var result = new List<Episode>();
|
|
|
|
if (parsedEpisodeInfo.FullSeason)
|
|
{
|
|
return _episodeService.GetEpisodesBySeason(series.Id, parsedEpisodeInfo.SeasonNumber);
|
|
}
|
|
|
|
if (parsedEpisodeInfo.IsDaily())
|
|
{
|
|
if (series.SeriesType == SeriesTypes.Standard)
|
|
{
|
|
_logger.Warn("Found daily-style episode for non-daily series: {0}.", series);
|
|
return result;
|
|
}
|
|
|
|
var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate, searchCriteria);
|
|
|
|
if (episodeInfo != null)
|
|
{
|
|
result.Add(episodeInfo);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if (parsedEpisodeInfo.IsAbsoluteNumbering())
|
|
{
|
|
foreach (var absoluteEpisodeNumber in parsedEpisodeInfo.AbsoluteEpisodeNumbers)
|
|
{
|
|
var episodeInfo = _episodeService.FindEpisode(series.Id, absoluteEpisodeNumber);
|
|
|
|
if (episodeInfo != null)
|
|
{
|
|
_logger.Info("Using absolute episode number {0} for: {1} - TVDB: {2}x{3:00}",
|
|
absoluteEpisodeNumber,
|
|
series.Title,
|
|
episodeInfo.SeasonNumber,
|
|
episodeInfo.EpisodeNumber);
|
|
result.Add(episodeInfo);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if (parsedEpisodeInfo.EpisodeNumbers == null)
|
|
return result;
|
|
|
|
foreach (var episodeNumber in parsedEpisodeInfo.EpisodeNumbers)
|
|
{
|
|
Episode episodeInfo = null;
|
|
|
|
if (series.UseSceneNumbering && sceneSource)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (episodeInfo == null && searchCriteria != null)
|
|
{
|
|
episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber &&
|
|
e.EpisodeNumber == episodeNumber);
|
|
}
|
|
|
|
if (episodeInfo == null)
|
|
{
|
|
episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber);
|
|
}
|
|
|
|
if (episodeInfo != null)
|
|
{
|
|
result.Add(episodeInfo);
|
|
}
|
|
|
|
else
|
|
{
|
|
_logger.Debug("Unable to find {0}", parsedEpisodeInfo);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria)
|
|
{
|
|
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;
|
|
}
|
|
|
|
private Episode GetDailyEpisode(Series series, String airDate, SearchCriteriaBase searchCriteria)
|
|
{
|
|
Episode episodeInfo = null;
|
|
|
|
if (searchCriteria != null)
|
|
{
|
|
episodeInfo = searchCriteria.Episodes.SingleOrDefault(
|
|
e => e.AirDate == airDate);
|
|
}
|
|
|
|
if (episodeInfo == null)
|
|
{
|
|
episodeInfo = _episodeService.FindEpisode(series.Id, airDate);
|
|
}
|
|
|
|
return episodeInfo;
|
|
}
|
|
}
|
|
} |