Implemented the 'First day' parameter for repeating timers

This commit is contained in:
Klaus Schmidinger 2002-02-17 13:05:05 +01:00
parent 3ecbdd489e
commit a87e7625dd
9 changed files with 232 additions and 54 deletions

11
FORMATS
View File

@ -50,7 +50,7 @@ Video Disk Recorder File Formats
and recognize if the user has modified them. When a user modifes an active
timer the 'active' field will be explicitly set to '1'.
- Program number of the channel to record
- Day of recording, either one or more of
- Day of recording (in case of a repeating timer), either one or more of
M------ = Monday
-T----- = Tuesday
--W---- = Wednesday
@ -61,7 +61,14 @@ Video Disk Recorder File Formats
(any combination is possible, for example MTWTF--, and the days may be
indicated by any characters except '-', so for example ABC---- would set
a timer that records on monday, tuesday and wednesday) or the "day of month"
(1..31)
(1..31) in case of a single shot timer.
The day definition of a repeating timer may be followed by the date when that
timer shall hit for the first time. The format for this is @YYYY-MM-DD,
so a complete definition could look like this: MTWTF--@2002-02-18. This
"first day" feature can be used to disable a repeating timer for a couple
of days, or for instance to define a new Mon...Fri timer on wednesday, which
actually starts "monday next week". The "first day" date given need not be
that of a day when the timer would actually hit.
- Start time (first two digits for the hour, second two digits for the minutes)
- End time (first two digits for the hour, second two digits for the minutes)
- Priority (from 0 to 99, 0 = lowest prioity, 99 = highest priority)

View File

@ -979,7 +979,7 @@ Video Disk Recorder Revision History
- Only reporting the 'EPG bugfix statistics' if there really were any fixes.
- Added Finnish language texts (thanks to Hannu Savolainen).
- Reverted to the previous way of searching for the EPG record of the current
recording in case of a periodic timer (i.e. taking the one that is in the
recording in case of a repeating timer (i.e. taking the one that is in the
middle between start and end time).
- Added a typedef for 'in_addr_t' to make it work with glibc < 2.2 (thanks to
Jürgen Schmidt).
@ -997,7 +997,7 @@ Video Disk Recorder Revision History
- If a recording has no episode title, the trailing '~' is no longer shown in
the progress display.
2002-02-16: Version 1.0.0pre1
2002-02-17: Version 1.0.0pre1
- Added scanning for EPG data for another 4 days on channels that support this
(thanks to Oleg Assovski).
@ -1005,3 +1005,7 @@ Video Disk Recorder Revision History
- Fixed the "Low disk space!" message (thanks to Sergei Haller).
- Added the TPID to Hessen-3 in 'channels.conf' (thanks to Sergei Haller).
- Fixed a crash when replaying with DEBUG_OSD=1 (thanks to Stefan Huelswitt).
- Implemented the "First day" parameter for repeating timers. See FORMATS for
information about the enhanced 'timers.conf' file format, and MANUAL for
a description of the new item in the "Edit Timer" menu and the enhanced
functionality of the "Blue" button in the "Timers" menu.

17
MANUAL
View File

@ -41,8 +41,15 @@ Video Disk Recorder User's Manual
any changes that might have been made in the current menu.
In the "Timers" menu, the current timer can be enabled or disabled with
the "Right" or "Left" key, respectively (enabled timers are marked with '>',
timers that are currently recording are marked with '#').
the "Blue" key (this is only possible if the "Timers" list is sorted,
otherwise the "Blue" key is used to mark a timer in order to move it to
another position in the list). Enabled timers are marked with '>', timers
that are currently recording are marked with '#'. If a timer has the
"First day" set so that it will start recording only on the given date,
it is marked with '!'. The "Blue" key toggles through the "enabled" and
"disabled" states, and for repeating timers that are currently recording
also a state that ends this recording prematurely and sets the "First day"
date so that it will record again the next time the timer hits.
"Ok" here opens the "Edit timer" menu.
Textual options, like channel names or recording file names, can be edited
@ -160,7 +167,7 @@ Video Disk Recorder User's Manual
list with the "Up" and "Down" button and press "Ok" (or the "Red" button)
to start playback. New recordings are marked with an '*'.
If the Setup parameter RecordingDirs has been set and there are recordings
from periodic timers organized in a subdirectory structure, only the
from repeating timers organized in a subdirectory structure, only the
directory is displayed and it can be opened by pressing "Ok" (or the "Red"
button). A directory entry displays the total number of recordings within
that directory (and any possible subdirectory thereof) as well as the total
@ -170,7 +177,7 @@ Video Disk Recorder User's Manual
If the setup parameter UseSubtitle was turned on when a recording took place,
VDR adds the "subtitle" (which is usually the name of the episode in case of
a series) to the recording's name. The "Recordings" menu then displays all
recordings of a periodic timer in chronological order, since these are
recordings of a repeating timer in chronological order, since these are
usually the individual episodes of a series, which you may want to view in
the order in which they were broadcast.
@ -321,6 +328,8 @@ Video Disk Recorder User's Manual
of this timer being collected in a common subdirectory.
If this field is left blank, the channel name will be used to form
the name of the recording.
First day: The date of the first day when this timer shall start recording
(only available for repeating timers).
A timer can also be programmed by pressing the "Red" button on the "Schedule",
"Now", "Next" or "Event" menus.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.83 2002/02/10 11:39:00 kls Exp $
* $Id: config.c 1.84 2002/02/17 11:37:05 kls Exp $
*/
#include "config.h"
@ -338,6 +338,7 @@ cTimer::cTimer(bool Instant)
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);
@ -367,6 +368,7 @@ cTimer::cTimer(const cEventInfo *EventInfo)
const char *Title = EventInfo->GetTitle();
if (!isempty(Title))
strn0cpy(file, EventInfo->GetTitle(), sizeof(file));
firstday = 0;
summary = NULL;
}
@ -395,7 +397,7 @@ const char *cTimer::ToText(cTimer *Timer)
delete 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->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
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;
@ -411,20 +413,38 @@ int cTimer::TimeToInt(int t)
return (t / 100 * 60 + t % 100) * 60;
}
int cTimer::ParseDay(const char *s)
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) {
if (strlen(s) == 7) {
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 <<= 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;
*FirstDay = mktime(&tm_r);
}
}
else
d = 0;
}
}
}
else if (d < 1 || d > 31)
@ -432,24 +452,40 @@ int cTimer::ParseDay(const char *s)
return d;
}
const char *cTimer::PrintDay(int d)
const char *cTimer::PrintDay(int d, time_t FirstDay)
{
static char buffer[8];
#define DAYBUFFERSIZE 32
static char buffer[DAYBUFFERSIZE];
if ((d & 0x80000000) != 0) {
char *b = buffer;
const char *w = tr("MTWTFSS");
*b = 0;
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;
@ -477,7 +513,7 @@ bool cTimer::Parse(const char *s)
summary = NULL;
}
//TODO add more plausibility checks
day = ParseDay(buffer1);
day = ParseDay(buffer1, &firstday);
strn0cpy(file, buffer2, MaxFileName);
strreplace(file, '|', ':');
strreplace(summary, '|', '\n');
@ -563,13 +599,17 @@ bool cTimer::Matches(time_t t)
if (DayMatches(t0)) {
time_t a = SetTime(t0, begin);
time_t b = a + length;
if (t <= b) {
if ((!firstday || a >= firstday) && t <= b) {
startTime = a;
stopTime = b;
if (t >= firstday)
firstday = 0;
break;
}
}
}
if (!startTime)
startTime = firstday; // just to have something that's more than a week in the future
return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
}
@ -598,6 +638,11 @@ void cTimer::SetPending(bool Pending)
pending = Pending;
}
void cTimer::SkipToday(void)
{
firstday = IncDay(SetTime(recording ? StartTime() : time(NULL), 0), 1);
}
// --- cCommand -------------------------------------------------------------
char *cCommand::result = NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.95 2002/02/10 15:44:40 kls Exp $
* $Id: config.h 1.96 2002/02/17 12:17:29 kls Exp $
*/
#ifndef __CONFIG_H
@ -135,6 +135,7 @@ public:
int priority;
int lifetime;
char file[MaxFileName];
time_t firstday;
char *summary;
cTimer(bool Instant = false);
cTimer(const cEventInfo *EventInfo);
@ -148,17 +149,19 @@ public:
int GetMDay(time_t t);
int GetWDay(time_t t);
bool DayMatches(time_t t);
time_t IncDay(time_t t, int Days);
time_t SetTime(time_t t, int SecondsFromMidnight);
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 SkipToday(void);
const char *PrintFirstDay(void);
static int TimeToInt(int t);
static int ParseDay(const char *s);
static const char *PrintDay(int d);
static int ParseDay(const char *s, time_t *FirstDay = NULL);
static const char *PrintDay(int d, time_t FirstDay = 0);
};
class cCommand : public cListObject {

22
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: i18n.c 1.53 2002/02/10 15:07:46 kls Exp $
* $Id: i18n.c 1.54 2002/02/17 12:36:19 kls Exp $
*
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
@ -261,6 +261,16 @@ const tPhrase Phrases[] = {
"Marker",
"Merkitse",
},
{ "On/Off",
"Ein/Aus",
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
},
{ "Record",
"Aufnehmen",
"Posnemi",
@ -684,6 +694,16 @@ const tPhrase Phrases[] = {
"Filnavn",
"Tiedosto",
},
{ "First day",
"Erster Tag",
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
"", // TODO
},
// Error messages:
{ "Channel is being used by a timer!",
"Kanal wird von einem Timer benutzt!",

121
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.152 2002/02/10 11:52:34 kls Exp $
* $Id: menu.c 1.153 2002/02/17 13:00:13 kls Exp $
*/
#include "menu.h"
@ -254,6 +254,62 @@ eOSState cMenuEditDayItem::ProcessKey(eKeys Key)
return osContinue;
}
// --- cMenuEditDateItem -----------------------------------------------------
class cMenuEditDateItem : public cMenuEditItem {
protected:
time_t *value;
virtual void Set(void);
public:
cMenuEditDateItem(const char *Name, time_t *Value);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value)
:cMenuEditItem(Name)
{
value = Value;
Set();
}
void cMenuEditDateItem::Set(void)
{
#define DATEBUFFERSIZE 32
char buf[DATEBUFFERSIZE];
if (*value) {
struct tm tm_r;
localtime_r(value, &tm_r);
strftime(buf, DATEBUFFERSIZE, "%Y-%m-%d ", &tm_r);
strcat(buf, WeekDayName(tm_r.tm_wday));
}
else
*buf = 0;
SetValue(buf);
}
eOSState cMenuEditDateItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
*value -= SECSINDAY;
if (*value < time(NULL))
*value = 0;
}
else if (NORMALKEY(Key) == kRight) {
if (!*value)
*value = cTimer::SetTime(time(NULL), 0);
*value += SECSINDAY;
}
else
return state;
Set();
state = osContinue;
}
return state;
}
// --- cMenuEditTimeItem -----------------------------------------------------
class cMenuEditTimeItem : public cMenuEditItem {
@ -905,6 +961,8 @@ class cMenuEditTimer : public cOsdMenu {
private:
cTimer *timer;
cTimer data;
cMenuEditDateItem *firstday;
void SetFirstDayItem(void);
public:
cMenuEditTimer(int Index, bool New = false);
virtual eOSState ProcessKey(eKeys Key);
@ -913,6 +971,7 @@ public:
cMenuEditTimer::cMenuEditTimer(int Index, bool New)
:cOsdMenu(tr("Edit Timer"), 12)
{
firstday = NULL;
timer = Timers.Get(Index);
if (timer) {
data = *timer;
@ -927,6 +986,21 @@ cMenuEditTimer::cMenuEditTimer(int Index, bool New)
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), FileNameChars));
SetFirstDayItem();
}
}
void cMenuEditTimer::SetFirstDayItem(void)
{
if (!firstday && !data.IsSingleEvent()) {
Add(firstday = new cMenuEditDateItem(tr("First day"), &data.firstday));
Display();
}
else if (firstday && data.IsSingleEvent()) {
Del(firstday->Index());
firstday = NULL;
data.firstday = 0;
Display();
}
}
@ -953,6 +1027,8 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
default: break;
}
}
if (Key != kNone)
SetFirstDayItem();
return state;
}
@ -983,7 +1059,7 @@ 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->recording ? '#' : '>' : ' ',
!timer->active ? ' ' : timer->firstday ? '!' : timer->recording ? '#' : '>',
timer->channel,
timer->PrintDay(timer->day),
timer->start / 100,
@ -998,10 +1074,10 @@ void cMenuTimerItem::Set(void)
class cMenuTimers : public cOsdMenu {
private:
eOSState Activate(bool On);
eOSState Edit(void);
eOSState New(void);
eOSState Del(void);
eOSState OnOff(void);
virtual void Move(int From, int To);
eOSState Summary(void);
cTimer *CurrentTimer(void);
@ -1022,7 +1098,7 @@ cMenuTimers::cMenuTimers(void)
}
if (Setup.SortTimers)
Sort();
SetHelp(tr("Edit"), tr("New"), tr("Delete"), Setup.SortTimers ? NULL : tr("Mark"));
SetHelp(tr("Edit"), tr("New"), tr("Delete"), Setup.SortTimers ? tr("On/Off") : tr("Mark"));
}
cTimer *cMenuTimers::CurrentTimer(void)
@ -1031,14 +1107,27 @@ cTimer *cMenuTimers::CurrentTimer(void)
return item ? item->Timer() : NULL;
}
eOSState cMenuTimers::Activate(bool On)
eOSState cMenuTimers::OnOff(void)
{
cTimer *timer = CurrentTimer();
if (timer && timer->active != On) {
timer->active = On;
if (timer) {
if (timer->IsSingleEvent())
timer->active = !timer->active;
else if (timer->firstday) {
timer->firstday = 0;
timer->active = false;
}
else if (timer->active)
timer->SkipToday();
else
timer->active = true;
timer->Matches(); // refresh start and end time
RefreshCurrent();
DisplayCurrent(true);
isyslog(LOG_INFO, "timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de");
if (timer->firstday)
isyslog(LOG_INFO, "timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
else
isyslog(LOG_INFO, "timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de");
Timers.Save();
}
return osContinue;
@ -1106,27 +1195,17 @@ eOSState cMenuTimers::Summary(void)
eOSState cMenuTimers::ProcessKey(eKeys Key)
{
// Must do these before calling cOsdMenu::ProcessKey() because cOsdMenu
// uses them to page up/down:
if (!HasSubMenu()) {
switch (Key) {
case kLeft:
case kRight: return Activate(Key == kRight);
default: break;
}
}
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kLeft:
case kRight: return Activate(Key == kRight);
case kOk: return Summary();
case kRed: return Edit();
case kGreen: return New();
case kYellow: return Del();
case kBlue: if (!Setup.SortTimers)
case kBlue: if (Setup.SortTimers)
OnOff();
else
Mark();
break;
default: break;

22
tools.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.c 1.58 2002/02/16 12:41:44 kls Exp $
* $Id: tools.c 1.59 2002/02/17 12:57:23 kls Exp $
*/
#include "tools.h"
@ -462,6 +462,20 @@ bool SpinUpDisk(const char *FileName)
return false;
}
const char *WeekDayName(int WeekDay)
{
static char buffer[4];
WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with monday==0!
if (0 <= WeekDay && WeekDay <= 6) {
const char *day = tr("MonTueWedThuFriSatSun");
day += WeekDay * 3;
strncpy(buffer, day, 3);
return buffer;
}
else
return "???";
}
const char *DayDateTime(time_t t)
{
static char buffer[32];
@ -469,11 +483,7 @@ const char *DayDateTime(time_t t)
time(&t);
struct tm tm_r;
tm *tm = localtime_r(&t, &tm_r);
int weekday = tm->tm_wday == 0 ? 6 : tm->tm_wday - 1; // we start with monday==0!
const char *day = tr("MonTueWedThuFriSatSun");
day += weekday * 3;
strncpy(buffer, day, 3);
snprintf(buffer + 3, sizeof(buffer) - 3, " %2d.%02d %02d:%02d", tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
snprintf(buffer, sizeof(buffer), "%s %2d.%02d %02d:%02d", WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
return buffer;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.h 1.41 2002/02/03 12:36:25 kls Exp $
* $Id: tools.h 1.42 2002/02/17 12:57:44 kls Exp $
*/
#ifndef __TOOLS_H
@ -66,6 +66,7 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false);
char *ReadLink(const char *FileName);
bool SpinUpDisk(const char *FileName);
const char *WeekDayName(int WeekDay); // returns a statically allocated string!
const char *DayDateTime(time_t t = 0); // returns a statically allocated string!
class cFile {