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

The list of recordings is now kept statically in memory

This commit is contained in:
Klaus Schmidinger 2004-06-13 20:26:51 +02:00
parent 1369239b53
commit 5ed57fa1c5
11 changed files with 177 additions and 81 deletions

View File

@ -2893,7 +2893,7 @@ Video Disk Recorder Revision History
strings in order to avoid buffer overflows (thanks to Philip Lawatsch for strings in order to avoid buffer overflows (thanks to Philip Lawatsch for
debugging a buffer overflow in eit.c). debugging a buffer overflow in eit.c).
2004-06-12: Version 1.3.11 2004-06-13: Version 1.3.11
- In order to avoid problems on NPTL systems, VDR now checks for the presence - In order to avoid problems on NPTL systems, VDR now checks for the presence
of NPTL at program start, and if it is, exists and tells the user to do of NPTL at program start, and if it is, exists and tells the user to do
@ -2911,3 +2911,10 @@ Video Disk Recorder Revision History
- Fixed switching channels while an encrypted channel is being recorded, because the - Fixed switching channels while an encrypted channel is being recorded, because the
channel was switched if the new channel was on the same transponder and was channel was switched if the new channel was on the same transponder and was
a radio channel (thanks to Martin Dauskardt for reporting this one). a radio channel (thanks to Martin Dauskardt for reporting this one).
- The list of recordings is now kept statically in memory to avoid long delays
when opening the "Recordings" menu. As a side effect, external modifications to
the video directory are no longer immediately reflected in the "Recordings" menu.
If a plugin manipulates the video directory in any way, it can call the function
Recordings.TriggerUpdate() to trigger an update of the list of recordings.
If some external tool manipulates the video directory, it can touch the file
'.update' in the video directory to trigger an update of the list of recordings.

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: cutter.c 1.6 2003/10/18 11:29:37 kls Exp $ * $Id: cutter.c 1.7 2004/06/13 16:04:08 kls Exp $
*/ */
#include "cutter.h" #include "cutter.h"
@ -205,6 +205,7 @@ bool cCutter::Start(const char *FileName)
// XXX // XXX
editedVersionName = strdup(evn); editedVersionName = strdup(evn);
Recording.WriteSummary(); Recording.WriteSummary();
Recordings.AddByName(editedVersionName);
cuttingThread = new cCuttingThread(FileName, editedVersionName); cuttingThread = new cCuttingThread(FileName, editedVersionName);
return true; return true;
} }
@ -224,6 +225,7 @@ void cCutter::Stop(void)
if (Error) if (Error)
esyslog("ERROR: '%s' during editing process", Error); esyslog("ERROR: '%s' during editing process", Error);
RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed? RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
Recordings.DelByName(editedVersionName);
} }
} }

13
menu.c
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: menu.c 1.308 2004/06/06 09:47:44 kls Exp $ * $Id: menu.c 1.309 2004/06/13 20:26:51 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -1410,7 +1410,6 @@ void cMenuRecordingItem::IncrementCounter(bool New)
// --- cMenuRecordings ------------------------------------------------------- // --- cMenuRecordings -------------------------------------------------------
cRecordings cMenuRecordings::Recordings;
int cMenuRecordings::helpKeys = -1; int cMenuRecordings::helpKeys = -1;
cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus) cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
@ -1419,15 +1418,11 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
base = Base ? strdup(Base) : NULL; base = Base ? strdup(Base) : NULL;
level = Setup.RecordingDirs ? Level : -1; level = Setup.RecordingDirs ? Level : -1;
Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
if (!Base)
Skins.Message(mtStatus, tr("scanning recordings..."));
bool Loaded = Base || Recordings.Load();
if (!Base)
Skins.Message(mtStatus, NULL);
if (Loaded) {
const char *LastReplayed = cReplayControl::LastReplayed(); const char *LastReplayed = cReplayControl::LastReplayed();
cMenuRecordingItem *LastItem = NULL; cMenuRecordingItem *LastItem = NULL;
char *LastItemText = NULL; char *LastItemText = NULL;
if (!Base)
Recordings.Sort();
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) { if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) {
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level); cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
@ -1452,7 +1447,6 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
SetCurrent(First()); SetCurrent(First());
else if (OpenSubMenus && Open(true)) else if (OpenSubMenus && Open(true))
return; return;
}
SetHelpKeys(); SetHelpKeys();
} }
@ -2780,6 +2774,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
cStatus::MsgRecording(device, Recording.Name()); cStatus::MsgRecording(device, Recording.Name());
if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
cReplayControl::SetRecording(fileName, Recording.Name()); cReplayControl::SetRecording(fileName, Recording.Name());
Recordings.AddByName(fileName);
} }
else else
DELETENULL(recorder); DELETENULL(recorder);

4
menu.h
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: menu.h 1.63 2004/05/23 09:47:26 kls Exp $ * $Id: menu.h 1.64 2004/06/13 11:46:03 kls Exp $
*/ */
#ifndef __MENU_H #ifndef __MENU_H
@ -16,7 +16,6 @@
#include "osdbase.h" #include "osdbase.h"
#include "dvbplayer.h" #include "dvbplayer.h"
#include "recorder.h" #include "recorder.h"
#include "recording.h"
#include "skins.h" #include "skins.h"
class cMenuText : public cOsdMenu { class cMenuText : public cOsdMenu {
@ -107,7 +106,6 @@ class cMenuRecordingItem;
class cMenuRecordings : public cOsdMenu { class cMenuRecordings : public cOsdMenu {
private: private:
static cRecordings Recordings;
char *base; char *base;
int level; int level;
static int helpKeys; static int helpKeys;

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: recording.c 1.87 2004/05/07 14:24:18 kls Exp $ * $Id: recording.c 1.88 2004/06/13 20:25:19 kls Exp $
*/ */
#include "recording.h" #include "recording.h"
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
@ -25,7 +26,7 @@
#define RECEXT ".rec" #define RECEXT ".rec"
#define DELEXT ".del" #define DELEXT ".del"
/* This was the original code, which works fine in a Linux only environment. /* This was the original code, which works fine in a Linux only environment.
Unfortunately, because of windows and its brain dead file system, we have Unfortunately, because of Windows and its brain dead file system, we have
to use a more complicated approach, in order to allow users who have enabled to use a more complicated approach, in order to allow users who have enabled
the VFAT compile time option to see their recordings even if they forget to the VFAT compile time option to see their recordings even if they forget to
enable VFAT when compiling a new version of VDR... Gee, do I hate Windows. enable VFAT when compiling a new version of VDR... Gee, do I hate Windows.
@ -47,8 +48,6 @@
#define SUMMARYFILESUFFIX "/summary.vdr" #define SUMMARYFILESUFFIX "/summary.vdr"
#define MARKSFILESUFFIX "/marks.vdr" #define MARKSFILESUFFIX "/marks.vdr"
#define FINDCMD "cd '%s' && find '%s' -follow -type d -name '%s' 2> /dev/null"
#define MINDISKSPACE 1024 // MB #define MINDISKSPACE 1024 // MB
#define DELETEDLIFETIME 1 // hours after which a deleted recording will be actually removed #define DELETEDLIFETIME 1 // hours after which a deleted recording will be actually removed
@ -70,14 +69,14 @@ void RemoveDeletedRecordings(void)
if (!LockFile.Lock()) if (!LockFile.Lock())
return; return;
// Remove the oldest file that has been "deleted": // Remove the oldest file that has been "deleted":
cRecordings Recordings; cRecordings DeletedRecordings(true);
if (Recordings.Load(true)) { if (DeletedRecordings.Load()) {
cRecording *r = Recordings.First(); cRecording *r = DeletedRecordings.First();
cRecording *r0 = r; cRecording *r0 = r;
while (r) { while (r) {
if (r->start < r0->start) if (r->start < r0->start)
r0 = r; r0 = r;
r = Recordings.Next(r); r = DeletedRecordings.Next(r);
} }
if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) { if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) {
r0->Remove(); r0->Remove();
@ -105,14 +104,14 @@ void AssertFreeDiskSpace(int Priority)
return; return;
// Remove the oldest file that has been "deleted": // Remove the oldest file that has been "deleted":
isyslog("low disk space while recording, trying to remove a deleted recording..."); isyslog("low disk space while recording, trying to remove a deleted recording...");
cRecordings Recordings; cRecordings DeletedRecordings(true);
if (Recordings.Load(true)) { if (DeletedRecordings.Load()) {
cRecording *r = Recordings.First(); cRecording *r = DeletedRecordings.First();
cRecording *r0 = r; cRecording *r0 = r;
while (r) { while (r) {
if (r->start < r0->start) if (r->start < r0->start)
r0 = r; r0 = r;
r = Recordings.Next(r); r = DeletedRecordings.Next(r);
} }
if (r0 && r0->Remove()) { if (r0 && r0->Remove()) {
LastFreeDiskCheck += REMOVELATENCY / Factor; LastFreeDiskCheck += REMOVELATENCY / Factor;
@ -121,7 +120,7 @@ void AssertFreeDiskSpace(int Priority)
} }
// No "deleted" files to remove, so let's see if we can delete a recording: // No "deleted" files to remove, so let's see if we can delete a recording:
isyslog("...no deleted recording found, trying to delete an old recording..."); isyslog("...no deleted recording found, trying to delete an old recording...");
if (Recordings.Load(false)) { if (Recordings.Load()) {
cRecording *r = Recordings.First(); cRecording *r = Recordings.First();
cRecording *r0 = NULL; cRecording *r0 = NULL;
while (r) { while (r) {
@ -138,9 +137,11 @@ void AssertFreeDiskSpace(int Priority)
} }
r = Recordings.Next(r); r = Recordings.Next(r);
} }
if (r0 && r0->Delete()) if (r0 && r0->Delete()) {
Recordings.Del(r0);
return; return;
} }
}
// Unable to free disk space, but there's nothing we can do about that... // Unable to free disk space, but there's nothing we can do about that...
isyslog("...no old recording found, giving up"); isyslog("...no old recording found, giving up");
Interface->Confirm(tr("Low disk space!"), 30); Interface->Confirm(tr("Low disk space!"), 30);
@ -617,30 +618,75 @@ bool cRecording::Remove(void)
// --- cRecordings ----------------------------------------------------------- // --- cRecordings -----------------------------------------------------------
bool cRecordings::Load(bool Deleted) cRecordings Recordings;
cRecordings::cRecordings(bool Deleted)
{ {
Clear(); deleted = Deleted;
bool result = false; lastUpdate = 0;
char *cmd = NULL; }
asprintf(&cmd, FINDCMD, VideoDirectory, VideoDirectory, Deleted ? "*" DELEXT : "*" RECEXT);
FILE *p = popen(cmd, "r"); bool cRecordings::ScanVideoDir(const char *DirName)
if (p) { {
char *s; DIR *d = opendir(DirName);
while ((s = readline(p)) != NULL) { if (d) {
cRecording *r = new cRecording(s); struct dirent *e;
while ((e = readdir(d)) != NULL) {
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
char *buffer;
asprintf(&buffer, "%s/%s", DirName, e->d_name);
struct stat st;
if (stat(buffer, &st) == 0) {
if (S_ISLNK(st.st_mode)) {
free(buffer);
buffer = ReadLink(buffer);
if (!buffer)
return false;
if (stat(buffer, &st) != 0) {
LOG_ERROR_STR(DirName);
return false;
}
}
if (S_ISDIR(st.st_mode)) {
if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
cRecording *r = new cRecording(buffer);
if (r->Name()) if (r->Name())
Add(r); Add(r);
else else
delete r; delete r;
} }
pclose(p); else if (!ScanVideoDir(buffer))
Sort(); return false;
result = Count() > 0;
} }
else }
Skins.Message(mtError, "Error while opening pipe!"); else {
free(cmd); LOG_ERROR_STR(DirName);
return result; return false;
}
free(buffer);
}
}
closedir(d);
}
else {
LOG_ERROR_STR(DirName);
return false;
}
return true;
}
bool cRecordings::NeedsUpdate(void)
{
return lastUpdate <= LastModifiedTime(AddDirectory(VideoDirectory, ".update"));
}
bool cRecordings::Load(void)
{
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
Clear();
ScanVideoDir(VideoDirectory);
Sort();
return Count() > 0;
} }
cRecording *cRecordings::GetByName(const char *FileName) cRecording *cRecordings::GetByName(const char *FileName)
@ -652,6 +698,22 @@ cRecording *cRecordings::GetByName(const char *FileName)
return NULL; return NULL;
} }
void cRecordings::AddByName(const char *FileName)
{
cRecording *recording = GetByName(FileName);
if (!recording) {
recording = new cRecording(FileName);
Add(recording);
}
}
void cRecordings::DelByName(const char *FileName)
{
cRecording *recording = GetByName(FileName);
if (recording)
Del(recording);
}
// --- cMark ----------------------------------------------------------------- // --- cMark -----------------------------------------------------------------
char *cMark::buffer = NULL; char *cMark::buffer = NULL;

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.29 2004/05/07 14:24:22 kls Exp $ * $Id: recording.h 1.30 2004/06/13 15:37:42 kls Exp $
*/ */
#ifndef __RECORDING_H #ifndef __RECORDING_H
@ -69,11 +69,22 @@ public:
}; };
class cRecordings : public cList<cRecording> { class cRecordings : public cList<cRecording> {
private:
bool deleted;
time_t lastUpdate;
bool ScanVideoDir(const char *DirName);
public: public:
bool Load(bool Deleted = false); cRecordings(bool Deleted = false);
bool Load(void);
void TriggerUpdate(void) { lastUpdate = 0; }
bool NeedsUpdate(void);
cRecording *GetByName(const char *FileName); cRecording *GetByName(const char *FileName);
void AddByName(const char *FileName);
void DelByName(const char *FileName);
}; };
extern cRecordings Recordings;
class cMark : public cListObject { class cMark : public cListObject {
private: private:
static char *buffer; static char *buffer;

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 1.62 2004/03/25 17:00:23 kls Exp $ * $Id: svdrp.c 1.63 2004/06/13 13:38:38 kls Exp $
*/ */
#include "svdrp.h" #include "svdrp.h"
@ -504,8 +504,10 @@ void cSVDRP::CmdDELR(const char *Option)
if (isnumber(Option)) { if (isnumber(Option)) {
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1); cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
if (recording) { if (recording) {
if (recording->Delete()) if (recording->Delete()) {
Reply(250, "Recording \"%s\" deleted", Option); Reply(250, "Recording \"%s\" deleted", Option);
::Recordings.Load(); // must make sure the global recordings list is updated
}
else else
Reply(554, "Error while deleting recording!"); Reply(554, "Error while deleting recording!");
} }

10
tools.c
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: tools.c 1.79 2004/05/22 12:13:27 kls Exp $ * $Id: tools.c 1.80 2004/06/13 14:36:41 kls Exp $
*/ */
#include "tools.h" #include "tools.h"
@ -481,6 +481,14 @@ bool SpinUpDisk(const char *FileName)
return false; return false;
} }
time_t LastModifiedTime(const char *FileName)
{
struct stat fs;
if (stat(FileName, &fs) == 0)
return fs.st_mtime;
return 0;
}
const char *WeekDayName(int WeekDay) const char *WeekDayName(int WeekDay)
{ {
static char buffer[4]; static char buffer[4];

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: tools.h 1.56 2004/05/22 12:11:44 kls Exp $ * $Id: tools.h 1.57 2004/06/13 14:13:26 kls Exp $
*/ */
#ifndef __TOOLS_H #ifndef __TOOLS_H
@ -83,6 +83,7 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false); bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false);
char *ReadLink(const char *FileName); char *ReadLink(const char *FileName);
bool SpinUpDisk(const char *FileName); bool SpinUpDisk(const char *FileName);
time_t LastModifiedTime(const char *FileName);
const char *WeekDayName(int WeekDay); ///< \warning returns a statically allocated string! const char *WeekDayName(int WeekDay); ///< \warning returns a statically allocated string!
const char *WeekDayName(time_t t); ///< \warning returns a statically allocated string! const char *WeekDayName(time_t t); ///< \warning returns a statically allocated string!
const char *DayDateTime(time_t t = 0); ///< \warning returns a statically allocated string! const char *DayDateTime(time_t t = 0); ///< \warning returns a statically allocated string!

6
vdr.1
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.1 1.10 2004/05/16 12:10:52 kls Exp $ .\" $Id: vdr.1 1.11 2004/06/13 14:48:03 kls Exp $
.\" .\"
.TH vdr 1 "1 June 2003" "1.2.0" "Video Disk Recorder" .TH vdr 1 "1 June 2003" "1.2.0" "Video Disk Recorder"
.SH NAME .SH NAME
@ -176,6 +176,10 @@ The actual data files of a recording.
.I epg.data .I epg.data
Contains all current EPG data. Can be used for external processing and will Contains all current EPG data. Can be used for external processing and will
also be read at program startup to have the full EPG data available immediately. also be read at program startup to have the full EPG data available immediately.
.TP
.I .update
If this file is present in the video directory, its last modification time will
be used to trigger an update of the list of recordings in the "Recordings" menu.
.SH SEE ALSO .SH SEE ALSO
.BR vdr (5) .BR vdr (5)
.SH AUTHOR .SH AUTHOR

8
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/vdr * The project's page is at http://www.cadsoft.de/vdr
* *
* $Id: vdr.c 1.183 2004/06/12 10:07:17 kls Exp $ * $Id: vdr.c 1.184 2004/06/13 13:52:09 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -475,6 +475,10 @@ int main(int argc, char *argv[])
else else
cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true); cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
// Recordings:
Recordings.Load();
// Signal handlers: // Signal handlers:
if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
@ -606,6 +610,8 @@ int main(int argc, char *argv[])
TimerInVpsMargin = true; TimerInVpsMargin = true;
} }
} }
if (!Menu && Recordings.NeedsUpdate())
Recordings.Load();
// CAM control: // CAM control:
if (!Menu && !cOsd::IsOpen()) if (!Menu && !cOsd::IsOpen())
Menu = CamControl(); Menu = CamControl();