private Task CreateTimer(CancellationToken cancellationToken, Program program, Timers timers)
        {
            if (!timers.Timer.Any(t =>
                                  t.ChannelId == program.ChannelId &&
                                  GeneralExtensions.GetScheduleTime(t.Date, t.Start).AddMinutes(t.PreEPG) == GeneralExtensions.GetProgramTime(program.Start) &&
                                  GeneralExtensions.GetScheduleTime(t.Date, t.Start).AddMinutes(t.Duration).AddMinutes(-t.PostEPG) == GeneralExtensions.GetProgramTime(program.Stop)))
            {
                var builder = new StringBuilder("api/timeradd.html?");

                builder.AppendFormat("title={0}&", (program.EpisodeTitle != null ? program.Name + " - " + program.EpisodeTitle : program.Name).ToUrlString());
                builder.AppendFormat("series={0}&", program.Name.ToUrlString());
                builder.AppendFormat("encoding={0}&", 255);
                builder.AppendFormat("ch={0}&", program.ChannelId);
                builder.AppendFormat("dor={0}&", GeneralExtensions.GetProgramTime(program.Start).ToDelphiDate());
                builder.AppendFormat("start={0}&", GeneralExtensions.GetProgramTime(program.Start).AddMinutes(-(double)Configuration.TimerPrePadding).ToLocalTime().TimeOfDay.TotalMinutes);
                builder.AppendFormat("stop={0}&", GeneralExtensions.GetProgramTime(program.Stop).AddMinutes((double)Configuration.TimerPostPadding).ToLocalTime().TimeOfDay.TotalMinutes);
                builder.AppendFormat("pre={0}&", Configuration.TimerPrePadding);
                builder.AppendFormat("post={0}&", Configuration.TimerPostPadding);
                builder.AppendFormat("prio={0}&", 49);
                builder.AppendFormat("after={0}&", !String.IsNullOrWhiteSpace(Configuration.TimerTask) ? Configuration.TimerTask.ToUrlString() : String.Empty);

                builder.Remove(builder.Length - 1, 1);

                Plugin.Logger.Info("Create new schedule: {0} - {1}, StartTime: {2}, EndTime: {3}, ChannelId: {4}", program.Name, program.EpisodeTitle, program.Start, program.Stop, program.ChannelId);
                return(Task.FromResult(GetToService(cancellationToken, builder.ToString())));
            }

            return(Task.Delay(0));
        }
        private void SearchForTimers(CancellationToken cancellationToken)
        {
            var missingEpisodes = _libraryManager.GetItemList(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { typeof(Episode).Name },
                IsMissing        = true,
            });

            var timers = GetFromService <Timers>(cancellationToken, typeof(Timers), "api/timerlist.html?utf8=2");

            var programs = GetFromService <Guide>(cancellationToken, typeof(Guide),
                                                  "api/epg.html?lvl=2&start={0}&end={1}",
                                                  GeneralExtensions.FloatDateTime(DateTime.Now),
                                                  GeneralExtensions.FloatDateTime(DateTime.Now.AddHours(25))).Program;

            foreach (var program in programs)
            {
                if (!String.IsNullOrEmpty(program.Name))
                {
                    foreach (var episode in missingEpisodes.Where(x =>
                                                                  x.Parent.Parent.Name.Contains(Regex.Replace(program.Name, @"\s\W[a-zA-Z]?[0-9]{1,3}?\W$", String.Empty)) &&
                                                                  x.IndexNumber.Equals(Convert.ToInt32(program.EpisodeNumber)) &&
                                                                  x.ParentIndexNumber.Equals(Convert.ToInt32(program.SeasonNumber))))
                    {
                        CreateTimer(cancellationToken, program, timers);
                    }
                }
            }

            Plugin.TvProxy.RefreshSchedules(cancellationToken);
        }
        private Task CreateTimer(CancellationToken cancellationToken, Program program, Timers timers)
        {
            var channels = _tvServiceProxy.GetChannelList(cancellationToken).Root.ChannelGroup.SelectMany(c => c.Channel);
            var guide    = GetFromService <Guide>(cancellationToken, typeof(Guide),
                                                  "api/epg.html?lvl=2&channel={0}&start={1}&end={2}",
                                                  program.ChannelEPGID,
                                                  GeneralExtensions.FloatDateTime(GeneralExtensions.GetProgramTime(program.Start)),
                                                  GeneralExtensions.FloatDateTime(GeneralExtensions.GetProgramTime(program.Stop)));

            var episodeTitle = program.EpisodeTitle;

            if (!timers.Timer.Any(t =>
                                  t.ChannelId == program.ChannelId &&
                                  GeneralExtensions.GetScheduleTime(t.Date, t.Start).AddMinutes(t.PreEPG) == GeneralExtensions.GetProgramTime(program.Start) &&
                                  GeneralExtensions.GetScheduleTime(t.Date, t.Start).AddMinutes(t.Duration).AddMinutes(-t.PostEPG) == GeneralExtensions.GetProgramTime(program.Stop)))
            {
                var builder = new StringBuilder("api/timeradd.html?");

                builder.AppendFormat("title={0}&", (episodeTitle != null ? program.Name + " - " + episodeTitle : program.Name).ToUrlString());
                builder.AppendFormat("series={0}&", program.Name.ToUrlString());
                builder.AppendFormat("encoding={0}&", 255);
                builder.AppendFormat("ch={0}&", program.ChannelId);
                builder.AppendFormat("dor={0}&", GeneralExtensions.GetProgramTime(program.Start).ToDelphiDate());
                builder.AppendFormat("start={0}&", GeneralExtensions.GetProgramTime(program.Start).AddMinutes(-(double)Configuration.TimerPrePadding).ToLocalTime().TimeOfDay.TotalMinutes);
                builder.AppendFormat("stop={0}&", GeneralExtensions.GetProgramTime(program.Stop).AddMinutes((double)Configuration.TimerPostPadding).ToLocalTime().TimeOfDay.TotalMinutes);
                builder.AppendFormat("pre={0}&", Configuration.TimerPrePadding);
                builder.AppendFormat("post={0}&", Configuration.TimerPostPadding);
                builder.AppendFormat("after={0}&", !String.IsNullOrWhiteSpace(Configuration.TimerTask) ? Configuration.TimerTask.ToUrlString() : String.Empty);

                builder.Remove(builder.Length - 1, 1);

                Plugin.Logger.Info("Create new schedule: {0}, StartTime: {1}, EndTime: {2}, ChannelId: {3}", program.Name, program.Start, program.Stop, program.ChannelId);
                return(Task.FromResult(GetToService(cancellationToken, builder.ToString())));
            }

            return(Task.Delay(0));
        }
        private void SearchForTimers(CancellationToken cancellationToken)
        {
            List <Program> programs = new List <Program>();

            Plugin.Logger.Info("AUTOCREATE DVBViewer TIMERS: Get missing episodes now");
            var missingEpisodes = _libraryManager.GetItemList(new InternalItemsQuery
            {
                IncludeItemTypes = new[] { typeof(Episode).Name },
                IsMissing        = true,
            }).Where(x => !x.Tags.Contains("NoDMS") && !x.Parent.Tags.Contains("NoDMS") && !x.Parent.Parent.Tags.Contains("NoDMS"));

            // Extra logging
            if (Plugin.Instance.Configuration.EnableLogging)
            {
                foreach (var item in missingEpisodes)
                {
                    Plugin.Logger.Info("MISSING EPISODES > Series: {0}; Number: {1}.{2}; Episode: {3}",
                                       item.Parent.Parent.Name,
                                       item.Parent.IndexNumber,
                                       item.IndexNumber,
                                       item.Name);
                }
            }

            Plugin.Logger.Info("AUTOCREATE DVBViewer TIMERS: Get timers from backend now");
            var timers = GetFromService <Timers>(cancellationToken, typeof(Timers), "api/timerlist.html?utf8=2");

            var channels = Plugin.TvProxy.GetChannelList(cancellationToken, "TimerChannelGroup").Root.ChannelGroup.SelectMany(c => c.DMSChannel);

            Plugin.Logger.Info("AUTOCREATE DVBViewer TIMERS: Get guide data from backend now");
            foreach (var channel in channels)
            {
                var program = GetFromService <Guide>(cancellationToken, typeof(Guide), "api/epg.html?lvl=2&channel={0}&start={1}&end={2}", channel.EPGID, GeneralExtensions.FloatDateTimeOffset(DateTimeOffset.Now), GeneralExtensions.FloatDateTimeOffset(DateTimeOffset.Now.AddHours(72))).Program;
                programs.AddRange(program.Where(p => p.ChannelEPGID == channel.EPGID));
            }

            bool refreshSchedules = false;

            Plugin.Logger.Info("AUTOCREATE DVBViewer TIMERS: Compare guide data with missing episodes now");
            foreach (var program in programs)
            {
                if (missingEpisodes != null && !String.IsNullOrEmpty(program.Name) && program.SeasonNumber.HasValue && program.EpisodeNumber.HasValue)
                {
                    // Extra logging
                    Plugin.Logger.Info("PROGRAM > Title: {0}; Number: {1}.{2}; Subtitle: {3}; Channel: {4}; ChannelId: {5}; ChannelEPGID: {6} Start: {7}",
                                       program.Name,
                                       program.SeasonNumber,
                                       program.EpisodeNumber,
                                       program.EpisodeTitleRegEx,
                                       program.ChannelName,
                                       program.ChannelId,
                                       program.ChannelEPGID, GeneralExtensions.GetProgramTime(program.Start).ToLocalTime());

                    foreach (var episode in missingEpisodes.Where(x =>
                                                                  Regex.Replace(x.Parent.Parent.Name, @"\s\W[a-zA-Z]?[0-9]{1,4}?\W$", String.Empty).Equals(Regex.Replace(program.Name, @"\s\W[a-zA-Z]?[0-9]{1,4}?\W$", String.Empty), StringComparison.OrdinalIgnoreCase) &&
                                                                  x.IndexNumber.Equals(program.EpisodeNumber) &&
                                                                  x.ParentIndexNumber.Equals(program.SeasonNumber)))
                    {
                        CreateTimer(cancellationToken, program, timers);
                        refreshSchedules = true;
                    }
                }
            }

            if (refreshSchedules)
            {
                Plugin.TvProxy.RefreshSchedules(cancellationToken);
            }
        }