2010-09-28 19:32:19 +00:00
using System ;
2010-10-17 17:22:48 +00:00
using System.Linq ;
2010-09-28 19:32:19 +00:00
using System.Collections.Generic ;
2010-09-28 05:01:54 +00:00
using System.Text.RegularExpressions ;
2010-10-05 06:21:18 +00:00
using NLog ;
2010-10-21 01:49:23 +00:00
using NzbDrone.Core.Model ;
using NzbDrone.Core.Repository ;
2010-09-28 19:32:19 +00:00
using SubSonic.Repository ;
2010-09-28 05:01:54 +00:00
namespace NzbDrone.Core.Providers
{
2010-09-28 20:44:33 +00:00
public class EpisodeProvider : IEpisodeProvider
2010-09-28 05:01:54 +00:00
{
2010-09-28 19:32:19 +00:00
//TODO: Remove parsing of the series name, it should be done in series provider
private readonly IRepository _sonicRepo ;
2010-10-05 06:21:18 +00:00
private readonly ISeriesProvider _series ;
private readonly ISeasonProvider _seasons ;
private readonly ITvDbProvider _tvDb ;
2011-01-29 06:10:22 +00:00
private readonly IHistoryProvider _history ;
2011-02-16 16:37:28 +00:00
private readonly IQualityProvider _quality ;
2010-10-07 22:17:24 +00:00
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
2010-09-28 19:32:19 +00:00
2011-02-16 16:37:28 +00:00
public EpisodeProvider ( IRepository sonicRepo , ISeriesProvider seriesProvider , ISeasonProvider seasonProvider , ITvDbProvider tvDbProvider , IHistoryProvider history , IQualityProvider quality )
2010-09-28 05:01:54 +00:00
{
2010-09-28 19:32:19 +00:00
_sonicRepo = sonicRepo ;
2010-10-05 06:21:18 +00:00
_series = seriesProvider ;
_tvDb = tvDbProvider ;
_seasons = seasonProvider ;
2011-01-29 06:10:22 +00:00
_history = history ;
2011-02-16 16:37:28 +00:00
_quality = quality ;
2010-09-28 19:32:19 +00:00
}
2010-10-21 01:49:23 +00:00
public Episode GetEpisode ( long id )
2010-09-28 19:32:19 +00:00
{
2010-10-24 07:46:58 +00:00
return _sonicRepo . Single < Episode > ( id ) ;
}
public Episode GetEpisode ( int seriesId , int seasonNumber , int episodeNumber )
{
return _sonicRepo . Single < Episode > ( c = > c . SeriesId = = seriesId & & c . SeasonNumber = = seasonNumber & & c . EpisodeNumber = = episodeNumber ) ;
2010-09-28 19:32:19 +00:00
}
2010-10-21 01:49:23 +00:00
public IList < Episode > GetEpisodeBySeries ( long seriesId )
2010-09-28 19:32:19 +00:00
{
2010-10-21 01:49:23 +00:00
return _sonicRepo . Find < Episode > ( e = > e . SeriesId = = seriesId ) ;
2010-09-28 19:32:19 +00:00
}
2010-10-30 02:46:32 +00:00
public IList < Episode > GetEpisodeBySeason ( long seasonId )
{
return _sonicRepo . Find < Episode > ( e = > e . SeasonId = = seasonId ) ;
}
2010-10-21 01:49:23 +00:00
public String GetSabTitle ( Episode episode )
2010-09-28 19:32:19 +00:00
{
2010-10-05 06:21:18 +00:00
var series = _series . GetSeries ( episode . SeriesId ) ;
2010-09-28 19:32:19 +00:00
if ( series = = null ) throw new ArgumentException ( "Unknown series. ID: " + episode . SeriesId ) ;
//TODO: This method should return a standard title for the sab episode.
throw new NotImplementedException ( ) ;
2010-09-28 05:01:54 +00:00
}
2010-09-28 19:32:19 +00:00
/// <summary>
/// Comprehensive check on whether or not this episode is needed.
/// </summary>
/// <param name="episode">Episode that needs to be checked</param>
/// <returns></returns>
2010-10-21 01:49:23 +00:00
public bool IsNeeded ( EpisodeModel episode )
2010-09-28 05:01:54 +00:00
{
2011-01-29 06:10:22 +00:00
//IsSeasonIgnored
//IsQualityWanted
//EpisodeFileExists
//IsInHistory
//IsOnDisk? (How to handle episodes that are downloaded manually?)
if ( IsSeasonIgnored ( episode ) )
return false ;
2011-02-16 04:44:19 +00:00
2011-02-17 02:17:01 +00:00
//Quickly check if this quality is wanted at all (We will later check if the quality is still needed)
2011-01-29 06:10:22 +00:00
if ( ! _series . QualityWanted ( episode . SeriesId , episode . Quality ) )
{
Logger . Debug ( "Quality [{0}] is not wanted for: {1}" , episode . Quality , episode . SeriesTitle ) ;
return false ;
}
//Check to see if there is an episode file for this episode
var dbEpisode = GetEpisode ( episode . SeriesId , episode . SeasonNumber , episode . EpisodeNumber ) ;
2011-02-16 16:37:28 +00:00
if ( dbEpisode = = null )
{
2011-02-17 02:17:01 +00:00
//Todo: How do we want to handle this really? Episode could be released before information is on TheTvDB (Parks and Rec did this a lot in the first season, from experience)
2011-02-16 16:37:28 +00:00
throw new NotImplementedException ( "Episode was not found in the database" ) ;
}
2011-01-29 06:10:22 +00:00
episode . EpisodeId = dbEpisode . EpisodeId ;
2011-02-22 08:13:16 +00:00
var file = _sonicRepo . Single < EpisodeFile > ( c = > c . FileId = = dbEpisode . FileId ) ;
2011-02-16 04:44:19 +00:00
2011-02-22 08:13:16 +00:00
if ( file ! = null )
2011-01-29 06:10:22 +00:00
{
//If not null we need to see if this episode has the quality as the download (or if it is better)
2011-02-22 08:13:16 +00:00
if ( file . Quality = = episode . Quality )
2011-01-29 06:10:22 +00:00
{
2011-02-22 08:13:16 +00:00
//If the episodeFile is a Proper we don't need to download again
if ( file . Proper )
return false ;
}
//There will never be a time when the episode quality is less than what we have and we want it... ever.... I think.
if ( file . Quality > episode . Quality )
return false ;
//Now we need to handle upgrades and actually pay attention to the Cutoff Value
if ( file . Quality < episode . Quality )
{
var series = _series . GetSeries ( episode . SeriesId ) ;
var quality = _quality . Find ( series . QualityProfileId ) ;
if ( quality . Cutoff < = file . Quality )
2011-02-16 16:37:28 +00:00
{
//If the episodeFile is a Proper we don't need to download again
if ( file . Proper )
return false ;
}
2011-01-29 06:10:22 +00:00
}
}
//IsInHistory? (NZBDrone)
if ( _history . Exists ( dbEpisode . EpisodeId , episode . Quality , episode . Proper ) )
{
Logger . Debug ( "Episode in history: {0}" , episode . ToString ( ) ) ;
return false ;
}
2011-02-17 02:17:01 +00:00
return true ; //If we get to this point and the file has not yet been rejected then accept it
2010-09-28 19:32:19 +00:00
}
2010-10-17 17:22:48 +00:00
public void RefreshEpisodeInfo ( int seriesId )
2010-10-05 06:21:18 +00:00
{
Logger . Info ( "Starting episode info refresh for series:{0}" , seriesId ) ;
int successCount = 0 ;
int failCount = 0 ;
var targetSeries = _tvDb . GetSeries ( seriesId , true ) ;
2010-10-17 17:22:48 +00:00
2010-10-21 01:49:23 +00:00
var updateList = new List < Episode > ( ) ;
var newList = new List < Episode > ( ) ;
2010-10-17 17:22:48 +00:00
2010-10-30 02:46:32 +00:00
Logger . Debug ( "Updating season info for series:{0}" , targetSeries . SeriesName ) ;
2010-10-17 17:22:48 +00:00
targetSeries . Episodes . Select ( e = > new { e . SeasonId , e . SeasonNumber } )
. Distinct ( ) . ToList ( )
. ForEach ( s = > _seasons . EnsureSeason ( seriesId , s . SeasonId , s . SeasonNumber ) ) ;
2010-10-05 06:21:18 +00:00
foreach ( var episode in targetSeries . Episodes )
{
try
{
2011-01-29 06:10:22 +00:00
//DateTime throws an error in SQLServer per message below:
//SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
//So lets hack it so it works for SQLServer (as well as SQLite), perhaps we can find a better solution
//Todo: Fix this hack
if ( episode . FirstAired < new DateTime ( 1753 , 1 , 1 ) )
episode . FirstAired = new DateTime ( 1753 , 1 , 1 ) ;
2010-10-30 02:46:32 +00:00
Logger . Trace ( "Updating info for series:{0} - episode:{1}" , targetSeries . SeriesName , episode . EpisodeNumber ) ;
2010-10-21 01:49:23 +00:00
var newEpisode = new Episode ( )
2010-10-17 17:22:48 +00:00
{
AirDate = episode . FirstAired ,
EpisodeId = episode . Id ,
EpisodeNumber = episode . EpisodeNumber ,
Language = episode . Language . Abbriviation ,
Overview = episode . Overview ,
SeasonId = episode . SeasonId ,
SeasonNumber = episode . SeasonNumber ,
SeriesId = seriesId ,
Title = episode . EpisodeName
} ;
2010-10-21 01:49:23 +00:00
if ( _sonicRepo . Exists < Episode > ( e = > e . EpisodeId = = newEpisode . EpisodeId ) )
2010-10-05 06:21:18 +00:00
{
2010-10-17 17:22:48 +00:00
updateList . Add ( newEpisode ) ;
}
else
{
newList . Add ( newEpisode ) ;
}
2010-10-05 06:21:18 +00:00
2010-10-17 17:22:48 +00:00
successCount + + ;
2010-10-05 06:21:18 +00:00
}
catch ( Exception e )
{
2010-10-17 17:22:48 +00:00
Logger . FatalException ( String . Format ( "An error has occurred while updating episode info for series {0}" , seriesId ) , e ) ;
2010-10-05 06:21:18 +00:00
failCount + + ;
}
}
2010-10-17 17:22:48 +00:00
_sonicRepo . AddMany ( newList ) ;
_sonicRepo . UpdateMany ( updateList ) ;
2010-10-30 02:46:32 +00:00
Logger . Debug ( "Finished episode refresh for series:{0}. Successful:{1} - Failed:{2} " , targetSeries . SeriesName , successCount , failCount ) ;
2010-10-05 06:21:18 +00:00
}
2011-01-29 06:10:22 +00:00
2011-02-10 06:42:46 +00:00
public void RefreshEpisodeInfo ( Season season )
{
Logger . Info ( "Starting episode info refresh for season {0} of series:{1}" , season . SeasonNumber , season . SeriesId ) ;
int successCount = 0 ;
int failCount = 0 ;
var targetSeries = _tvDb . GetSeries ( season . SeriesId , true ) ;
var updateList = new List < Episode > ( ) ;
var newList = new List < Episode > ( ) ;
foreach ( var episode in targetSeries . Episodes . Where ( e = > e . SeasonId = = season . SeasonId ) )
{
try
{
//DateTime throws an error in SQLServer per message below:
//SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
//So lets hack it so it works for SQLServer (as well as SQLite), perhaps we can find a better solution
//Todo: Fix this hack
if ( episode . FirstAired < new DateTime ( 1753 , 1 , 1 ) )
episode . FirstAired = new DateTime ( 1753 , 1 , 1 ) ;
Logger . Trace ( "Updating info for series:{0} - episode:{1}" , targetSeries . SeriesName , episode . EpisodeNumber ) ;
var newEpisode = new Episode ( )
{
AirDate = episode . FirstAired ,
EpisodeId = episode . Id ,
EpisodeNumber = episode . EpisodeNumber ,
Language = episode . Language . Abbriviation ,
Overview = episode . Overview ,
SeasonId = episode . SeasonId ,
SeasonNumber = episode . SeasonNumber ,
SeriesId = season . SeriesId ,
Title = episode . EpisodeName
} ;
if ( _sonicRepo . Exists < Episode > ( e = > e . EpisodeId = = newEpisode . EpisodeId ) )
{
updateList . Add ( newEpisode ) ;
}
else
{
newList . Add ( newEpisode ) ;
}
successCount + + ;
}
catch ( Exception e )
{
Logger . FatalException ( String . Format ( "An error has occurred while updating episode info for season {0} of series {1}" , season . SeasonNumber , season . SeriesId ) , e ) ;
failCount + + ;
}
}
_sonicRepo . AddMany ( newList ) ;
_sonicRepo . UpdateMany ( updateList ) ;
Logger . Debug ( "Finished episode refresh for series:{0}. Successful:{1} - Failed:{2} " , targetSeries . SeriesName , successCount , failCount ) ;
}
2011-02-18 06:49:23 +00:00
public void DeleteEpisode ( int episodeId )
{
_sonicRepo . Delete < Episode > ( episodeId ) ;
}
2011-02-23 06:23:59 +00:00
public void UpdateEpisode ( Episode episode )
{
_sonicRepo . Update ( episode ) ;
}
2011-01-29 06:10:22 +00:00
private bool IsSeasonIgnored ( EpisodeModel episode )
{
//Check if this Season is ignored
if ( _seasons . IsIgnored ( episode . SeriesId , episode . SeasonNumber ) )
{
Logger . Debug ( "Season {0} is ignored for: {1}" , episode . SeasonNumber , episode . SeriesTitle ) ;
return true ;
}
Logger . Debug ( "Season {0} is wanted for: {1}" , episode . SeasonNumber , episode . SeriesTitle ) ;
return false ;
}
2010-09-28 05:01:54 +00:00
}
}