1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Switched from 'summary.vdr' to 'info.vdr'

This commit is contained in:
Klaus Schmidinger 2005-05-16 14:45:11 +02:00
parent 6e274dfeef
commit b0583e5373
21 changed files with 525 additions and 249 deletions

20
HISTORY
View File

@ -3513,7 +3513,7 @@ Video Disk Recorder Revision History
- Fixed a wrong inheritance in libsi's SubtitlingDescriptor::Subtitling (thanks to
Marco Schlüßler).
2005-05-14: Version 1.3.25
2005-05-16: Version 1.3.25
- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
- Some cable providers don't mark short channel names according to the standard,
@ -3526,3 +3526,21 @@ Video Disk Recorder Revision History
- Made cOsd::isOpen an integer counter to avoid problems with messages when a
cOsdObject uses the raw OSD (thanks to Andreas Regel for reporting this one).
- Updated the Danish OSD texts (thanks to Mogens Elneff).
- The file 'summary.vdr' has been replaced with 'info.vdr' and now contains the
information about a recording, in the same format as the events are stored in
'epg.data' (see man vdr(5) for details). Existing summary files can be converted
to the new format by running the Perl script 'summary2info.pl', as in
summary2info.pl /video
(the parameter given has to be the video directory).
- The "Summary" button in the "Recordings" menu has been renamed to "Info", and
the page it brings up now shows the recording's information, much like the EPG
event page. Therefore it now no longer uses the skin's SetText() function, but
rather the SetRecording() function. Skin plugins may need to adjust that function
accordingly (see skinsttng.c, for instance).
- The SVDRP command LSTR now lists the recording information in the same tagged
format as the LSTE command lists the EPG data.
- The audio track menu now contains track descriptions when replaying (provided
such descriptions were available in the EPG data when the recording was made,
and are stored in the info.vdr file).

2
MANUAL
View File

@ -23,7 +23,7 @@ Version 1.2
Red - Record Edit Edit ABC/abc Play/Commands(2) Jump -
Green - Audio New New Ins/Ovr Rewind Skip -60s -
Yellow - Pause live Delete Delete Delete Delete Skip +60s -
Blue - Stop/Resume Mark On/Off(1) - Summary Stop -
Blue - Stop/Resume Mark On/Off(1) - Info Stop -
0..9 Ch select - Sort(3) Day(4) Numeric inp. Exec cmd(2) Editing -
In a numerical input field (like the response to a CAM enquiry) the keys 0..9

View File

@ -13,3 +13,7 @@ VDR Plugin 'skincurses' Revision History
- Made several functions threadsafe.
- New audio track display.
2005-05-16: Version 0.0.4
- New "recording info" display.

View File

@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
* $Id: skincurses.c 1.5 2005/01/09 11:56:26 kls Exp $
* $Id: skincurses.c 1.6 2005/05/16 10:45:12 kls Exp $
*/
#include <ncurses.h>
@ -11,7 +11,7 @@
#include <vdr/plugin.h>
#include <vdr/skins.h>
static const char *VERSION = "0.0.3";
static const char *VERSION = "0.0.4";
static const char *DESCRIPTION = "A text only skin";
static const char *MAINMENUENTRY = NULL;
@ -407,7 +407,30 @@ void cSkinCursesDisplayMenu::SetEvent(const cEvent *Event)
void cSkinCursesDisplayMenu::SetRecording(const cRecording *Recording)
{
SetText(Recording->Summary(), false); //TODO
if (!Recording)
return;
const cRecordingInfo *Info = Recording->Info();
int y = 2;
cTextScroller ts;
char t[32];
snprintf(t, sizeof(t), "%s %s", *DateString(Recording->start), *TimeString(Recording->start));
ts.Set(osd, 0, y, OsdWidth, OsdHeight - y - 2, t, &Font, clrYellow, clrBackground);
y += ts.Height();
y += 1;
const char *Title = Info->Title();
if (isempty(Title))
Title = Recording->Name();
ts.Set(osd, 0, y, OsdWidth, OsdHeight - y - 2, Title, &Font, clrCyan, clrBackground);
y += ts.Height();
if (!isempty(Info->ShortText())) {
ts.Set(osd, 0, y, OsdWidth, OsdHeight - y - 2, Info->ShortText(), &Font, clrYellow, clrBackground);
y += ts.Height();
}
y += 1;
if (!isempty(Info->Description())) {
textScroller.Set(osd, 0, y, OsdWidth - 2, OsdHeight - y - 2, Info->Description(), &Font, clrCyan, clrBackground);
SetScrollbar();
}
}
void cSkinCursesDisplayMenu::SetText(const char *Text, bool FixedFont)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: cutter.c 1.7 2004/06/13 16:04:08 kls Exp $
* $Id: cutter.c 1.8 2005/05/15 14:21:08 kls Exp $
*/
#include "cutter.h"
@ -204,7 +204,7 @@ bool cCutter::Start(const char *FileName)
free(s);
// XXX
editedVersionName = strdup(evn);
Recording.WriteSummary();
Recording.WriteInfo();
Recordings.AddByName(editedVersionName);
cuttingThread = new cCuttingThread(FileName, editedVersionName);
return true;

28
eit.c
View File

@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
* $Id: eit.c 1.103 2005/03/20 12:33:51 kls Exp $
* $Id: eit.c 1.104 2005/05/15 10:36:04 kls Exp $
*/
#include "eit.h"
@ -91,8 +91,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
cLinkChannels *LinkChannels = NULL;
int NumComponents = 0;
SI::ComponentDescriptor *ComponentDescriptors[MAXCOMPONENTS];
cComponents *Components = NULL;
for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) {
switch (d->getDescriptorTag()) {
case SI::ExtendedEventDescriptorTag: {
@ -193,12 +192,10 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
uchar Stream = cd->getStreamContent();
uchar Type = cd->getComponentType();
if (1 <= Stream && Stream <= 2 && Type != 0) {
if (NumComponents < MAXCOMPONENTS) {
ComponentDescriptors[NumComponents++] = cd;
d = NULL; // so that it is not deleted
}
else
dsyslog("more than %d component descriptors!", MAXCOMPONENTS);
if (!Components)
Components = new cComponents;
char buffer[256];
Components->SetComponent(Components->NumComponents(), cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
}
}
break;
@ -221,18 +218,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
delete ExtendedEventDescriptors;
delete ShortEventDescriptor;
if (NumComponents > 0) {
cComponents *Components = new cComponents(NumComponents);
for (int i = 0; i < NumComponents; i++) {
char buffer[256];
SI::ComponentDescriptor *cd = ComponentDescriptors[i];
Components->SetComponent(i, cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
delete cd;
}
pEvent->SetComponents(Components);
}
else
pEvent->SetComponents(NULL);
pEvent->SetComponents(Components);
pEvent->FixEpgBugs();
if (LinkChannels)

141
epg.c
View File

@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
*
* $Id: epg.c 1.29 2005/05/05 13:53:19 kls Exp $
* $Id: epg.c 1.30 2005/05/16 14:12:00 kls Exp $
*/
#include "epg.h"
@ -40,11 +40,10 @@ bool tComponent::FromString(const char *s)
// --- cComponents -----------------------------------------------------------
cComponents::cComponents(int NumComponents)
cComponents::cComponents(void)
{
numComponents = NumComponents;
components = MALLOC(tComponent, numComponents);
memset(components, 0, sizeof(tComponent) * numComponents);
numComponents = 0;
components = NULL;
}
cComponents::~cComponents(void)
@ -54,24 +53,30 @@ cComponents::~cComponents(void)
free(components);
}
bool cComponents::SetComponent(int Index, const char *s)
void cComponents::Realloc(int Index)
{
if (Index < numComponents)
return components[Index].FromString(s);
return false;
if (Index >= numComponents) {
int n = numComponents;
numComponents = Index + 1;
components = (tComponent *)realloc(components, numComponents * sizeof(tComponent));
memset(&components[n], 0, sizeof(tComponent) * (numComponents - n));
}
}
bool cComponents::SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description)
void cComponents::SetComponent(int Index, const char *s)
{
if (Index < numComponents) {
tComponent *p = &components[Index];
p->stream = Stream;
p->type = Type;
strn0cpy(p->language, Language, sizeof(p->language));
p->description = strcpyrealloc(p->description, !isempty(Description) ? Description : NULL);
return true;
}
return false;
Realloc(Index);
components[Index].FromString(s);
}
void cComponents::SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description)
{
Realloc(Index);
tComponent *p = &components[Index];
p->stream = Stream;
p->type = Type;
strn0cpy(p->language, Language, sizeof(p->language));
p->description = strcpyrealloc(p->description, !isempty(Description) ? Description : NULL);
}
// --- cEvent ----------------------------------------------------------------
@ -107,6 +112,11 @@ int cEvent::Compare(const cListObject &ListObject) const
return startTime - e->startTime;
}
void cEvent::SetChannelID(tChannelID ChannelID)
{
channelID = ChannelID;
}
void cEvent::SetEventID(u_int16_t EventID)
{
eventID = EventID;
@ -187,30 +197,17 @@ bool cEvent::IsRunning(bool OrAboutToStart) const
cString cEvent::GetDateString(void) const
{
char buf[32];
struct tm tm_r;
tm *tm = localtime_r(&startTime, &tm_r);
char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
*p++ = ' ';
strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
return buf;
return DateString(startTime);
}
cString cEvent::GetTimeString(void) const
{
char buf[25];
struct tm tm_r;
strftime(buf, sizeof(buf), "%R", localtime_r(&startTime, &tm_r));
return buf;
return TimeString(startTime);
}
cString cEvent::GetEndTimeString(void) const
{
char buf[25];
time_t EndTime = startTime + duration;
struct tm tm_r;
strftime(buf, sizeof(buf), "%R", localtime_r(&EndTime, &tm_r));
return buf;
return TimeString(startTime + duration);
}
cString cEvent::GetVpsString(void) const
@ -221,10 +218,11 @@ cString cEvent::GetVpsString(void) const
return buf;
}
void cEvent::Dump(FILE *f, const char *Prefix) const
void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
{
if (startTime + duration + Setup.EPGLinger * 60 >= time(NULL)) {
fprintf(f, "%sE %u %ld %d %X\n", Prefix, eventID, startTime, duration, tableID);
if (InfoOnly || startTime + duration + Setup.EPGLinger * 60 >= time(NULL)) {
if (!InfoOnly)
fprintf(f, "%sE %u %ld %d %X\n", Prefix, eventID, startTime, duration, tableID);
if (!isempty(title))
fprintf(f, "%sT %s\n", Prefix, title);
if (!isempty(shortText))
@ -240,18 +238,40 @@ void cEvent::Dump(FILE *f, const char *Prefix) const
fprintf(f, "%sX %s\n", Prefix, *p->ToString());
}
}
if (vps)
if (!InfoOnly && vps)
fprintf(f, "%sV %ld\n", Prefix, vps);
fprintf(f, "%se\n", Prefix);
if (!InfoOnly)
fprintf(f, "%se\n", Prefix);
}
}
bool cEvent::Parse(char *s)
{
char *t = skipspace(s + 1);
switch (*s) {
case 'T': SetTitle(t);
break;
case 'S': SetShortText(t);
break;
case 'D': strreplace(t, '|', '\n');
SetDescription(t);
break;
case 'X': if (!components)
components = new cComponents;
components->SetComponent(components->NumComponents(), t);
break;
case 'V': SetVps(atoi(t));
break;
default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
return false;
}
return true;
}
bool cEvent::Read(FILE *f, cSchedule *Schedule)
{
if (Schedule) {
cEvent *Event = NULL;
int NumComponents = 0;
char *ComponentStrings[MAXCOMPONENTS];
char *s;
cReadLine ReadLine;
while ((s = ReadLine.Read(f)) != NULL) {
@ -273,45 +293,14 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule)
Event->SetDuration(Duration);
}
}
NumComponents = 0;
}
break;
case 'T': if (Event)
Event->SetTitle(t);
break;
case 'S': if (Event)
Event->SetShortText(t);
break;
case 'D': if (Event) {
strreplace(t, '|', '\n');
Event->SetDescription(t);
}
break;
case 'X': if (Event) {
if (NumComponents < MAXCOMPONENTS)
ComponentStrings[NumComponents++] = strdup(t);
else
dsyslog("more than %d component descriptors!", MAXCOMPONENTS);
}
break;
case 'V': if (Event)
Event->SetVps(atoi(t));
break;
case 'e': if (Event && NumComponents > 0) {
cComponents *Components = new cComponents(NumComponents);
for (int i = 0; i < NumComponents; i++) {
if (!Components->SetComponent(i, ComponentStrings[i]))
esyslog("ERROR: faulty component string in EPG data: '%s'", ComponentStrings[i]);
free(ComponentStrings[i]);
}
Event->SetComponents(Components);
}
Event = NULL;
case 'e': Event = NULL;
break;
case 'c': // to keep things simple we react on 'c' here
return true;
default: esyslog("ERROR: unexpected tag while reading EPG data: %s", s);
return false;
default: if (Event && !Event->Parse(s))
return false;
}
}
esyslog("ERROR: unexpected end of file while reading EPG data");

16
epg.h
View File

@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
*
* $Id: epg.h 1.21 2005/03/20 12:32:36 kls Exp $
* $Id: epg.h 1.22 2005/05/16 14:11:28 kls Exp $
*/
#ifndef __EPG_H
@ -18,7 +18,6 @@
#include "tools.h"
#define MAXEPGBUGFIXLEVEL 3
#define MAXCOMPONENTS 32
enum eDumpMode { dmAll, dmPresent, dmFollowing, dmAtTime };
@ -35,12 +34,13 @@ class cComponents {
private:
int numComponents;
tComponent *components;
void Realloc(int Index);
public:
cComponents(int NumComponents);
cComponents(void);
~cComponents(void);
int NumComponents(void) const { return numComponents; }
bool SetComponent(int Index, const char *s);
bool SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description);
void SetComponent(int Index, const char *s);
void SetComponent(int Index, uchar Stream, uchar Type, const char *Language, const char *Description);
tComponent *Component(int Index) const { return (Index < numComponents) ? &components[Index] : NULL; }
};
@ -56,7 +56,7 @@ private:
char *title; // Title of this event
char *shortText; // Short description of this event (typically the episode name in case of a series)
char *description; // Description of this event
cComponents *components; // The stream components of this event (separated by '\n')
cComponents *components; // The stream components of this event
time_t startTime; // Start time of this event
int duration; // Duration of this event in seconds
time_t vps; // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
@ -86,6 +86,7 @@ public:
cString GetTimeString(void) const;
cString GetEndTimeString(void) const;
cString GetVpsString(void) const;
void SetChannelID(tChannelID ChannelID);
void SetEventID(u_int16_t EventID);
void SetTableID(uchar TableID);
void SetVersion(uchar Version);
@ -98,7 +99,8 @@ public:
void SetDuration(int Duration);
void SetVps(time_t Vps);
void SetSeen(void);
void Dump(FILE *f, const char *Prefix = "") const;
void Dump(FILE *f, const char *Prefix = "", bool InfoOnly = false) const;
bool Parse(char *s);
static bool Read(FILE *f, cSchedule *Schedule);
void FixEpgBugs(void);
};

23
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.190 2005/05/15 09:22:07 kls Exp $
* $Id: i18n.c 1.191 2005/05/15 14:37:59 kls Exp $
*
* Translations provided by:
*
@ -399,6 +399,27 @@ const tI18nPhrase Phrases[] = {
"Kokkuvõte",
"Omtale",
},
{ "Info",
"Info",
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
"",//TODO
},
{ "Schedule - %s",
"Programm - %s",
"Program - %s",

161
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.348 2005/03/20 15:14:51 kls Exp $
* $Id: menu.c 1.349 2005/05/16 13:59:03 kls Exp $
*/
#include "menu.h"
@ -872,7 +872,6 @@ eOSState cMenuTimers::Summary(void)
cTimer *ti = CurrentTimer();
if (ti && !isempty(ti->Summary()))
return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
//XXX cSkin::SetRecording()???
return Edit(); // convenience for people not using the Summary feature ;-)
}
@ -1297,8 +1296,12 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
if (state == osUnknown) {
switch (Key) {
case kOk: return Execute();
default: break;
case kRed:
case kGreen:
case kYellow:
case kBlue: return osContinue;
case kOk: return Execute();
default: break;
}
}
return state;
@ -1430,6 +1433,63 @@ cOsdObject *CamControl(void)
return NULL;
}
// --- cMenuRecording --------------------------------------------------------
class cMenuRecording : public cOsdMenu {
private:
const cRecording *recording;
public:
cMenuRecording(const cRecording *Recording);
virtual void Display(void);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuRecording::cMenuRecording(const cRecording *Recording)
:cOsdMenu(tr("Recording"))
{
recording = Recording;
if (recording)
SetHelp(tr("Play"), tr("Rewind"));
}
void cMenuRecording::Display(void)
{
cOsdMenu::Display();
DisplayMenu()->SetRecording(recording);
cStatus::MsgOsdTextItem(recording->Info()->Description());
}
eOSState cMenuRecording::ProcessKey(eKeys Key)
{
switch (Key) {
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
case kDown:
case kLeft|k_Repeat:
case kLeft:
case kRight|k_Repeat:
case kRight:
DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp);
return osContinue;
default: break;
}
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kRed: Key = kOk; // will play the recording, even if recording commands are defined
case kGreen: cRemote::Put(Key, true);
// continue with osBack to close the info menu and process the key
case kOk: return osBack;
default: break;
}
}
return state;
}
// --- cMenuRecordingItem ----------------------------------------------------
class cMenuRecordingItem : public cOsdItem {
@ -1530,7 +1590,7 @@ void cMenuRecordings::SetHelpKeys(void)
else {
NewHelpKeys = 2;
cRecording *recording = GetRecording(ri);
if (recording && recording->Summary())
if (recording && recording->Info()->Title())
NewHelpKeys = 3;
}
}
@ -1539,7 +1599,7 @@ void cMenuRecordings::SetHelpKeys(void)
case 0: SetHelp(NULL); break;
case 1: SetHelp(tr("Open")); break;
case 2:
case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Summary") : NULL);
case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Info") : NULL);
}
helpKeys = NewHelpKeys;
}
@ -1644,15 +1704,15 @@ eOSState cMenuRecordings::Delete(void)
return osContinue;
}
eOSState cMenuRecordings::Summary(void)
eOSState cMenuRecordings::Info(void)
{
if (HasSubMenu() || Count() == 0)
return osContinue;
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri && !ri->IsDirectory()) {
cRecording *recording = GetRecording(ri);
if (recording && recording->Summary() && *recording->Summary())
return AddSubMenu(new cMenuText(tr("Summary"), recording->Summary()));
if (recording && recording->Info()->Title())
return AddSubMenu(new cMenuRecording(recording));
}
return osContinue;
}
@ -1689,7 +1749,7 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
case kRed: return (helpKeys > 1 && RecordingCommands.Count()) ? Commands() : Play();
case kGreen: return Rewind();
case kYellow: return Delete();
case kBlue: return Summary();
case kBlue: return Info();
case k1...k9: return Commands(Key);
default: break;
}
@ -2568,36 +2628,43 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
// --- SetTrackDescriptions --------------------------------------------------
static void SetTrackDescriptions(void)
static void SetTrackDescriptions(bool Live)
{
cDevice::PrimaryDevice()->ClrAvailableTracks(true);
cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (Channel) {
cSchedulesLock SchedulesLock;
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
if (Schedules) {
const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
if (Schedule) {
const cEvent *Present = Schedule->GetPresentEvent(true);
if (Present) {
const cComponents *Components = Present->Components();
if (Components) {
int indexAudio = 0;
int indexDolby = 0;
for (int i = 0; i < Components->NumComponents(); i++) {
const tComponent *p = Components->Component(i);
if (p->stream == 2) {
if (p->type == 0x05)
cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, NULL, p->description);
else
cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, NULL, p->description);
}
}
}
const cComponents *Components = NULL;
cSchedulesLock SchedulesLock;
if (Live) {
cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (Channel) {
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
if (Schedules) {
const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
if (Schedule) {
const cEvent *Present = Schedule->GetPresentEvent(true);
if (Present)
Components = Present->Components();
}
}
}
}
else if (cReplayControl::LastReplayed()) {
cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed());
if (Recording)
Components = Recording->Info()->Components();
}
if (Components) {
int indexAudio = 0;
int indexDolby = 0;
for (int i = 0; i < Components->NumComponents(); i++) {
const tComponent *p = Components->Component(i);
if (p->stream == 2) {
if (p->type == 0x05)
cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, NULL, p->description);
else
cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, NULL, p->description);
}
}
}
}
// --- cDisplayChannel -------------------------------------------------------
@ -2657,7 +2724,7 @@ void cDisplayChannel::DisplayInfo(void)
const cEvent *Present = Schedule->GetPresentEvent(true);
const cEvent *Following = Schedule->GetFollowingEvent(true);
if (Present != lastPresent || Following != lastFollowing) {
SetTrackDescriptions();
SetTrackDescriptions(true);
displayChannel->SetEvents(Present, Following);
cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL);
lastPresent = Present;
@ -2877,9 +2944,7 @@ cDisplayTracks::cDisplayTracks(void)
:cOsdObject(true)
{
cDevice::PrimaryDevice()->EnsureAudioTrack();
// Get the actual audio track descriptions from the EPG if we're not replaying:
if (!cDevice::PrimaryDevice()->Replaying() || cTransferControl::ReceiverDevice())
SetTrackDescriptions();
SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cTransferControl::ReceiverDevice());
currentDisplayTracks = this;
numTracks = track = 0;
audioChannel = cDevice::PrimaryDevice()->GetAudioChannel();
@ -2994,6 +3059,11 @@ eOSState cDisplayTracks::ProcessKey(eKeys Key)
cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
{
// We're going to manipulate an event here, so we need to prevent
// others from modifying any EPG data:
cSchedulesLock SchedulesLock;
cSchedules::Schedules(SchedulesLock);
event = NULL;
instantId = NULL;
fileName = NULL;
@ -3011,16 +3081,9 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
timer->SetRecording(true);
event = timer->Event();
const char *Title = NULL;
const char *Subtitle = NULL;
const char *Summary = NULL;
if (event || GetEvent()) {
Title = event->Title();
Subtitle = event->ShortText();
Summary = event->Description();
dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
}
cRecording Recording(timer, Title, Subtitle, Summary);
if (event || GetEvent())
dsyslog("Title: '%s' Subtitle: '%s'", event->Title(), event->ShortText());
cRecording Recording(timer, event);
fileName = strdup(Recording.FileName());
// crude attempt to avoid duplicate recordings:
@ -3047,7 +3110,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
const cChannel *ch = timer->Channel();
recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
if (device->AttachReceiver(recorder)) {
Recording.WriteSummary();
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name());
if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
cReplayControl::SetRecording(fileName, Recording.Name());

4
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.69 2005/03/20 10:57:29 kls Exp $
* $Id: menu.h 1.70 2005/05/15 14:34:54 kls Exp $
*/
#ifndef __MENU_H
@ -133,7 +133,7 @@ private:
eOSState Play(void);
eOSState Rewind(void);
eOSState Delete(void);
eOSState Summary(void);
eOSState Info(void);
eOSState Commands(eKeys Key = kNone);
public:
cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.c 1.98 2005/05/07 15:25:15 kls Exp $
* $Id: recording.c 1.99 2005/05/16 14:19:38 kls Exp $
*/
#include "recording.h"
@ -45,7 +45,7 @@
// end of implementation for brain dead systems
#define RESUMEFILESUFFIX "/resume%s%s.vdr"
#define SUMMARYFILESUFFIX "/summary.vdr"
#define INFOFILESUFFIX "/info.vdr"
#define MARKSFILESUFFIX "/marks.vdr"
#define MINDISKSPACE 1024 // MB
@ -213,6 +213,58 @@ void cResumeFile::Delete(void)
}
}
// --- cRecordingInfo --------------------------------------------------------
cRecordingInfo::cRecordingInfo(const cEvent *Event)
{
if (Event) {
event = Event;
ownEvent = NULL;
}
else
event = ownEvent = new cEvent(tChannelID(), 0);
}
cRecordingInfo::~cRecordingInfo()
{
delete ownEvent;
}
bool cRecordingInfo::Read(FILE *f)
{
if (ownEvent) {
cReadLine ReadLine;
char *s;
while ((s = ReadLine.Read(f)) != NULL) {
char *t = skipspace(s + 1);
switch (*s) {
case 'C': {
char *p = strchr(t, ' ');
if (p)
*p = 0; // strips optional channel name
if (*t)
ownEvent->SetChannelID(tChannelID::FromString(t));
}
break;
default: if (!ownEvent->Parse(s))
return false;
break;
}
}
return true;
}
return false;
}
bool cRecordingInfo::Write(FILE *f, const char *Prefix) const
{
cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true);
if (channel)
fprintf(f, "%sC %s %s\n", Prefix, *channel->GetChannelID().ToString(), channel->Name());
event->Dump(f, Prefix, true);
return true;
}
// --- cRecording ------------------------------------------------------------
#define RESUME_NOT_INITIALIZED (-2)
@ -308,7 +360,7 @@ static char *ExchangeChars(char *s, bool ToFileSystem)
return s;
}
cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, const char *Summary)
cRecording::cRecording(cTimer *Timer, const cEvent *Event)
{
resume = RESUME_NOT_INITIALIZED;
titleBuffer = NULL;
@ -316,7 +368,8 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
fileName = NULL;
name = NULL;
// set up the actual name:
const char *OriginalSubtitle = Subtitle;
const char *Title = Event ? Event->Title() : NULL;
const char *Subtitle = Event ? Event->ShortText() : NULL;
char SubtitleBuffer[MAX_SUBTITLE_LENGTH];
if (isempty(Title))
Title = Timer->Channel()->Name();
@ -347,17 +400,13 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
start = Timer->StartTime();
priority = Timer->Priority();
lifetime = Timer->Lifetime();
// handle summary:
summary = !isempty(Timer->Summary()) ? strdup(Timer->Summary()) : NULL;
if (!summary) {
Subtitle = OriginalSubtitle;
if (isempty(Subtitle))
Subtitle = "";
if (isempty(Summary))
Summary = "";
if (*Subtitle || *Summary)
asprintf(&summary, "%s\n\n%s%s%s", Title, Subtitle, (*Subtitle && *Summary) ? "\n\n" : "", Summary);
}
// handle info:
info = new cRecordingInfo(Event);
// this is a somewhat ugly hack to get the 'summary' information from the
// timer into the recording info, but it saves us from having to actually
// copy the entire event data:
if (!isempty(Timer->Summary()))
((cEvent *)Event)->SetDescription(Timer->Summary());
}
cRecording::cRecording(const char *FileName)
@ -370,7 +419,7 @@ cRecording::cRecording(const char *FileName)
char *p = strrchr(FileName, '/');
name = NULL;
summary = NULL;
info = new cRecordingInfo;
if (p) {
time_t now = time(NULL);
struct tm tm_r;
@ -386,39 +435,17 @@ cRecording::cRecording(const char *FileName)
name[p - FileName] = 0;
name = ExchangeChars(name, false);
}
// 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 = MALLOC(char, size + 1); // +1 for terminating 0
if (summary) {
int rbytes = safe_read(f, summary, size);
if (rbytes >= 0) {
summary[rbytes] = 0;
if (rbytes != size)
esyslog("%s: expected %d bytes but read %d", SummaryFileName, size, rbytes);
}
else {
LOG_ERROR_STR(SummaryFileName);
free(summary);
summary = NULL;
}
}
else
esyslog("can't allocate %d byte of memory for summary file '%s'", size + 1, SummaryFileName);
close(f);
}
else
LOG_ERROR_STR(SummaryFileName);
// read an optional info file:
char *InfoFileName = NULL;
asprintf(&InfoFileName, "%s%s", fileName, INFOFILESUFFIX);
FILE *f = fopen(InfoFileName, "r");
if (f) {
info->Read(f);
fclose(f);
}
else if (errno != ENOENT)
LOG_ERROR_STR(SummaryFileName);
free(SummaryFileName);
LOG_ERROR_STR(InfoFileName);
free(InfoFileName);
}
}
@ -428,7 +455,7 @@ cRecording::~cRecording()
free(sortBuffer);
free(fileName);
free(name);
free(summary);
delete info;
}
char *cRecording::StripEpisodeName(char *s)
@ -568,21 +595,18 @@ bool cRecording::IsEdited(void) const
return *s == '%';
}
bool cRecording::WriteSummary(void)
bool cRecording::WriteInfo(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);
free(SummaryFileName);
char *InfoFileName = NULL;
asprintf(&InfoFileName, "%s%s", fileName, INFOFILESUFFIX);
FILE *f = fopen(InfoFileName, "w");
if (f) {
info->Write(f);
fclose(f);
}
else
LOG_ERROR_STR(InfoFileName);
free(InfoFileName);
return true;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recording.h 1.34 2005/01/16 15:11:31 kls Exp $
* $Id: recording.h 1.35 2005/05/16 14:18:43 kls Exp $
*/
#ifndef __RECORDING_H
@ -12,6 +12,7 @@
#include <time.h>
#include "config.h"
#include "epg.h"
#include "thread.h"
#include "timers.h"
#include "tools.h"
@ -32,6 +33,21 @@ public:
void Delete(void);
};
class cRecordingInfo {
private:
const cEvent *event;
cEvent *ownEvent;
public:
cRecordingInfo(const cEvent *Event = NULL);
~cRecordingInfo();
const char *Title(void) const { return event->Title(); }
const char *ShortText(void) const { return event->ShortText(); }
const char *Description(void) const { return event->Description(); }
const cComponents *Components(void) const { return event->Components(); }
bool Read(FILE *f);
bool Write(FILE *f, const char *Prefix = "") const;
};
class cRecording : public cListObject {
private:
mutable int resume;
@ -39,7 +55,7 @@ private:
mutable char *sortBuffer;
mutable char *fileName;
mutable char *name;
char *summary;
cRecordingInfo *info;
static char *StripEpisodeName(char *s);
char *SortName(void) const;
int GetResume(void) const;
@ -47,19 +63,19 @@ public:
time_t start;
int priority;
int lifetime;
cRecording(cTimer *Timer, const char *Title, const char *Subtitle, const char *Summary);
cRecording(cTimer *Timer, const cEvent *Event);
cRecording(const char *FileName);
~cRecording();
virtual int Compare(const cListObject &ListObject) const;
const char *Name(void) const { return name; }
const char *FileName(void) const;
const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
const char *Summary(void) const { return summary; }
const cRecordingInfo *Info(void) const { return info; }
const char *PrefixFileName(char Prefix);
int HierarchyLevels(void) const;
bool IsNew(void) const { return GetResume() <= 0; }
bool IsEdited(void) const;
bool WriteSummary(void);
bool WriteInfo(void);
bool Delete(void);
// Changes the file name so that it will no longer be visible in the "Recordings" menu
// Returns false in case of error

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skinclassic.c 1.11 2005/01/09 11:56:29 kls Exp $
* $Id: skinclassic.c 1.12 2005/05/16 10:45:07 kls Exp $
*/
#include "skinclassic.h"
@ -326,7 +326,33 @@ void cSkinClassicDisplayMenu::SetEvent(const cEvent *Event)
void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording)
{
SetText(Recording->Summary(), false); //TODO
if (!Recording)
return;
const cRecordingInfo *Info = Recording->Info();
const cFont *font = cFont::GetFont(fontOsd);
int xl = x0 + 10;
int y = y2;
cTextScroller ts;
char t[32];
snprintf(t, sizeof(t), "%s %s", *DateString(Recording->start), *TimeString(Recording->start));
ts.Set(osd, xl, y, x1 - xl, y3 - y, t, font, Theme.Color(clrMenuEventTime), Theme.Color(clrBackground));
y += ts.Height();
y += font->Height();
const char *Title = Info->Title();
if (isempty(Title))
Title = Recording->Name();
ts.Set(osd, xl, y, x1 - xl, y3 - y, Title, font, Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground));
y += ts.Height();
if (!isempty(Info->ShortText())) {
const cFont *font = cFont::GetFont(fontSml);
ts.Set(osd, xl, y, x1 - xl, y3 - y, Info->ShortText(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
y += ts.Height();
}
y += font->Height();
if (!isempty(Info->Description())) {
textScroller.Set(osd, xl, y, x1 - xl - 2 * ScrollWidth, y3 - y, Info->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
SetScrollbar();
}
}
void cSkinClassicDisplayMenu::SetText(const char *Text, bool FixedFont)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skins.h 1.7 2005/02/27 14:37:37 kls Exp $
* $Id: skins.h 1.8 2005/05/15 14:41:41 kls Exp $
*/
#ifndef __SKINS_H
@ -150,7 +150,7 @@ public:
///< that text if necessary.
virtual void SetRecording(const cRecording *Recording) = 0;
///< Sets the Recording that shall be displayed, using the entire central area
///< of the menu. The Recording's 'summary' shall be displayed using a
///< of the menu. The Recording's 'description' shall be displayed using a
///< cTextScroller, and the Scroll() function will be called to drive scrolling
///< that text if necessary.
virtual void SetText(const char *Text, bool FixedFont) = 0;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skinsttng.c 1.13 2005/02/27 14:45:19 kls Exp $
* $Id: skinsttng.c 1.14 2005/05/16 10:44:58 kls Exp $
*/
// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
@ -576,7 +576,39 @@ void cSkinSTTNGDisplayMenu::SetEvent(const cEvent *Event)
void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording)
{
SetText(Recording->Summary(), false); //XXX
if (!Recording)
return;
const cRecordingInfo *Info = Recording->Info();
const cFont *font = cFont::GetFont(fontOsd);
int xl = x3 + 5;
int y = y3;
cTextScroller ts;
char t[32];
snprintf(t, sizeof(t), "%s %s", *DateString(Recording->start), *TimeString(Recording->start));
ts.Set(osd, xl, y, x4 - xl, y4 - y, t, font, Theme.Color(clrMenuEventTime), Theme.Color(clrBackground));
y += ts.Height();
y += font->Height();
const char *Title = Info->Title();
if (isempty(Title))
Title = Recording->Name();
ts.Set(osd, xl, y, x4 - xl, y4 - y, Title, font, Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground));
y += ts.Height();
if (!isempty(Info->ShortText())) {
const cFont *font = cFont::GetFont(fontSml);
ts.Set(osd, xl, y, x4 - xl, y4 - y, Info->ShortText(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
y += ts.Height();
}
y += font->Height();
if (!isempty(Info->Description())) {
int yt = y;
int yb = y4 - Roundness;
textScroller.Set(osd, xl, yt, x4 - xl, yb - yt, Info->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
yb = yt + textScroller.Height();
osd->DrawEllipse (x1, yt - Roundness, x2, yt, frameColor, -3);
osd->DrawRectangle(x1, yt, x2, yb, frameColor);
osd->DrawEllipse (x1, yb, x2, yb + Roundness, frameColor, -2);
SetScrollbar();
}
}
void cSkinSTTNGDisplayMenu::SetText(const char *Text, bool FixedFont)

47
summary2info.pl Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/perl
# Convert 'summary.vdr' files to 'info.vdr'
#
# Converts all 'summary.vdr' files in the video directory to the
# 'info.vdr' format as used from VDR version 1.3.25 upward.
#
# Usage: summary2info.pl /video
#
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: summary2info.pl 1.1 2005/05/15 16:03:10 kls Exp $
$VideoDir = $ARGV[0] || die "please provide the name of the video directory\n";
@SummaryFiles = `find "$VideoDir" -name summary.vdr`;
for $SummaryFile (@SummaryFiles) {
chomp($SummaryFile);
print STDERR "converting $SummaryFile...";
open(F, $SummaryFile) || die "$SummaryFile: $!\n";
$line = 0;
@data = ();
while (<F>) {
chomp;
if ($_) {
$data[$line] .= '|' if ($data[$line]);
$data[$line] .= $_;
}
else {
$line++ unless ($_);
}
}
close(F);
if ($line == 1) {
$data[2] = $data[1];
$data[1] = "";
}
($InfoFile = $SummaryFile) =~ s/summary\.vdr$/info.vdr/;
open(F, ">$InfoFile") || die "$InfoFile: $!\n";
print F "T $data[0]\n" if ($data[0]);
print F "S $data[1]\n" if ($data[1]);
print F "D $data[2]\n" if ($data[2]);
close(F);
print STDERR "done.\n";
}

18
svdrp.c
View File

@ -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.70 2005/05/06 13:47:39 kls Exp $
* $Id: svdrp.c 1.71 2005/05/16 14:20:25 kls Exp $
*/
#include "svdrp.h"
@ -214,7 +214,7 @@ const char *HelpPages[] = {
" events at the given time (which must be in time_t form).",
"LSTR [ <number> ]\n"
" List recordings. Without option, all recordings are listed. Otherwise\n"
" the summary for the given recording is listed.",
" the information for the given recording is listed.",
"LSTT [ <number> ]\n"
" List timers. Without option, all timers are listed. Otherwise\n"
" only the given timer is listed.",
@ -281,7 +281,7 @@ const char *HelpPages[] = {
/* SVDRP Reply Codes:
214 Help message
215 EPG data record
215 EPG or recording data record
220 VDR service ready
221 VDR service closing transmission channel
250 Requested VDR action okay, completed
@ -800,13 +800,15 @@ void cSVDRP::CmdLSTR(const char *Option)
if (isnumber(Option)) {
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
if (recording) {
if (recording->Summary()) {
char *summary = strdup(recording->Summary());
Reply(250, "%s", strreplace(summary,'\n','|'));
free(summary);
FILE *f = fdopen(file, "w");
if (f) {
recording->Info()->Write(f, "215-");
fflush(f);
Reply(215, "End of recording information");
// don't 'fclose(f)' here!
}
else
Reply(550, "No summary available");
Reply(451, "Can't open file connection");
}
else
Reply(550, "Recording \"%s\" not found", Option);

21
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.91 2005/03/20 14:44:33 kls Exp $
* $Id: tools.c 1.92 2005/05/16 09:55:26 kls Exp $
*/
#include "tools.h"
@ -571,6 +571,25 @@ cString TimeToString(time_t t)
return "???";
}
cString DateString(time_t t)
{
char buf[32];
struct tm tm_r;
tm *tm = localtime_r(&t, &tm_r);
char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
*p++ = ' ';
strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
return buf;
}
cString TimeString(time_t t)
{
char buf[25];
struct tm tm_r;
strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r));
return buf;
}
// --- cReadLine -------------------------------------------------------------
char *cReadLine::Read(FILE *f)

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.68 2005/03/20 14:44:24 kls Exp $
* $Id: tools.h 1.69 2005/05/16 09:55:19 kls Exp $
*/
#ifndef __TOOLS_H
@ -102,6 +102,8 @@ cString WeekDayName(int WeekDay);
cString WeekDayName(time_t t);
cString DayDateTime(time_t t = 0);
cString TimeToString(time_t t);
cString DateString(time_t t);
cString TimeString(time_t t);
class cTimeMs {
private:

10
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 1.36 2005/05/07 10:40:23 kls Exp $
.\" $Id: vdr.5 1.37 2005/05/16 14:16:48 kls Exp $
.\"
.TH vdr 5 "19 Mar 2005" "1.3.23" "Video Disk Recorder Files"
.SH NAME
@ -578,11 +578,13 @@ the current position within the recording, and to implement skipping
and fast forward/back functions.
See the definition of the \fBcIndexFile\fR class for details about the
actual contents of this file.
.SS SUMMARY
The file \fIsummary.vdr\fR (if present in a recording directory) contains
.SS INFO
The file \fIinfo.vdr\fR (if present in a recording directory) contains
a description of the recording, derived from the EPG data at recording time
(if such data was available) or the \fBSummary\fR field of the corresponding
timer. This is a plain ASCII file and can contain arbitrary text.
timer. This is a plain ASCII file and contains tagged lines like the \fBEPG DATA\fR
file (see the description of the \fIepg.data\fR file). Note that the tags
c, E, e and V will not appear in an \fIinfo.vdr\fR file.
.SS RESUME
The file \fIresume.vdr\fR (if present in a recording directory) contains
the position within the recording where the last replay session left off.