2020-02-06 14:20:23 +00:00
#pragma warning disable CS1591
2019-01-06 20:50:43 +00:00
using System ;
2018-12-28 14:30:53 +00:00
using System.Collections.Concurrent ;
using System.Globalization ;
using System.Linq ;
2019-02-05 08:49:46 +00:00
using System.Threading ;
2020-08-14 00:48:28 +00:00
using Jellyfin.Data.Events ;
2015-07-20 18:32:55 +00:00
using MediaBrowser.Controller.LiveTv ;
2016-06-20 22:07:18 +00:00
using MediaBrowser.Model.LiveTv ;
2019-01-13 19:22:00 +00:00
using Microsoft.Extensions.Logging ;
2015-07-20 18:32:55 +00:00
2016-11-03 23:35:19 +00:00
namespace Emby.Server.Implementations.LiveTv.EmbyTV
2015-07-20 18:32:55 +00:00
{
public class TimerManager : ItemDataProvider < TimerInfo >
{
2019-02-05 08:49:46 +00:00
private readonly ConcurrentDictionary < string , Timer > _timers = new ConcurrentDictionary < string , Timer > ( StringComparer . OrdinalIgnoreCase ) ;
2015-07-20 18:32:55 +00:00
2020-12-23 12:12:40 +00:00
public TimerManager ( ILogger logger , string dataPath )
: base ( logger , dataPath , ( r1 , r2 ) = > string . Equals ( r1 . Id , r2 . Id , StringComparison . OrdinalIgnoreCase ) )
2015-07-20 18:32:55 +00:00
{
}
2021-08-28 15:32:09 +00:00
public event EventHandler < GenericEventArgs < TimerInfo > > ? TimerFired ;
2019-09-12 19:30:57 +00:00
2015-07-20 18:32:55 +00:00
public void RestartTimers ( )
{
StopTimers ( ) ;
2015-07-29 03:42:03 +00:00
2019-09-12 19:30:57 +00:00
foreach ( var item in GetAll ( ) )
2015-07-29 03:42:03 +00:00
{
2019-01-02 14:57:48 +00:00
AddOrUpdateSystemTimer ( item ) ;
2015-07-29 03:42:03 +00:00
}
2015-07-20 18:32:55 +00:00
}
public void StopTimers ( )
{
foreach ( var pair in _timers . ToList ( ) )
{
pair . Value . Dispose ( ) ;
}
_timers . Clear ( ) ;
}
public override void Delete ( TimerInfo item )
{
base . Delete ( item ) ;
2015-08-22 18:29:12 +00:00
StopTimer ( item ) ;
2015-07-20 18:32:55 +00:00
}
public override void Update ( TimerInfo item )
{
base . Update ( item ) ;
2019-01-02 14:57:48 +00:00
AddOrUpdateSystemTimer ( item ) ;
2015-07-20 18:32:55 +00:00
}
2016-06-23 05:26:34 +00:00
public void AddOrUpdate ( TimerInfo item , bool resetTimer )
{
if ( resetTimer )
{
AddOrUpdate ( item ) ;
return ;
}
2019-09-12 19:30:57 +00:00
base . AddOrUpdate ( item ) ;
}
2016-06-23 05:26:34 +00:00
2019-09-12 19:30:57 +00:00
public override void AddOrUpdate ( TimerInfo item )
{
base . AddOrUpdate ( item ) ;
AddOrUpdateSystemTimer ( item ) ;
2016-06-23 05:26:34 +00:00
}
2015-07-20 18:32:55 +00:00
public override void Add ( TimerInfo item )
{
2022-10-13 17:08:00 +00:00
ArgumentException . ThrowIfNullOrEmpty ( item . Id ) ;
2015-07-20 18:32:55 +00:00
base . Add ( item ) ;
2019-01-02 14:57:48 +00:00
AddOrUpdateSystemTimer ( item ) ;
2016-09-26 18:59:18 +00:00
}
2019-01-06 20:50:43 +00:00
private static bool ShouldStartTimer ( TimerInfo item )
2016-09-26 18:59:18 +00:00
{
2019-09-12 19:30:57 +00:00
if ( item . Status = = RecordingStatus . Completed
| | item . Status = = RecordingStatus . Cancelled )
2016-09-26 18:59:18 +00:00
{
return false ;
}
return true ;
2015-07-20 18:32:55 +00:00
}
2019-01-02 14:57:48 +00:00
private void AddOrUpdateSystemTimer ( TimerInfo item )
2015-07-20 18:32:55 +00:00
{
2016-09-26 18:59:18 +00:00
StopTimer ( item ) ;
if ( ! ShouldStartTimer ( item ) )
2016-06-20 22:07:18 +00:00
{
return ;
}
2015-07-29 03:42:03 +00:00
var startDate = RecordingHelper . GetStartTime ( item ) ;
var now = DateTime . UtcNow ;
if ( startDate < now )
{
2020-04-05 16:10:56 +00:00
TimerFired ? . Invoke ( this , new GenericEventArgs < TimerInfo > ( item ) ) ;
2015-07-29 03:42:03 +00:00
return ;
}
2016-09-26 18:59:18 +00:00
var dueTime = startDate - now ;
StartTimer ( item , dueTime ) ;
2015-08-22 18:29:12 +00:00
}
2015-07-20 18:32:55 +00:00
2016-09-26 18:59:18 +00:00
private void StartTimer ( TimerInfo item , TimeSpan dueTime )
2015-08-22 18:29:12 +00:00
{
2019-02-05 08:49:46 +00:00
var timer = new Timer ( TimerCallback , item . Id , dueTime , TimeSpan . Zero ) ;
2015-07-20 18:32:55 +00:00
2016-01-28 19:10:56 +00:00
if ( _timers . TryAdd ( item . Id , timer ) )
{
2022-11-22 21:02:00 +00:00
if ( item . IsSeries )
{
Logger . LogInformation (
"Creating recording timer for {Id}, {Name} {SeasonNumber}x{EpisodeNumber:D2} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}" ,
2019-09-12 19:30:57 +00:00
item . Id ,
item . Name ,
2022-11-22 21:02:00 +00:00
item . SeasonNumber ,
item . EpisodeNumber ,
item . ChannelId ,
dueTime . TotalMinutes . ToString ( CultureInfo . InvariantCulture ) ,
item . StartDate ) ;
}
else
{
Logger . LogInformation (
"Creating recording timer for {Id}, {Name} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}" ,
item . Id ,
item . Name ,
item . ChannelId ,
dueTime . TotalMinutes . ToString ( CultureInfo . InvariantCulture ) ,
item . StartDate ) ;
}
2016-01-28 19:10:56 +00:00
}
else
2015-07-20 18:32:55 +00:00
{
timer . Dispose ( ) ;
2019-09-12 19:30:57 +00:00
Logger . LogWarning ( "Timer already exists for item {Id}" , item . Id ) ;
2015-07-20 18:32:55 +00:00
}
}
2015-08-22 18:29:12 +00:00
private void StopTimer ( TimerInfo item )
{
2019-01-13 20:46:33 +00:00
if ( _timers . TryRemove ( item . Id , out var timer ) )
2015-08-22 18:29:12 +00:00
{
timer . Dispose ( ) ;
}
}
2021-08-28 15:32:09 +00:00
private void TimerCallback ( object? state )
2015-07-20 18:32:55 +00:00
{
2021-08-28 15:32:09 +00:00
var timerId = ( string? ) state ? ? throw new ArgumentNullException ( nameof ( state ) ) ;
2015-07-20 18:32:55 +00:00
var timer = GetAll ( ) . FirstOrDefault ( i = > string . Equals ( i . Id , timerId , StringComparison . OrdinalIgnoreCase ) ) ;
2022-12-05 14:01:13 +00:00
if ( timer is not null )
2015-07-20 18:32:55 +00:00
{
2020-04-05 16:10:56 +00:00
TimerFired ? . Invoke ( this , new GenericEventArgs < TimerInfo > ( timer ) ) ;
2015-07-20 18:32:55 +00:00
}
}
2016-09-27 05:13:56 +00:00
2021-08-28 15:32:09 +00:00
public TimerInfo ? GetTimer ( string id )
2016-09-27 05:13:56 +00:00
{
return GetAll ( ) . FirstOrDefault ( r = > string . Equals ( r . Id , id , StringComparison . OrdinalIgnoreCase ) ) ;
}
2017-03-26 04:21:32 +00:00
2021-08-28 15:32:09 +00:00
public TimerInfo ? GetTimerByProgramId ( string programId )
2017-03-26 04:21:32 +00:00
{
return GetAll ( ) . FirstOrDefault ( r = > string . Equals ( r . ProgramId , programId , StringComparison . OrdinalIgnoreCase ) ) ;
}
2015-07-20 18:32:55 +00:00
}
}