Implemented 'summary' feature

This commit is contained in:
Klaus Schmidinger 2000-07-24 16:43:04 +02:00
parent 6602eb5c90
commit 07d92eaee7
7 changed files with 175 additions and 23 deletions

View File

@ -3,6 +3,7 @@ Thanks go to the following people for patches and contributions:
Carsten Koch <Carsten.Koch@icem.de> Carsten Koch <Carsten.Koch@icem.de>
for adding LIRC support for adding LIRC support
for making the 'Recordings' menu be listed alphabetically for making the 'Recordings' menu be listed alphabetically
for implementing the 'Summary' feature
Plamen Ganev <pganev@com-it.net> Plamen Ganev <pganev@com-it.net>
for fixing the frequency offset for Hotbird channels for fixing the frequency offset for Hotbird channels

12
HISTORY
View File

@ -56,7 +56,7 @@ Video Disk Recorder Revision History
the PC keyboard to better resemble the "up-down-left-right-ok" layout on the PC keyboard to better resemble the "up-down-left-right-ok" layout on
menu controlling remote control units. menu controlling remote control units.
2000-07-23: Version 0.06 2000-07-24: Version 0.6
- Added support for LIRC remote control (thanks to Carsten Koch!). - Added support for LIRC remote control (thanks to Carsten Koch!).
There are now three different remote control modes: KBD (PC-Keyboard), RCU There are now three different remote control modes: KBD (PC-Keyboard), RCU
@ -87,3 +87,13 @@ Video Disk Recorder Revision History
the VDR over a network connection. the VDR over a network connection.
- Implemented command line option handling. - Implemented command line option handling.
- The program can now run in full background mode by using the --daemon option. - The program can now run in full background mode by using the --daemon option.
- Added a "summary" field to the timers (thanks to Carsten Koch!).
This field can contain a descriptive text of the programme and will be
displayed when the "Blue" key is pressed on a recording that was created by
this timer. If the text contains the special character '|', a newline will
be inserted at that place. When pressing "Ok" on a timer that contains a
summary field, the summary will be displayed. To edit such a timer the "Red"
key must be pressed. Timers without a summary still go into Edit mode when
pressing "Ok". The summary field can only be filled in directly by editing
the 'timers.conf' file with a text editor, or by defining/modifying the timer
via the SVDRP interface.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.c 1.13 2000/07/23 11:56:06 kls Exp $ * $Id: config.c 1.14 2000/07/23 17:22:08 kls Exp $
*/ */
#include "config.h" #include "config.h"
@ -274,13 +274,27 @@ cTimer::cTimer(bool Instant)
priority = 99; priority = 99;
lifetime = 99; lifetime = 99;
*file = 0; *file = 0;
summary = NULL;
if (Instant) if (Instant)
snprintf(file, sizeof(file), "@%s", cChannel::GetChannelName(CurrentChannel)); snprintf(file, sizeof(file), "@%s", cChannel::GetChannelName(CurrentChannel));
} }
cTimer::~cTimer()
{
delete summary;
}
cTimer& cTimer::operator= (const cTimer &Timer)
{
memcpy(this, &Timer, sizeof(*this));
if (summary)
summary = strdup(summary);
return *this;
}
const char *cTimer::ToText(cTimer *Timer) const char *cTimer::ToText(cTimer *Timer)
{ {
asprintf(&buffer, "%d:%d:%s:%d:%d:%d:%d:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file); asprintf(&buffer, "%d:%d:%s:%d:%d:%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 : "");
return buffer; return buffer;
} }
@ -344,7 +358,9 @@ bool cTimer::Parse(const char *s)
{ {
char *buffer1 = NULL; char *buffer1 = NULL;
char *buffer2 = NULL; char *buffer2 = NULL;
if (8 == sscanf(s, "%d:%d:%a[^:]:%d:%d:%d:%d:%a[^:\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2)) { delete summary;
summary = NULL;
if (8 <= sscanf(s, "%d:%d:%a[^:]:%d:%d:%d:%d:%a[^:\n]:%a[^\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
//TODO add more plausibility checks //TODO add more plausibility checks
day = ParseDay(buffer1); day = ParseDay(buffer1);
strncpy(file, buffer2, MaxFileName - 1); strncpy(file, buffer2, MaxFileName - 1);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.h 1.10 2000/07/23 11:54:53 kls Exp $ * $Id: config.h 1.11 2000/07/23 17:17:10 kls Exp $
*/ */
#ifndef __CONFIG_H #ifndef __CONFIG_H
@ -17,7 +17,7 @@
#include "dvbapi.h" #include "dvbapi.h"
#include "tools.h" #include "tools.h"
#define MaxBuffer 1000 #define MaxBuffer 10000
enum eKeys { // "Up" and "Down" must be the first two keys! enum eKeys { // "Up" and "Down" must be the first two keys!
kUp, kUp,
@ -100,7 +100,10 @@ public:
int priority; int priority;
int lifetime; int lifetime;
char file[MaxFileName]; char file[MaxFileName];
char *summary;
cTimer(bool Instant = false); cTimer(bool Instant = false);
~cTimer();
cTimer& operator= (const cTimer &Timer);
const char *ToText(void); const char *ToText(void);
bool Parse(const char *s); bool Parse(const char *s);
bool Save(FILE *f); bool Save(FILE *f);

78
menu.c
View File

@ -4,10 +4,11 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 1.19 2000/07/16 14:52:48 kls Exp $ * $Id: menu.c 1.20 2000/07/24 16:25:53 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
#include <ctype.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -707,6 +708,51 @@ eOSState cMenuChannels::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuSummary --------------------------------------------------------
class cMenuSummary : public cOsdMenu {
public:
cMenuSummary(const char *Text);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuSummary::cMenuSummary(const char *Text)
:cOsdMenu("Summary")
{
while (*Text) {
char line[MenuColumns + 1];
char *p = line;
const char *b = NULL;
*p++ = ' ';
while (*Text && p - line < MenuColumns - 2) {
if (isspace(*Text))
b = Text; // remember the blank
if (*Text == '\n')
break;
*p++ = *Text++;
}
if (*Text) {
if (b && Text - b > 0) {
p -= Text - b;
Text = b + 1;
}
else
Text++;
}
*p = 0;
Add(new cOsdItem(line, osBack));
}
}
eOSState cMenuSummary::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown)
state = osContinue;
return state;
}
// --- cMenuEditTimer -------------------------------------------------------- // --- cMenuEditTimer --------------------------------------------------------
class cMenuEditTimer : public cOsdMenu { class cMenuEditTimer : public cOsdMenu {
@ -799,6 +845,7 @@ private:
eOSState New(void); eOSState New(void);
eOSState Del(void); eOSState Del(void);
virtual void Move(int From, int To); virtual void Move(int From, int To);
eOSState Summary(void);
public: public:
cMenuTimers(void); cMenuTimers(void);
virtual eOSState ProcessKey(eKeys Key); virtual eOSState ProcessKey(eKeys Key);
@ -880,6 +927,16 @@ void cMenuTimers::Move(int From, int To)
isyslog(LOG_INFO, "timer %d moved to %d", From + 1, To + 1); isyslog(LOG_INFO, "timer %d moved to %d", From + 1, To + 1);
} }
eOSState cMenuTimers::Summary(void)
{
if (HasSubMenu() || Count() == 0)
return osContinue;
cTimer *ti = Timers.Get(Current());
if (ti && ti->summary && *ti->summary)
return AddSubMenu(new cMenuSummary(ti->summary));
return Edit(); // convenience for people not using the Summary feature ;-)
}
eOSState cMenuTimers::ProcessKey(eKeys Key) eOSState cMenuTimers::ProcessKey(eKeys Key)
{ {
eOSState state = cOsdMenu::ProcessKey(Key); eOSState state = cOsdMenu::ProcessKey(Key);
@ -888,7 +945,7 @@ eOSState cMenuTimers::ProcessKey(eKeys Key)
switch (Key) { switch (Key) {
case kLeft: case kLeft:
case kRight: return Activate(Key == kRight); case kRight: return Activate(Key == kRight);
case kOk: case kOk: return Summary();
case kRed: return Edit(); case kRed: return Edit();
case kGreen: return New(); case kGreen: return New();
case kYellow: return Del(); case kYellow: return Del();
@ -926,6 +983,7 @@ private:
cRecordings Recordings; cRecordings Recordings;
eOSState Play(void); eOSState Play(void);
eOSState Del(void); eOSState Del(void);
eOSState Summary(void);
public: public:
cMenuRecordings(void); cMenuRecordings(void);
virtual eOSState ProcessKey(eKeys Key); virtual eOSState ProcessKey(eKeys Key);
@ -941,7 +999,7 @@ cMenuRecordings::cMenuRecordings(void)
recording = Recordings.Next(recording); recording = Recordings.Next(recording);
} }
} }
SetHelp("Play", NULL/*XXX"Resume"*/, "Delete"); SetHelp("Play", NULL/*XXX"Resume"*/, "Delete", "Summary");
} }
eOSState cMenuRecordings::Play(void) eOSState cMenuRecordings::Play(void)
@ -975,6 +1033,16 @@ eOSState cMenuRecordings::Del(void)
return osContinue; return osContinue;
} }
eOSState cMenuRecordings::Summary(void)
{
if (HasSubMenu() || Count() == 0)
return osContinue;
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri && ri->recording->Summary() && *ri->recording->Summary())
return AddSubMenu(new cMenuSummary(ri->recording->Summary()));
return osContinue;
}
eOSState cMenuRecordings::ProcessKey(eKeys Key) eOSState cMenuRecordings::ProcessKey(eKeys Key)
{ {
eOSState state = cOsdMenu::ProcessKey(Key); eOSState state = cOsdMenu::ProcessKey(Key);
@ -984,6 +1052,7 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
case kOk: case kOk:
case kRed: return Play(); case kRed: return Play();
case kYellow: return Del(); case kYellow: return Del();
case kBlue: return Summary();
default: break; default: break;
} }
} }
@ -1061,7 +1130,8 @@ cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
timer->SetRecording(true); timer->SetRecording(true);
cChannel::SwitchTo(timer->channel - 1, dvbApi); cChannel::SwitchTo(timer->channel - 1, dvbApi);
cRecording Recording(timer); cRecording Recording(timer);
dvbApi->StartRecord(Recording.FileName()); if (dvbApi->StartRecord(Recording.FileName()))
Recording.WriteSummary();
Interface.DisplayRecording(dvbApi->Index(), true); Interface.DisplayRecording(dvbApi->Index(), true);
} }

View File

@ -4,14 +4,16 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.c 1.11 2000/07/16 14:21:37 kls Exp $ * $Id: recording.c 1.12 2000/07/24 16:31:07 kls Exp $
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include "recording.h" #include "recording.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "interface.h" #include "interface.h"
#include "tools.h" #include "tools.h"
@ -20,6 +22,8 @@
#define DATAFORMAT "%4d-%02d-%02d.%02d:%02d.%02d.%02d" RECEXT #define DATAFORMAT "%4d-%02d-%02d.%02d:%02d.%02d.%02d" RECEXT
#define NAMEFORMAT "%s/%s/" DATAFORMAT #define NAMEFORMAT "%s/%s/" DATAFORMAT
#define SUMMARYFILESUFFIX "/summary.vdr"
#define FINDCMD "find %s -type d -name '%s' | sort -df" #define FINDCMD "find %s -type d -name '%s' | sort -df"
#define DFCMD "df -m %s" #define DFCMD "df -m %s"
@ -102,21 +106,14 @@ void AssertFreeDiskSpace(void)
// --- cRecording ------------------------------------------------------------ // --- cRecording ------------------------------------------------------------
cRecording::cRecording(const char *Name, time_t Start, int Priority, int LifeTime)
{
titleBuffer = NULL;
fileName = NULL;
name = strdup(Name);
start = Start;
priority = Priority;
lifetime = LifeTime;
}
cRecording::cRecording(cTimer *Timer) cRecording::cRecording(cTimer *Timer)
{ {
titleBuffer = NULL; titleBuffer = NULL;
fileName = NULL; fileName = NULL;
name = strdup(Timer->file); name = strdup(Timer->file);
summary = Timer->summary ? strdup(Timer->summary) : NULL;
if (summary)
strreplace(summary, '|', '\n');
start = Timer->StartTime(); start = Timer->StartTime();
priority = Timer->priority; priority = Timer->priority;
lifetime = Timer->lifetime; lifetime = Timer->lifetime;
@ -130,6 +127,7 @@ cRecording::cRecording(const char *FileName)
char *p = strrchr(FileName, '/'); char *p = strrchr(FileName, '/');
name = NULL; name = NULL;
summary = NULL;
if (p) { if (p) {
time_t now = time(NULL); time_t now = time(NULL);
struct tm t = *localtime(&now); // this initializes the time zone in 't' struct tm t = *localtime(&now); // this initializes the time zone in 't'
@ -143,6 +141,39 @@ cRecording::cRecording(const char *FileName)
name[p - FileName] = 0; name[p - FileName] = 0;
strreplace(name, '_', ' '); strreplace(name, '_', ' ');
} }
// read an optional summary file:
char *SummaryFileName = NULL;
asprintf(&SummaryFileName, "%s%s", fileName, SUMMARYFILESUFFIX);
int f = open(SummaryFileName, O_RDONLY);
if (f >= 0) {
struct stat buf;
if (fstat(f, &buf) == 0) {
int size = buf.st_size;
summary = new char[size + 1]; // +1 for terminating 0
if (summary) {
int rbytes = read(f, summary, size);
if (rbytes >= 0) {
summary[rbytes] = 0;
if (rbytes != size)
esyslog(LOG_ERR, "%s: expected %d bytes but read %d", SummaryFileName, size, rbytes);
}
else {
LOG_ERROR_STR(SummaryFileName);
delete summary;
summary = NULL;
}
}
else
esyslog(LOG_ERR, "can't allocate %d byte of memory for summary file '%s'", size + 1, SummaryFileName);
close(f);
}
else
LOG_ERROR_STR(SummaryFileName);
}
else if (errno != ENOENT)
LOG_ERROR_STR(SummaryFileName);
delete SummaryFileName;
} }
} }
@ -151,6 +182,7 @@ cRecording::~cRecording()
delete titleBuffer; delete titleBuffer;
delete fileName; delete fileName;
delete name; delete name;
delete summary;
} }
const char *cRecording::FileName(void) const char *cRecording::FileName(void)
@ -181,6 +213,24 @@ const char *cRecording::Title(char Delimiter)
return titleBuffer; return titleBuffer;
} }
bool cRecording::WriteSummary(void)
{
if (summary) {
char *SummaryFileName = NULL;
asprintf(&SummaryFileName, "%s%s", fileName, SUMMARYFILESUFFIX);
FILE *f = fopen(SummaryFileName, "w");
if (f) {
if (fputs(summary, f) < 0)
LOG_ERROR_STR(SummaryFileName);
fclose(f);
}
else
LOG_ERROR_STR(SummaryFileName);
delete SummaryFileName;
}
return true;
}
bool cRecording::Delete(void) bool cRecording::Delete(void)
{ {
bool result = true; bool result = true;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.h 1.6 2000/04/24 09:45:49 kls Exp $ * $Id: recording.h 1.7 2000/07/23 19:06:14 kls Exp $
*/ */
#ifndef __RECORDING_H #ifndef __RECORDING_H
@ -22,16 +22,18 @@ private:
char *titleBuffer; char *titleBuffer;
char *fileName; char *fileName;
char *name; char *name;
char *summary;
public: public:
time_t start; time_t start;
int priority; int priority;
int lifetime; int lifetime;
cRecording(const char *Name, time_t Start, int Priority, int LifeTime);
cRecording(cTimer *Timer); cRecording(cTimer *Timer);
cRecording(const char *FileName); cRecording(const char *FileName);
~cRecording(); ~cRecording();
const char *FileName(void); const char *FileName(void);
const char *Title(char Delimiter = ' '); const char *Title(char Delimiter = ' ');
const char *Summary(void) { return summary; }
bool WriteSummary(void);
bool Delete(void); bool Delete(void);
// Changes the file name so that it will no longer be visible in the "Recordings" menu // Changes the file name so that it will no longer be visible in the "Recordings" menu
// Returns false in case of error // Returns false in case of error