mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Timers now internally have a pointer to their channel
This commit is contained in:
parent
ab4ceb29a0
commit
ac9622bb8a
4
HISTORY
4
HISTORY
@ -1611,7 +1611,7 @@ Video Disk Recorder Revision History
|
||||
shall be executed from the "Recordings" menu; see MANUAL and 'man vdr(5)' for
|
||||
details (suggested by Gerhard Steiner).
|
||||
|
||||
2002-10-19: Version 1.1.14
|
||||
2002-10-20: Version 1.1.14
|
||||
|
||||
- Fixed some faulty default parameter initializations (thanks to Robert Schiele).
|
||||
- Added further satellites to 'sources.conf' (thanks to Reinhard Walter Buchner).
|
||||
@ -1624,3 +1624,5 @@ Video Disk Recorder Revision History
|
||||
used to create 'gaps' in the channel numbering (see 'man 5 vdr'). BE CAREFUL
|
||||
TO UPDATE YOUR 'timers.conf' ACCORDINGLY IF INSERTING THIS NEW FEATURE INTO YOUR
|
||||
'channels.conf' FILE!
|
||||
- Timers now internally have a pointer to their channel (this is necessary to
|
||||
handle gaps in channel numbers, and in preparation for unique channel ids).
|
||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Makefile 1.48 2002/10/04 14:29:14 kls Exp $
|
||||
# $Id: Makefile 1.49 2002/10/19 15:46:08 kls Exp $
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@ -36,7 +36,7 @@ OBJS = audio.o channels.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd
|
||||
dvbplayer.o dvbspu.o eit.o eitscan.o font.o i18n.o interface.o keys.o\
|
||||
lirc.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o rcu.o\
|
||||
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sources.o\
|
||||
spu.o status.o svdrp.o thread.o tools.o transfer.o vdr.o videodir.o
|
||||
spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o
|
||||
|
||||
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
||||
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: channels.c 1.4 2002/10/19 14:46:05 kls Exp $
|
||||
* $Id: channels.c 1.5 2002/10/20 11:50:47 kls Exp $
|
||||
*/
|
||||
|
||||
#include "channels.h"
|
||||
@ -450,9 +450,3 @@ bool cChannels::SwitchTo(int Number)
|
||||
cChannel *channel = GetByNumber(Number);
|
||||
return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
|
||||
}
|
||||
|
||||
const char *cChannels::GetChannelNameByNumber(int Number)
|
||||
{
|
||||
cChannel *channel = GetByNumber(Number);
|
||||
return channel ? channel->Name() : NULL;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: channels.h 1.2 2002/10/19 11:48:02 kls Exp $
|
||||
* $Id: channels.h 1.3 2002/10/20 11:50:36 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CHANNELS_H
|
||||
@ -114,7 +114,6 @@ public:
|
||||
void ReNumber(void); // Recalculate 'number' based on channel type
|
||||
cChannel *GetByNumber(int Number, int SkipGap = 0);
|
||||
cChannel *GetByServiceID(unsigned short ServiceId);
|
||||
const char *GetChannelNameByNumber(int Number);
|
||||
bool SwitchTo(int Number);
|
||||
int MaxNumber(void) { return maxNumber; }
|
||||
};
|
||||
|
377
config.c
377
config.c
@ -4,13 +4,12 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.c 1.110 2002/10/19 11:34:01 kls Exp $
|
||||
* $Id: config.c 1.111 2002/10/19 15:49:51 kls Exp $
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include "channels.h" //XXX timers!
|
||||
#include "i18n.h"
|
||||
#include "interface.h"
|
||||
#include "plugin.h"
|
||||
@ -20,339 +19,6 @@
|
||||
// format characters in order to allow any number of blanks after a numeric
|
||||
// value!
|
||||
|
||||
// -- cTimer -----------------------------------------------------------------
|
||||
|
||||
char *cTimer::buffer = NULL;
|
||||
|
||||
cTimer::cTimer(bool Instant)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = Instant ? taActInst : taInactive;
|
||||
cChannel *ch = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
channel = ch ? ch->Number() : 0;
|
||||
time_t t = time(NULL);
|
||||
struct tm tm_r;
|
||||
struct tm *now = localtime_r(&t, &tm_r);
|
||||
day = now->tm_mday;
|
||||
start = now->tm_hour * 100 + now->tm_min;
|
||||
stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
|
||||
stop = (stop / 60) * 100 + (stop % 60);
|
||||
if (stop >= 2400)
|
||||
stop -= 2400;
|
||||
//TODO VPS???
|
||||
priority = Setup.DefaultPriority;
|
||||
lifetime = Setup.DefaultLifetime;
|
||||
*file = 0;
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
if (Instant && ch)
|
||||
snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : ch->Name());
|
||||
}
|
||||
|
||||
cTimer::cTimer(const cEventInfo *EventInfo)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = true;
|
||||
cChannel *ch = Channels.GetByServiceID(EventInfo->GetServiceID());
|
||||
channel = ch ? ch->Number() : 0;
|
||||
time_t tstart = EventInfo->GetTime();
|
||||
time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
|
||||
tstart -= Setup.MarginStart * 60;
|
||||
struct tm tm_r;
|
||||
struct tm *time = localtime_r(&tstart, &tm_r);
|
||||
day = time->tm_mday;
|
||||
start = time->tm_hour * 100 + time->tm_min;
|
||||
time = localtime_r(&tstop, &tm_r);
|
||||
stop = time->tm_hour * 100 + time->tm_min;
|
||||
if (stop >= 2400)
|
||||
stop -= 2400;
|
||||
priority = Setup.DefaultPriority;
|
||||
lifetime = Setup.DefaultLifetime;
|
||||
*file = 0;
|
||||
const char *Title = EventInfo->GetTitle();
|
||||
if (!isempty(Title))
|
||||
strn0cpy(file, EventInfo->GetTitle(), sizeof(file));
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
}
|
||||
|
||||
cTimer::~cTimer()
|
||||
{
|
||||
free(summary);
|
||||
}
|
||||
|
||||
cTimer& cTimer::operator= (const cTimer &Timer)
|
||||
{
|
||||
memcpy(this, &Timer, sizeof(*this));
|
||||
if (summary)
|
||||
summary = strdup(summary);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool cTimer::operator< (const cListObject &ListObject)
|
||||
{
|
||||
cTimer *ti = (cTimer *)&ListObject;
|
||||
time_t t1 = StartTime();
|
||||
time_t t2 = ti->StartTime();
|
||||
return t1 < t2 || (t1 == t2 && priority > ti->priority);
|
||||
}
|
||||
|
||||
const char *cTimer::ToText(cTimer *Timer)
|
||||
{
|
||||
free(buffer);
|
||||
strreplace(Timer->file, ':', '|');
|
||||
strreplace(Timer->summary, '\n', '|');
|
||||
asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day, Timer->firstday), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
|
||||
strreplace(Timer->summary, '|', '\n');
|
||||
strreplace(Timer->file, '|', ':');
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *cTimer::ToText(void)
|
||||
{
|
||||
return ToText(this);
|
||||
}
|
||||
|
||||
int cTimer::TimeToInt(int t)
|
||||
{
|
||||
return (t / 100 * 60 + t % 100) * 60;
|
||||
}
|
||||
|
||||
int cTimer::ParseDay(const char *s, time_t *FirstDay)
|
||||
{
|
||||
char *tail;
|
||||
int d = strtol(s, &tail, 10);
|
||||
if (FirstDay)
|
||||
*FirstDay = 0;
|
||||
if (tail && *tail) {
|
||||
d = 0;
|
||||
if (tail == s) {
|
||||
const char *first = strchr(s, '@');
|
||||
int l = first ? first - s : strlen(s);
|
||||
if (l == 7) {
|
||||
for (const char *p = s + 6; p >= s; p--) {
|
||||
d <<= 1;
|
||||
d |= (*p != '-');
|
||||
}
|
||||
d |= 0x80000000;
|
||||
}
|
||||
if (FirstDay && first) {
|
||||
++first;
|
||||
if (strlen(first) == 10) {
|
||||
struct tm tm_r;
|
||||
if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
|
||||
tm_r.tm_year -= 1900;
|
||||
tm_r.tm_mon--;
|
||||
tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
|
||||
tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
*FirstDay = mktime(&tm_r);
|
||||
}
|
||||
}
|
||||
else
|
||||
d = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (d < 1 || d > 31)
|
||||
d = 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
const char *cTimer::PrintDay(int d, time_t FirstDay)
|
||||
{
|
||||
#define DAYBUFFERSIZE 32
|
||||
static char buffer[DAYBUFFERSIZE];
|
||||
if ((d & 0x80000000) != 0) {
|
||||
char *b = buffer;
|
||||
const char *w = tr("MTWTFSS");
|
||||
while (*w) {
|
||||
*b++ = (d & 1) ? *w : '-';
|
||||
d >>= 1;
|
||||
w++;
|
||||
}
|
||||
if (FirstDay) {
|
||||
struct tm tm_r;
|
||||
localtime_r(&FirstDay, &tm_r);
|
||||
b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
|
||||
}
|
||||
*b = 0;
|
||||
}
|
||||
else
|
||||
sprintf(buffer, "%d", d);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *cTimer::PrintFirstDay(void)
|
||||
{
|
||||
if (firstday) {
|
||||
const char *s = PrintDay(day, firstday);
|
||||
if (strlen(s) == 18)
|
||||
return s + 8;
|
||||
}
|
||||
return ""; // not NULL, so the caller can always use the result
|
||||
}
|
||||
|
||||
bool cTimer::Parse(const char *s)
|
||||
{
|
||||
char *buffer1 = NULL;
|
||||
char *buffer2 = NULL;
|
||||
free(summary);
|
||||
summary = NULL;
|
||||
//XXX Apparently sscanf() doesn't work correctly if the last %a argument
|
||||
//XXX results in an empty string (this first occured when the EIT gathering
|
||||
//XXX was put into a separate thread - don't know why this happens...
|
||||
//XXX As a cure we copy the original string and add a blank.
|
||||
//XXX If anybody can shed some light on why sscanf() failes here, I'd love
|
||||
//XXX to hear about that!
|
||||
char *s2 = NULL;
|
||||
int l2 = strlen(s);
|
||||
while (l2 > 0 && isspace(s[l2 - 1]))
|
||||
l2--;
|
||||
if (s[l2 - 1] == ':') {
|
||||
s2 = MALLOC(char, l2 + 3);
|
||||
strcat(strn0cpy(s2, s, l2 + 1), " \n");
|
||||
s = s2;
|
||||
}
|
||||
if (8 <= sscanf(s, "%d :%d :%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
|
||||
if (summary && !*skipspace(summary)) {
|
||||
free(summary);
|
||||
summary = NULL;
|
||||
}
|
||||
//TODO add more plausibility checks
|
||||
day = ParseDay(buffer1, &firstday);
|
||||
strn0cpy(file, buffer2, MaxFileName);
|
||||
strreplace(file, '|', ':');
|
||||
strreplace(summary, '|', '\n');
|
||||
free(buffer1);
|
||||
free(buffer2);
|
||||
free(s2);
|
||||
return day != 0;
|
||||
}
|
||||
free(s2);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cTimer::Save(FILE *f)
|
||||
{
|
||||
return fprintf(f, ToText()) > 0;
|
||||
}
|
||||
|
||||
bool cTimer::IsSingleEvent(void)
|
||||
{
|
||||
return (day & 0x80000000) == 0;
|
||||
}
|
||||
|
||||
int cTimer::GetMDay(time_t t)
|
||||
{
|
||||
struct tm tm_r;
|
||||
return localtime_r(&t, &tm_r)->tm_mday;
|
||||
}
|
||||
|
||||
int cTimer::GetWDay(time_t t)
|
||||
{
|
||||
struct tm tm_r;
|
||||
int weekday = localtime_r(&t, &tm_r)->tm_wday;
|
||||
return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
|
||||
}
|
||||
|
||||
bool cTimer::DayMatches(time_t t)
|
||||
{
|
||||
return IsSingleEvent() ? GetMDay(t) == day : (day & (1 << GetWDay(t))) != 0;
|
||||
}
|
||||
|
||||
time_t cTimer::IncDay(time_t t, int Days)
|
||||
{
|
||||
struct tm tm_r;
|
||||
tm tm = *localtime_r(&t, &tm_r);
|
||||
tm.tm_mday += Days; // now tm_mday may be out of its valid range
|
||||
int h = tm.tm_hour; // save original hour to compensate for DST change
|
||||
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
t = mktime(&tm); // normalize all values
|
||||
tm.tm_hour = h; // compensate for DST change
|
||||
return mktime(&tm); // calculate final result
|
||||
}
|
||||
|
||||
time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
|
||||
{
|
||||
struct tm tm_r;
|
||||
tm tm = *localtime_r(&t, &tm_r);
|
||||
tm.tm_hour = SecondsFromMidnight / 3600;
|
||||
tm.tm_min = (SecondsFromMidnight % 3600) / 60;
|
||||
tm.tm_sec = SecondsFromMidnight % 60;
|
||||
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
return mktime(&tm);
|
||||
}
|
||||
|
||||
char *cTimer::SetFile(const char *File)
|
||||
{
|
||||
if (!isempty(File))
|
||||
strn0cpy(file, File, sizeof(file));
|
||||
return file;
|
||||
}
|
||||
|
||||
bool cTimer::Matches(time_t t)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
if (t == 0)
|
||||
t = time(NULL);
|
||||
|
||||
int begin = TimeToInt(start); // seconds from midnight
|
||||
int length = TimeToInt(stop) - begin;
|
||||
if (length < 0)
|
||||
length += SECSINDAY;
|
||||
|
||||
int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
|
||||
for (int i = -1; i <= DaysToCheck; i++) {
|
||||
time_t t0 = IncDay(t, i);
|
||||
if (DayMatches(t0)) {
|
||||
time_t a = SetTime(t0, begin);
|
||||
time_t b = a + length;
|
||||
if ((!firstday || a >= firstday) && t <= b) {
|
||||
startTime = a;
|
||||
stopTime = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!startTime)
|
||||
startTime = firstday; // just to have something that's more than a week in the future
|
||||
else if (t > startTime || t > firstday + SECSINDAY + 3600) // +3600 in case of DST change
|
||||
firstday = 0;
|
||||
return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
|
||||
}
|
||||
|
||||
time_t cTimer::StartTime(void)
|
||||
{
|
||||
if (!startTime)
|
||||
Matches();
|
||||
return startTime;
|
||||
}
|
||||
|
||||
time_t cTimer::StopTime(void)
|
||||
{
|
||||
if (!stopTime)
|
||||
Matches();
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
void cTimer::SetRecording(bool Recording)
|
||||
{
|
||||
recording = Recording;
|
||||
isyslog("timer %d %s", Index() + 1, recording ? "start" : "stop");
|
||||
}
|
||||
|
||||
void cTimer::SetPending(bool Pending)
|
||||
{
|
||||
pending = Pending;
|
||||
}
|
||||
|
||||
void cTimer::Skip(void)
|
||||
{
|
||||
firstday = IncDay(SetTime(StartTime(), 0), 1);
|
||||
}
|
||||
|
||||
// --- cCommand -------------------------------------------------------------
|
||||
|
||||
char *cCommand::result = NULL;
|
||||
@ -476,47 +142,6 @@ bool cCaDefinition::Parse(const char *s)
|
||||
cCommands Commands;
|
||||
cCommands RecordingCommands;
|
||||
|
||||
// -- cTimers ----------------------------------------------------------------
|
||||
|
||||
cTimers Timers;
|
||||
|
||||
cTimer *cTimers::GetTimer(cTimer *Timer)
|
||||
{
|
||||
cTimer *ti = (cTimer *)First();
|
||||
while (ti) {
|
||||
if (ti->channel == Timer->channel && ti->day == Timer->day && ti->start == Timer->start && ti->stop == Timer->stop)
|
||||
return ti;
|
||||
ti = (cTimer *)ti->Next();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetMatch(time_t t)
|
||||
{
|
||||
cTimer *t0 = NULL;
|
||||
cTimer *ti = First();
|
||||
while (ti) {
|
||||
if (!ti->recording && ti->Matches(t)) {
|
||||
if (!t0 || ti->priority > t0->priority)
|
||||
t0 = ti;
|
||||
}
|
||||
ti = (cTimer *)ti->Next();
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetNextActiveTimer(void)
|
||||
{
|
||||
cTimer *t0 = NULL;
|
||||
cTimer *ti = First();
|
||||
while (ti) {
|
||||
if (ti->active && (!t0 || *ti < *t0))
|
||||
t0 = ti;
|
||||
ti = (cTimer *)ti->Next();
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
// -- cSVDRPhosts ------------------------------------------------------------
|
||||
|
||||
cSVDRPhosts SVDRPhosts;
|
||||
|
61
config.h
61
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 1.136 2002/10/19 11:29:46 kls Exp $
|
||||
* $Id: config.h 1.137 2002/10/19 15:43:31 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
@ -32,57 +32,6 @@
|
||||
|
||||
#define MaxFileName 256
|
||||
|
||||
enum eTimerActive { taInactive = 0,
|
||||
taActive = 1,
|
||||
taInstant = 2,
|
||||
taActInst = (taActive | taInstant)
|
||||
};
|
||||
|
||||
class cTimer : public cListObject {
|
||||
private:
|
||||
time_t startTime, stopTime;
|
||||
static char *buffer;
|
||||
static const char *ToText(cTimer *Timer);
|
||||
public:
|
||||
bool recording, pending;
|
||||
int active;
|
||||
int channel;
|
||||
int day;
|
||||
int start;
|
||||
int stop;
|
||||
//TODO VPS???
|
||||
int priority;
|
||||
int lifetime;
|
||||
char file[MaxFileName];
|
||||
time_t firstday;
|
||||
char *summary;
|
||||
cTimer(bool Instant = false);
|
||||
cTimer(const cEventInfo *EventInfo);
|
||||
virtual ~cTimer();
|
||||
cTimer& operator= (const cTimer &Timer);
|
||||
virtual bool operator< (const cListObject &ListObject);
|
||||
const char *ToText(void);
|
||||
bool Parse(const char *s);
|
||||
bool Save(FILE *f);
|
||||
bool IsSingleEvent(void);
|
||||
int GetMDay(time_t t);
|
||||
int GetWDay(time_t t);
|
||||
bool DayMatches(time_t t);
|
||||
static time_t IncDay(time_t t, int Days);
|
||||
static time_t SetTime(time_t t, int SecondsFromMidnight);
|
||||
char *SetFile(const char *File);
|
||||
bool Matches(time_t t = 0);
|
||||
time_t StartTime(void);
|
||||
time_t StopTime(void);
|
||||
void SetRecording(bool Recording);
|
||||
void SetPending(bool Pending);
|
||||
void Skip(void);
|
||||
const char *PrintFirstDay(void);
|
||||
static int TimeToInt(int t);
|
||||
static int ParseDay(const char *s, time_t *FirstDay = NULL);
|
||||
static const char *PrintDay(int d, time_t FirstDay = 0);
|
||||
};
|
||||
|
||||
class cCommand : public cListObject {
|
||||
private:
|
||||
char *title;
|
||||
@ -203,13 +152,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class cTimers : public cConfig<cTimer> {
|
||||
public:
|
||||
cTimer *GetTimer(cTimer *Timer);
|
||||
cTimer *GetMatch(time_t t);
|
||||
cTimer *GetNextActiveTimer(void);
|
||||
};
|
||||
|
||||
class cCommands : public cConfig<cCommand> {};
|
||||
|
||||
class cSVDRPhosts : public cConfig<cSVDRPhost> {
|
||||
@ -222,7 +164,6 @@ public:
|
||||
const cCaDefinition *Get(int Number);
|
||||
};
|
||||
|
||||
extern cTimers Timers;
|
||||
extern cCommands Commands;
|
||||
extern cCommands RecordingCommands;
|
||||
extern cSVDRPhosts SVDRPhosts;
|
||||
|
158
menu.c
158
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.218 2002/10/19 15:33:37 kls Exp $
|
||||
* $Id: menu.c 1.219 2002/10/20 12:03:13 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -24,6 +24,7 @@
|
||||
#include "remote.h"
|
||||
#include "sources.h"
|
||||
#include "status.h"
|
||||
#include "timers.h"
|
||||
#include "videodir.h"
|
||||
|
||||
#define MENUTIMEOUT 120 // seconds
|
||||
@ -617,17 +618,14 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)
|
||||
|
||||
class cMenuChannelItem : public cOsdItem {
|
||||
private:
|
||||
int index;
|
||||
cChannel *channel;
|
||||
public:
|
||||
cMenuChannelItem(int Index, cChannel *Channel);
|
||||
cMenuChannelItem(cChannel *Channel);
|
||||
virtual void Set(void);
|
||||
void SetIndex(int Index);
|
||||
};
|
||||
|
||||
cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel)
|
||||
cMenuChannelItem::cMenuChannelItem(cChannel *Channel)
|
||||
{
|
||||
index = Index;
|
||||
channel = Channel;
|
||||
if (channel->GroupSep())
|
||||
SetColor(clrCyan, clrBackground);
|
||||
@ -644,15 +642,11 @@ void cMenuChannelItem::Set(void)
|
||||
SetText(buffer, false);
|
||||
}
|
||||
|
||||
void cMenuChannelItem::SetIndex(int Index)
|
||||
{
|
||||
index = Index;
|
||||
Set();
|
||||
}
|
||||
|
||||
// --- cMenuChannels ---------------------------------------------------------
|
||||
|
||||
class cMenuChannels : public cOsdMenu {
|
||||
private:
|
||||
void Propagate(void);
|
||||
protected:
|
||||
eOSState Switch(void);
|
||||
eOSState Edit(void);
|
||||
@ -673,12 +667,22 @@ cMenuChannels::cMenuChannels(void)
|
||||
int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;
|
||||
|
||||
while ((channel = Channels.Get(i)) != NULL) {
|
||||
Add(new cMenuChannelItem(i, channel), i == curr);
|
||||
Add(new cMenuChannelItem(channel), i == curr);
|
||||
i++;
|
||||
}
|
||||
SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark"));
|
||||
}
|
||||
|
||||
void cMenuChannels::Propagate(void)
|
||||
{
|
||||
Channels.ReNumber();
|
||||
Channels.Save();
|
||||
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
|
||||
ci->Set();
|
||||
Timers.Save(); // channel numbering has changed!
|
||||
Display();
|
||||
}
|
||||
|
||||
eOSState cMenuChannels::Switch(void)
|
||||
{
|
||||
cChannel *ch = Channels.Get(Current());
|
||||
@ -702,7 +706,7 @@ eOSState cMenuChannels::New(void)
|
||||
cChannel *channel = new cChannel(Channels.Get(Current()));
|
||||
Channels.Add(channel);
|
||||
Channels.ReNumber();
|
||||
Add(new cMenuChannelItem(channel->Index()/*XXX*/, channel), true);
|
||||
Add(new cMenuChannelItem(channel), true);
|
||||
Channels.Save();
|
||||
isyslog("channel %d added", channel->Number());
|
||||
return AddSubMenu(new cMenuEditChannel(Current()));
|
||||
@ -715,36 +719,17 @@ eOSState cMenuChannels::Del(void)
|
||||
cChannel *channel = Channels.Get(Index);
|
||||
int DeletedChannel = channel->Number();
|
||||
// Check if there is a timer using this channel:
|
||||
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
|
||||
if (ti->channel == DeletedChannel) {
|
||||
for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) {
|
||||
if (ti->Channel() == channel) {
|
||||
Interface->Error(tr("Channel is being used by a timer!"));
|
||||
return osContinue;
|
||||
}
|
||||
}
|
||||
if (Interface->Confirm(tr("Delete channel?"))) {
|
||||
// Move and renumber the channels:
|
||||
Channels.Del(channel);
|
||||
Channels.ReNumber();
|
||||
cOsdMenu::Del(Index);
|
||||
int i = 0;
|
||||
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
|
||||
ci->SetIndex(i++);
|
||||
Channels.Save();
|
||||
Propagate();
|
||||
isyslog("channel %d deleted", DeletedChannel);
|
||||
// Fix the timers:
|
||||
bool TimersModified = false;
|
||||
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
|
||||
int OldChannel = ti->channel;
|
||||
if (ti->channel > DeletedChannel)
|
||||
ti->channel--;
|
||||
if (ti->channel != OldChannel) {
|
||||
TimersModified = true;
|
||||
isyslog("timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel);
|
||||
}
|
||||
}
|
||||
if (TimersModified)
|
||||
Timers.Save();
|
||||
Display();
|
||||
}
|
||||
}
|
||||
return osContinue;
|
||||
@ -754,35 +739,10 @@ void cMenuChannels::Move(int From, int To)
|
||||
{
|
||||
int FromNumber = Channels.Get(From)->Number();
|
||||
int ToNumber = Channels.Get(To)->Number();
|
||||
// Move and renumber the channels:
|
||||
Channels.Move(From, To);
|
||||
Channels.ReNumber();
|
||||
cOsdMenu::Move(From, To);
|
||||
int i = 0;
|
||||
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
|
||||
ci->SetIndex(i++);
|
||||
Channels.Save();
|
||||
Propagate();
|
||||
isyslog("channel %d moved to %d", FromNumber, ToNumber);
|
||||
// Fix the timers:
|
||||
bool TimersModified = false;
|
||||
From++; // user visible channel numbers start with '1'
|
||||
To++;
|
||||
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
|
||||
int OldChannel = ti->channel;
|
||||
if (ti->channel == FromNumber)
|
||||
ti->channel = ToNumber;
|
||||
else if (ti->channel > FromNumber && ti->channel <= ToNumber)
|
||||
ti->channel--;
|
||||
else if (ti->channel < FromNumber && ti->channel >= ToNumber)
|
||||
ti->channel++;
|
||||
if (ti->channel != OldChannel) {
|
||||
TimersModified = true;
|
||||
isyslog("timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel);
|
||||
}
|
||||
}
|
||||
if (TimersModified)
|
||||
Timers.Save();
|
||||
Display();
|
||||
}
|
||||
|
||||
eOSState cMenuChannels::ProcessKey(eKeys Key)
|
||||
@ -835,6 +795,7 @@ class cMenuEditTimer : public cOsdMenu {
|
||||
private:
|
||||
cTimer *timer;
|
||||
cTimer data;
|
||||
int channel;
|
||||
cMenuEditDateItem *firstday;
|
||||
void SetFirstDayItem(void);
|
||||
public:
|
||||
@ -851,12 +812,12 @@ cMenuEditTimer::cMenuEditTimer(int Index, bool New)
|
||||
data = *timer;
|
||||
if (New)
|
||||
data.active = 1;
|
||||
channel = data.Channel()->Number();
|
||||
Add(new cMenuEditBoolItem(tr("Active"), &data.active));
|
||||
Add(new cMenuEditChanItem(tr("Channel"), &data.channel));
|
||||
Add(new cMenuEditChanItem(tr("Channel"), &channel));
|
||||
Add(new cMenuEditDayItem( tr("Day"), &data.day));
|
||||
Add(new cMenuEditTimeItem(tr("Start"), &data.start));
|
||||
Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
|
||||
//TODO VPS???
|
||||
Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
|
||||
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
|
||||
Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars)));
|
||||
@ -884,8 +845,16 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
|
||||
|
||||
if (state == osUnknown) {
|
||||
switch (Key) {
|
||||
case kOk: if (!*data.file)
|
||||
strcpy(data.file, Channels.GetChannelNameByNumber(data.channel));
|
||||
case kOk: {
|
||||
cChannel *ch = Channels.GetByNumber(channel);
|
||||
if (ch)
|
||||
data.channel = ch;
|
||||
else {
|
||||
Interface->Error(tr("*** Invalid Channel ***"));
|
||||
break;
|
||||
}
|
||||
if (!*data.file)
|
||||
strcpy(data.file, data.Channel()->Name());
|
||||
if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
|
||||
*timer = data;
|
||||
if (timer->active)
|
||||
@ -893,6 +862,7 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
|
||||
Timers.Save();
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
|
||||
}
|
||||
}
|
||||
return osBack;
|
||||
case kRed:
|
||||
case kGreen:
|
||||
@ -933,14 +903,14 @@ void cMenuTimerItem::Set(void)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
|
||||
!timer->active ? ' ' : timer->firstday ? '!' : timer->recording ? '#' : '>',
|
||||
timer->channel,
|
||||
timer->PrintDay(timer->day),
|
||||
timer->start / 100,
|
||||
timer->start % 100,
|
||||
timer->stop / 100,
|
||||
timer->stop % 100,
|
||||
timer->file);
|
||||
!timer->Active() ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
|
||||
timer->Channel()->Number(),
|
||||
timer->PrintDay(timer->Day()),
|
||||
timer->Start() / 100,
|
||||
timer->Start() % 100,
|
||||
timer->Stop() / 100,
|
||||
timer->Stop() % 100,
|
||||
timer->File());
|
||||
SetText(buffer, false);
|
||||
}
|
||||
|
||||
@ -985,23 +955,13 @@ eOSState cMenuTimers::OnOff(void)
|
||||
{
|
||||
cTimer *timer = CurrentTimer();
|
||||
if (timer) {
|
||||
if (timer->IsSingleEvent())
|
||||
timer->active = !timer->active;
|
||||
else if (timer->firstday) {
|
||||
timer->firstday = 0;
|
||||
timer->active = false;
|
||||
}
|
||||
else if (timer->active)
|
||||
timer->Skip();
|
||||
else
|
||||
timer->active = true;
|
||||
timer->Matches(); // refresh start and end time
|
||||
timer->OnOff();
|
||||
RefreshCurrent();
|
||||
DisplayCurrent(true);
|
||||
if (timer->firstday)
|
||||
if (timer->FirstDay())
|
||||
isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
|
||||
else
|
||||
isyslog("timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de");
|
||||
isyslog("timer %d %sactivated", timer->Index() + 1, timer->Active() ? "" : "de");
|
||||
Timers.Save();
|
||||
}
|
||||
return osContinue;
|
||||
@ -1032,7 +992,7 @@ eOSState cMenuTimers::Del(void)
|
||||
// Check if this timer is active:
|
||||
cTimer *ti = CurrentTimer();
|
||||
if (ti) {
|
||||
if (!ti->recording) {
|
||||
if (!ti->Recording()) {
|
||||
if (Interface->Confirm(tr("Delete timer?"))) {
|
||||
int Index = ti->Index();
|
||||
Timers.Del(ti);
|
||||
@ -1062,8 +1022,8 @@ eOSState cMenuTimers::Summary(void)
|
||||
if (HasSubMenu() || Count() == 0)
|
||||
return osContinue;
|
||||
cTimer *ti = CurrentTimer();
|
||||
if (ti && ti->summary && *ti->summary)
|
||||
return AddSubMenu(new cMenuText(tr("Summary"), ti->summary));
|
||||
if (ti && !isempty(ti->Summary()))
|
||||
return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
|
||||
return Edit(); // convenience for people not using the Summary feature ;-)
|
||||
}
|
||||
|
||||
@ -2675,7 +2635,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
|
||||
timer = new cTimer(true);
|
||||
Timers.Add(timer);
|
||||
Timers.Save();
|
||||
asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), device->CardIndex() + 1);
|
||||
asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->CardIndex() + 1);
|
||||
}
|
||||
timer->SetPending(true);
|
||||
timer->SetRecording(true);
|
||||
@ -2692,8 +2652,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
|
||||
cRecording Recording(timer, Title, Subtitle, Summary);
|
||||
fileName = strdup(Recording.FileName());
|
||||
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
|
||||
cChannel *ch = Channels.GetByNumber(timer->channel);
|
||||
recorder = new cRecorder(fileName, ch->Ca(), timer->priority, ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
|
||||
const cChannel *ch = timer->Channel();
|
||||
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
|
||||
if (device->AttachReceiver(recorder)) {
|
||||
Recording.WriteSummary();
|
||||
cStatus::MsgRecording(device, Recording.Name());
|
||||
@ -2713,8 +2673,8 @@ cRecordControl::~cRecordControl()
|
||||
|
||||
bool cRecordControl::GetEventInfo(void)
|
||||
{
|
||||
cChannel *channel = Channels.GetByNumber(timer->channel);
|
||||
time_t Time = timer->active == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
|
||||
const cChannel *channel = timer->Channel();
|
||||
time_t Time = timer->Active() == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
|
||||
for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
|
||||
{
|
||||
cMutexLock MutexLock;
|
||||
@ -2759,7 +2719,7 @@ bool cRecordControl::Process(time_t t)
|
||||
{
|
||||
if (!recorder || !timer || !timer->Matches(t))
|
||||
return false;
|
||||
AssertFreeDiskSpace(timer->priority);
|
||||
AssertFreeDiskSpace(timer->Priority());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2769,12 +2729,12 @@ cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
|
||||
|
||||
bool cRecordControls::Start(cTimer *Timer)
|
||||
{
|
||||
int ch = Timer ? Timer->channel : cDevice::CurrentChannel();
|
||||
int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel();
|
||||
cChannel *channel = Channels.GetByNumber(ch);
|
||||
|
||||
if (channel) {
|
||||
bool NeedsDetachReceivers = false;
|
||||
cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->priority : Setup.DefaultPriority, &NeedsDetachReceivers);
|
||||
cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->Priority() : Setup.DefaultPriority, &NeedsDetachReceivers);
|
||||
if (device) {
|
||||
if (NeedsDetachReceivers)
|
||||
Stop(device);
|
||||
@ -2789,7 +2749,7 @@ bool cRecordControls::Start(cTimer *Timer)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!Timer || (Timer->priority >= Setup.PrimaryLimit && !Timer->pending))
|
||||
else if (!Timer || (Timer->Priority() >= Setup.PrimaryLimit && !Timer->Pending()))
|
||||
isyslog("no free DVB device to record channel %d!", ch);
|
||||
}
|
||||
else
|
||||
|
20
recording.c
20
recording.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.c 1.69 2002/10/13 09:08:45 kls Exp $
|
||||
* $Id: recording.c 1.70 2002/10/20 11:54:29 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@ -307,13 +307,13 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
|
||||
name = NULL;
|
||||
// set up the actual name:
|
||||
if (isempty(Title))
|
||||
Title = Channels.GetChannelNameByNumber(Timer->channel);
|
||||
Title = Timer->Channel()->Name();
|
||||
if (isempty(Subtitle))
|
||||
Subtitle = " ";
|
||||
char *macroTITLE = strstr(Timer->file, TIMERMACRO_TITLE);
|
||||
char *macroEPISODE = strstr(Timer->file, TIMERMACRO_EPISODE);
|
||||
char *macroTITLE = strstr(Timer->File(), TIMERMACRO_TITLE);
|
||||
char *macroEPISODE = strstr(Timer->File(), TIMERMACRO_EPISODE);
|
||||
if (macroTITLE || macroEPISODE) {
|
||||
name = strdup(Timer->file);
|
||||
name = strdup(Timer->File());
|
||||
name = strreplace(name, TIMERMACRO_TITLE, Title);
|
||||
name = strreplace(name, TIMERMACRO_EPISODE, Subtitle);
|
||||
if (Timer->IsSingleEvent()) {
|
||||
@ -322,16 +322,16 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
|
||||
}
|
||||
}
|
||||
else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
|
||||
name = strdup(Timer->file);
|
||||
name = strdup(Timer->File());
|
||||
else
|
||||
asprintf(&name, "%s~%s", Timer->file, Subtitle);
|
||||
asprintf(&name, "%s~%s", Timer->File(), Subtitle);
|
||||
// substitute characters that would cause problems in file names:
|
||||
strreplace(name, '\n', ' ');
|
||||
start = Timer->StartTime();
|
||||
priority = Timer->priority;
|
||||
lifetime = Timer->lifetime;
|
||||
priority = Timer->Priority();
|
||||
lifetime = Timer->Lifetime();
|
||||
// handle summary:
|
||||
summary = !isempty(Timer->summary) ? strdup(Timer->summary) : NULL;
|
||||
summary = !isempty(Timer->Summary()) ? strdup(Timer->Summary()) : NULL;
|
||||
if (!summary) {
|
||||
if (isempty(Subtitle))
|
||||
Subtitle = "";
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.h 1.24 2002/06/22 10:09:27 kls Exp $
|
||||
* $Id: recording.h 1.25 2002/10/19 15:48:52 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDING_H
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#include <time.h>
|
||||
#include "config.h"
|
||||
#include "timers.h"
|
||||
#include "tools.h"
|
||||
|
||||
void RemoveDeletedRecordings(void);
|
||||
|
11
svdrp.c
11
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 1.46 2002/10/19 11:48:02 kls Exp $
|
||||
* $Id: svdrp.c 1.47 2002/10/20 10:24:20 kls Exp $
|
||||
*/
|
||||
|
||||
#include "svdrp.h"
|
||||
@ -31,6 +31,7 @@
|
||||
#include "device.h"
|
||||
#include "keys.h"
|
||||
#include "remote.h"
|
||||
#include "timers.h"
|
||||
#include "tools.h"
|
||||
|
||||
// --- cSocket ---------------------------------------------------------------
|
||||
@ -487,7 +488,7 @@ void cSVDRP::CmdDELT(const char *Option)
|
||||
if (isnumber(Option)) {
|
||||
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (timer) {
|
||||
if (!timer->recording) {
|
||||
if (!timer->Recording()) {
|
||||
Timers.Del(timer);
|
||||
Timers.Save();
|
||||
isyslog("timer %s deleted", Option);
|
||||
@ -806,16 +807,16 @@ void cSVDRP::CmdMODT(const char *Option)
|
||||
if (timer) {
|
||||
cTimer t = *timer;
|
||||
if (strcasecmp(tail, "ON") == 0)
|
||||
t.active = 1;
|
||||
t.SetActive(taActive);
|
||||
else if (strcasecmp(tail, "OFF") == 0)
|
||||
t.active = 0;
|
||||
t.SetActive(taInactive);
|
||||
else if (!t.Parse(tail)) {
|
||||
Reply(501, "Error in timer settings");
|
||||
return;
|
||||
}
|
||||
*timer = t;
|
||||
Timers.Save();
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1, timer->Active() ? "active" : "inactive");
|
||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
|
||||
}
|
||||
else
|
||||
|
408
timers.c
Normal file
408
timers.c
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* timers.c: Timer handling
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.c 1.1 2002/10/20 12:28:55 kls Exp $
|
||||
*/
|
||||
|
||||
#include "timers.h"
|
||||
#include <ctype.h>
|
||||
#include "channels.h"
|
||||
#include "i18n.h"
|
||||
|
||||
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
|
||||
// format characters in order to allow any number of blanks after a numeric
|
||||
// value!
|
||||
|
||||
// -- cTimer -----------------------------------------------------------------
|
||||
|
||||
char *cTimer::buffer = NULL;
|
||||
|
||||
cTimer::cTimer(bool Instant)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = Instant ? taActInst : taInactive;
|
||||
channel = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
time_t t = time(NULL);
|
||||
struct tm tm_r;
|
||||
struct tm *now = localtime_r(&t, &tm_r);
|
||||
day = now->tm_mday;
|
||||
start = now->tm_hour * 100 + now->tm_min;
|
||||
stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
|
||||
stop = (stop / 60) * 100 + (stop % 60);
|
||||
if (stop >= 2400)
|
||||
stop -= 2400;
|
||||
priority = Setup.DefaultPriority;
|
||||
lifetime = Setup.DefaultLifetime;
|
||||
*file = 0;
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
if (Instant && channel)
|
||||
snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
|
||||
}
|
||||
|
||||
cTimer::cTimer(const cEventInfo *EventInfo)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
recording = pending = false;
|
||||
active = true;
|
||||
channel = Channels.GetByServiceID(EventInfo->GetServiceID());
|
||||
time_t tstart = EventInfo->GetTime();
|
||||
time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
|
||||
tstart -= Setup.MarginStart * 60;
|
||||
struct tm tm_r;
|
||||
struct tm *time = localtime_r(&tstart, &tm_r);
|
||||
day = time->tm_mday;
|
||||
start = time->tm_hour * 100 + time->tm_min;
|
||||
time = localtime_r(&tstop, &tm_r);
|
||||
stop = time->tm_hour * 100 + time->tm_min;
|
||||
if (stop >= 2400)
|
||||
stop -= 2400;
|
||||
priority = Setup.DefaultPriority;
|
||||
lifetime = Setup.DefaultLifetime;
|
||||
*file = 0;
|
||||
const char *Title = EventInfo->GetTitle();
|
||||
if (!isempty(Title))
|
||||
strn0cpy(file, EventInfo->GetTitle(), sizeof(file));
|
||||
firstday = 0;
|
||||
summary = NULL;
|
||||
}
|
||||
|
||||
cTimer::~cTimer()
|
||||
{
|
||||
free(summary);
|
||||
}
|
||||
|
||||
cTimer& cTimer::operator= (const cTimer &Timer)
|
||||
{
|
||||
memcpy(this, &Timer, sizeof(*this));
|
||||
if (summary)
|
||||
summary = strdup(summary);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool cTimer::operator< (const cListObject &ListObject)
|
||||
{
|
||||
cTimer *ti = (cTimer *)&ListObject;
|
||||
time_t t1 = StartTime();
|
||||
time_t t2 = ti->StartTime();
|
||||
return t1 < t2 || (t1 == t2 && priority > ti->priority);
|
||||
}
|
||||
|
||||
const char *cTimer::ToText(cTimer *Timer)
|
||||
{
|
||||
free(buffer);
|
||||
strreplace(Timer->file, ':', '|');
|
||||
strreplace(Timer->summary, '\n', '|');
|
||||
asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->Channel()->Number(), PrintDay(Timer->day, Timer->firstday), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
|
||||
strreplace(Timer->summary, '|', '\n');
|
||||
strreplace(Timer->file, '|', ':');
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *cTimer::ToText(void)
|
||||
{
|
||||
return ToText(this);
|
||||
}
|
||||
|
||||
int cTimer::TimeToInt(int t)
|
||||
{
|
||||
return (t / 100 * 60 + t % 100) * 60;
|
||||
}
|
||||
|
||||
int cTimer::ParseDay(const char *s, time_t *FirstDay)
|
||||
{
|
||||
char *tail;
|
||||
int d = strtol(s, &tail, 10);
|
||||
if (FirstDay)
|
||||
*FirstDay = 0;
|
||||
if (tail && *tail) {
|
||||
d = 0;
|
||||
if (tail == s) {
|
||||
const char *first = strchr(s, '@');
|
||||
int l = first ? first - s : strlen(s);
|
||||
if (l == 7) {
|
||||
for (const char *p = s + 6; p >= s; p--) {
|
||||
d <<= 1;
|
||||
d |= (*p != '-');
|
||||
}
|
||||
d |= 0x80000000;
|
||||
}
|
||||
if (FirstDay && first) {
|
||||
++first;
|
||||
if (strlen(first) == 10) {
|
||||
struct tm tm_r;
|
||||
if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
|
||||
tm_r.tm_year -= 1900;
|
||||
tm_r.tm_mon--;
|
||||
tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
|
||||
tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
*FirstDay = mktime(&tm_r);
|
||||
}
|
||||
}
|
||||
else
|
||||
d = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (d < 1 || d > 31)
|
||||
d = 0;
|
||||
return d;
|
||||
}
|
||||
|
||||
const char *cTimer::PrintDay(int d, time_t FirstDay)
|
||||
{
|
||||
#define DAYBUFFERSIZE 32
|
||||
static char buffer[DAYBUFFERSIZE];
|
||||
if ((d & 0x80000000) != 0) {
|
||||
char *b = buffer;
|
||||
const char *w = tr("MTWTFSS");
|
||||
while (*w) {
|
||||
*b++ = (d & 1) ? *w : '-';
|
||||
d >>= 1;
|
||||
w++;
|
||||
}
|
||||
if (FirstDay) {
|
||||
struct tm tm_r;
|
||||
localtime_r(&FirstDay, &tm_r);
|
||||
b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
|
||||
}
|
||||
*b = 0;
|
||||
}
|
||||
else
|
||||
sprintf(buffer, "%d", d);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *cTimer::PrintFirstDay(void)
|
||||
{
|
||||
if (firstday) {
|
||||
const char *s = PrintDay(day, firstday);
|
||||
if (strlen(s) == 18)
|
||||
return s + 8;
|
||||
}
|
||||
return ""; // not NULL, so the caller can always use the result
|
||||
}
|
||||
|
||||
bool cTimer::Parse(const char *s)
|
||||
{
|
||||
char *buffer1 = NULL;
|
||||
char *buffer2 = NULL;
|
||||
free(summary);
|
||||
summary = NULL;
|
||||
//XXX Apparently sscanf() doesn't work correctly if the last %a argument
|
||||
//XXX results in an empty string (this first occured when the EIT gathering
|
||||
//XXX was put into a separate thread - don't know why this happens...
|
||||
//XXX As a cure we copy the original string and add a blank.
|
||||
//XXX If anybody can shed some light on why sscanf() failes here, I'd love
|
||||
//XXX to hear about that!
|
||||
char *s2 = NULL;
|
||||
int l2 = strlen(s);
|
||||
while (l2 > 0 && isspace(s[l2 - 1]))
|
||||
l2--;
|
||||
if (s[l2 - 1] == ':') {
|
||||
s2 = MALLOC(char, l2 + 3);
|
||||
strcat(strn0cpy(s2, s, l2 + 1), " \n");
|
||||
s = s2;
|
||||
}
|
||||
int ch = 0;
|
||||
if (8 <= sscanf(s, "%d :%d :%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &ch, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
|
||||
if (summary && !*skipspace(summary)) {
|
||||
free(summary);
|
||||
summary = NULL;
|
||||
}
|
||||
//TODO add more plausibility checks
|
||||
day = ParseDay(buffer1, &firstday);
|
||||
strn0cpy(file, buffer2, MaxFileName);
|
||||
strreplace(file, '|', ':');
|
||||
strreplace(summary, '|', '\n');
|
||||
free(buffer1);
|
||||
free(buffer2);
|
||||
free(s2);
|
||||
channel = Channels.GetByNumber(ch);
|
||||
if (!channel) {
|
||||
esyslog("ERROR: channel %d not defined", ch);
|
||||
return false;
|
||||
}
|
||||
return day != 0;
|
||||
}
|
||||
free(s2);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cTimer::Save(FILE *f)
|
||||
{
|
||||
return fprintf(f, ToText()) > 0;
|
||||
}
|
||||
|
||||
bool cTimer::IsSingleEvent(void)
|
||||
{
|
||||
return (day & 0x80000000) == 0;
|
||||
}
|
||||
|
||||
int cTimer::GetMDay(time_t t)
|
||||
{
|
||||
struct tm tm_r;
|
||||
return localtime_r(&t, &tm_r)->tm_mday;
|
||||
}
|
||||
|
||||
int cTimer::GetWDay(time_t t)
|
||||
{
|
||||
struct tm tm_r;
|
||||
int weekday = localtime_r(&t, &tm_r)->tm_wday;
|
||||
return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
|
||||
}
|
||||
|
||||
bool cTimer::DayMatches(time_t t)
|
||||
{
|
||||
return IsSingleEvent() ? GetMDay(t) == day : (day & (1 << GetWDay(t))) != 0;
|
||||
}
|
||||
|
||||
time_t cTimer::IncDay(time_t t, int Days)
|
||||
{
|
||||
struct tm tm_r;
|
||||
tm tm = *localtime_r(&t, &tm_r);
|
||||
tm.tm_mday += Days; // now tm_mday may be out of its valid range
|
||||
int h = tm.tm_hour; // save original hour to compensate for DST change
|
||||
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
t = mktime(&tm); // normalize all values
|
||||
tm.tm_hour = h; // compensate for DST change
|
||||
return mktime(&tm); // calculate final result
|
||||
}
|
||||
|
||||
time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
|
||||
{
|
||||
struct tm tm_r;
|
||||
tm tm = *localtime_r(&t, &tm_r);
|
||||
tm.tm_hour = SecondsFromMidnight / 3600;
|
||||
tm.tm_min = (SecondsFromMidnight % 3600) / 60;
|
||||
tm.tm_sec = SecondsFromMidnight % 60;
|
||||
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
|
||||
return mktime(&tm);
|
||||
}
|
||||
|
||||
char *cTimer::SetFile(const char *File)
|
||||
{
|
||||
if (!isempty(File))
|
||||
strn0cpy(file, File, sizeof(file));
|
||||
return file;
|
||||
}
|
||||
|
||||
bool cTimer::Matches(time_t t)
|
||||
{
|
||||
startTime = stopTime = 0;
|
||||
if (t == 0)
|
||||
t = time(NULL);
|
||||
|
||||
int begin = TimeToInt(start); // seconds from midnight
|
||||
int length = TimeToInt(stop) - begin;
|
||||
if (length < 0)
|
||||
length += SECSINDAY;
|
||||
|
||||
int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
|
||||
for (int i = -1; i <= DaysToCheck; i++) {
|
||||
time_t t0 = IncDay(t, i);
|
||||
if (DayMatches(t0)) {
|
||||
time_t a = SetTime(t0, begin);
|
||||
time_t b = a + length;
|
||||
if ((!firstday || a >= firstday) && t <= b) {
|
||||
startTime = a;
|
||||
stopTime = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!startTime)
|
||||
startTime = firstday; // just to have something that's more than a week in the future
|
||||
else if (t > startTime || t > firstday + SECSINDAY + 3600) // +3600 in case of DST change
|
||||
firstday = 0;
|
||||
return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
|
||||
}
|
||||
|
||||
time_t cTimer::StartTime(void)
|
||||
{
|
||||
if (!startTime)
|
||||
Matches();
|
||||
return startTime;
|
||||
}
|
||||
|
||||
time_t cTimer::StopTime(void)
|
||||
{
|
||||
if (!stopTime)
|
||||
Matches();
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
void cTimer::SetRecording(bool Recording)
|
||||
{
|
||||
recording = Recording;
|
||||
isyslog("timer %d %s", Index() + 1, recording ? "start" : "stop");
|
||||
}
|
||||
|
||||
void cTimer::SetPending(bool Pending)
|
||||
{
|
||||
pending = Pending;
|
||||
}
|
||||
|
||||
void cTimer::SetActive(int Active)
|
||||
{
|
||||
active = Active;
|
||||
}
|
||||
|
||||
void cTimer::Skip(void)
|
||||
{
|
||||
firstday = IncDay(SetTime(StartTime(), 0), 1);
|
||||
}
|
||||
|
||||
void cTimer::OnOff(void)
|
||||
{
|
||||
if (IsSingleEvent())
|
||||
active = !active;
|
||||
else if (firstday) {
|
||||
firstday = 0;
|
||||
active = false;
|
||||
}
|
||||
else if (active)
|
||||
Skip();
|
||||
else
|
||||
active = true;
|
||||
Matches(); // refresh start and end time
|
||||
}
|
||||
|
||||
// -- cTimers ----------------------------------------------------------------
|
||||
|
||||
cTimers Timers;
|
||||
|
||||
cTimer *cTimers::GetTimer(cTimer *Timer)
|
||||
{
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (ti->Channel() == Timer->Channel() && ti->Day() == Timer->Day() && ti->Start() == Timer->Start() && ti->Stop() == Timer->Stop())
|
||||
return ti;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetMatch(time_t t)
|
||||
{
|
||||
cTimer *t0 = NULL;
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (!ti->Recording() && ti->Matches(t)) {
|
||||
if (!t0 || ti->Priority() > t0->Priority())
|
||||
t0 = ti;
|
||||
}
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
cTimer *cTimers::GetNextActiveTimer(void)
|
||||
{
|
||||
cTimer *t0 = NULL;
|
||||
for (cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (ti->Active() && (!t0 || *ti < *t0))
|
||||
t0 = ti;
|
||||
}
|
||||
return t0;
|
||||
}
|
91
timers.h
Normal file
91
timers.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* timers.h: Timer handling
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.h 1.1 2002/10/20 11:52:23 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TIMERS_H
|
||||
#define __TIMERS_H
|
||||
|
||||
#include "channels.h"
|
||||
#include "config.h"
|
||||
#include "tools.h"
|
||||
|
||||
enum eTimerActive { taInactive = 0,
|
||||
taActive = 1,
|
||||
taInstant = 2,
|
||||
taActInst = (taActive | taInstant)
|
||||
};
|
||||
|
||||
class cTimer : public cListObject {
|
||||
friend class cMenuEditTimer;
|
||||
private:
|
||||
time_t startTime, stopTime;
|
||||
static char *buffer;
|
||||
bool recording, pending;
|
||||
int active;
|
||||
cChannel *channel;
|
||||
int day;
|
||||
int start;
|
||||
int stop;
|
||||
int priority;
|
||||
int lifetime;
|
||||
char file[MaxFileName];
|
||||
time_t firstday;
|
||||
char *summary;
|
||||
static const char *ToText(cTimer *Timer);
|
||||
public:
|
||||
cTimer(bool Instant = false);
|
||||
cTimer(const cEventInfo *EventInfo);
|
||||
virtual ~cTimer();
|
||||
cTimer& operator= (const cTimer &Timer);
|
||||
virtual bool operator< (const cListObject &ListObject);
|
||||
bool Recording(void) { return recording; }
|
||||
bool Pending(void) { return pending; }
|
||||
int Active(void) { return active; }
|
||||
const cChannel *Channel(void) { return channel; }
|
||||
int Day(void) { return day; }
|
||||
int Start(void) { return start; }
|
||||
int Stop(void) { return stop; }
|
||||
int Priority(void) { return priority; }
|
||||
int Lifetime(void) { return lifetime; }
|
||||
const char *File(void) { return file; }
|
||||
time_t FirstDay(void) { return firstday; }
|
||||
const char *Summary(void) { return summary; }
|
||||
const char *ToText(void);
|
||||
bool Parse(const char *s);
|
||||
bool Save(FILE *f);
|
||||
bool IsSingleEvent(void);
|
||||
int GetMDay(time_t t);
|
||||
int GetWDay(time_t t);
|
||||
bool DayMatches(time_t t);
|
||||
static time_t IncDay(time_t t, int Days);
|
||||
static time_t SetTime(time_t t, int SecondsFromMidnight);
|
||||
char *SetFile(const char *File);
|
||||
bool Matches(time_t t = 0);
|
||||
time_t StartTime(void);
|
||||
time_t StopTime(void);
|
||||
void SetRecording(bool Recording);
|
||||
void SetPending(bool Pending);
|
||||
void SetActive(int Active);
|
||||
void Skip(void);
|
||||
void OnOff(void);
|
||||
const char *PrintFirstDay(void);
|
||||
static int TimeToInt(int t);
|
||||
static int ParseDay(const char *s, time_t *FirstDay = NULL);
|
||||
static const char *PrintDay(int d, time_t FirstDay = 0);
|
||||
};
|
||||
|
||||
class cTimers : public cConfig<cTimer> {
|
||||
public:
|
||||
cTimer *GetTimer(cTimer *Timer);
|
||||
cTimer *GetMatch(time_t t);
|
||||
cTimer *GetNextActiveTimer(void);
|
||||
};
|
||||
|
||||
extern cTimers Timers;
|
||||
|
||||
#endif //__TIMERS_H
|
7
vdr.c
7
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||
*
|
||||
* $Id: vdr.c 1.127 2002/10/13 12:13:19 kls Exp $
|
||||
* $Id: vdr.c 1.128 2002/10/20 12:09:45 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -47,6 +47,7 @@
|
||||
#include "rcu.h"
|
||||
#include "recording.h"
|
||||
#include "sources.h"
|
||||
#include "timers.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
|
||||
@ -603,8 +604,8 @@ int main(int argc, char *argv[])
|
||||
if (WatchdogTimeout > 0)
|
||||
signal(SIGALRM, SIG_IGN);
|
||||
if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
|
||||
int Channel = timer ? timer->channel : 0;
|
||||
const char *File = timer ? timer->file : "";
|
||||
int Channel = timer ? timer->Channel()->Number() : 0;
|
||||
const char *File = timer ? timer->File() : "";
|
||||
char *cmd;
|
||||
asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, strescape(File, "\"$"), UserShutdown);
|
||||
isyslog("executing '%s'", cmd);
|
||||
|
Loading…
Reference in New Issue
Block a user