From b0583e5373e10119fd88d935cdded1e153101e22 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Mon, 16 May 2005 14:45:11 +0200 Subject: Switched from 'summary.vdr' to 'info.vdr' --- HISTORY | 20 ++++- MANUAL | 2 +- PLUGINS/src/skincurses/HISTORY | 4 + PLUGINS/src/skincurses/skincurses.c | 29 ++++++- cutter.c | 4 +- eit.c | 28 ++----- epg.c | 141 +++++++++++++++---------------- epg.h | 16 ++-- i18n.c | 23 +++++- menu.c | 161 +++++++++++++++++++++++++----------- menu.h | 4 +- recording.c | 146 ++++++++++++++++++-------------- recording.h | 26 ++++-- skinclassic.c | 30 ++++++- skins.h | 4 +- skinsttng.c | 36 +++++++- summary2info.pl | 47 +++++++++++ svdrp.c | 18 ++-- tools.c | 21 ++++- tools.h | 4 +- vdr.5 | 10 ++- 21 files changed, 525 insertions(+), 249 deletions(-) create mode 100755 summary2info.pl diff --git a/HISTORY b/HISTORY index 76a25e53..60663ed2 100644 --- a/HISTORY +++ b/HISTORY @@ -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). diff --git a/MANUAL b/MANUAL index ebf8ce02..8a65809e 100644 --- a/MANUAL +++ b/MANUAL @@ -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 diff --git a/PLUGINS/src/skincurses/HISTORY b/PLUGINS/src/skincurses/HISTORY index 8ba4ae4e..33119f4a 100644 --- a/PLUGINS/src/skincurses/HISTORY +++ b/PLUGINS/src/skincurses/HISTORY @@ -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. diff --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c index a1c59540..ad1a26b4 100644 --- a/PLUGINS/src/skincurses/skincurses.c +++ b/PLUGINS/src/skincurses/skincurses.c @@ -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 @@ -11,7 +11,7 @@ #include #include -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) diff --git a/cutter.c b/cutter.c index c8ed60ec..82c7fe5b 100644 --- a/cutter.c +++ b/cutter.c @@ -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; diff --git a/eit.c b/eit.c index 6c6381b1..9875f99b 100644 --- a/eit.c +++ b/eit.c @@ -8,7 +8,7 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $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) diff --git a/epg.c b/epg.c index 21bdeb5b..13a4ba81 100644 --- a/epg.c +++ b/epg.c @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $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"); diff --git a/epg.h b/epg.h index 6668a614..66d6cd43 100644 --- a/epg.h +++ b/epg.h @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $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); }; diff --git a/i18n.c b/i18n.c index b5a85a1b..e1a187de 100644 --- a/i18n.c +++ b/i18n.c @@ -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", diff --git a/menu.c b/menu.c index 30183dfc..e36cc65f 100644 --- a/menu.c +++ b/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.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()); diff --git a/menu.h b/menu.h index d142bbc4..93712e4e 100644 --- a/menu.h +++ b/menu.h @@ -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); diff --git a/recording.c b/recording.c index ed74c462..d20e9cbf 100644 --- a/recording.c +++ b/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.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; } diff --git a/recording.h b/recording.h index be1a5c44..12b07930 100644 --- a/recording.h +++ b/recording.h @@ -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 #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 diff --git a/skinclassic.c b/skinclassic.c index 7eb0513b..fd3ab0b1 100644 --- a/skinclassic.c +++ b/skinclassic.c @@ -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) diff --git a/skins.h b/skins.h index add8d56a..5fc63443 100644 --- a/skins.h +++ b/skins.h @@ -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; diff --git a/skinsttng.c b/skinsttng.c index 3760ad4d..b9d9ccbb 100644 --- a/skinsttng.c +++ b/skinsttng.c @@ -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) diff --git a/summary2info.pl b/summary2info.pl new file mode 100755 index 00000000..4bf70624 --- /dev/null +++ b/summary2info.pl @@ -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 () { + 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"; + } diff --git a/svdrp.c b/svdrp.c index 5104026f..30b4adfb 100644 --- a/svdrp.c +++ b/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.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 [ ]\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 [ ]\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); diff --git a/tools.c b/tools.c index 28ea2afc..075be185 100644 --- a/tools.c +++ b/tools.c @@ -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) diff --git a/tools.h b/tools.h index d9e85f3e..31a88d45 100644 --- a/tools.h +++ b/tools.h @@ -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: diff --git a/vdr.5 b/vdr.5 index 96136a1a..5be6da8c 100644 --- a/vdr.5 +++ b/vdr.5 @@ -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. -- cgit v1.2.3