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
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
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
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).
- 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
* 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"
@ -205,6 +205,7 @@ bool cCutter::Start(const char *FileName)
// XXX
editedVersionName = strdup(evn);
Recording.WriteSummary();
Recordings.AddByName(editedVersionName);
cuttingThread = new cCuttingThread(FileName, editedVersionName);
return true;
}
@ -224,6 +225,7 @@ void cCutter::Stop(void)
if (Error)
esyslog("ERROR: '%s' during editing process", Error);
RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
Recordings.DelByName(editedVersionName);
}
}

61
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.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"
@ -1410,7 +1410,6 @@ void cMenuRecordingItem::IncrementCounter(bool New)
// --- cMenuRecordings -------------------------------------------------------
cRecordings cMenuRecordings::Recordings;
int cMenuRecordings::helpKeys = -1;
cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
@ -1419,40 +1418,35 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
base = Base ? strdup(Base) : NULL;
level = Setup.RecordingDirs ? Level : -1;
Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
const char *LastReplayed = cReplayControl::LastReplayed();
cMenuRecordingItem *LastItem = NULL;
char *LastItemText = NULL;
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();
cMenuRecordingItem *LastItem = NULL;
char *LastItemText = NULL;
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) {
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
Add(Item);
LastItem = Item;
free(LastItemText);
LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters!
}
else
delete Item;
if (LastItem) {
if (LastReplayed && strcmp(LastReplayed, recording->FileName()) == 0)
SetCurrent(LastItem);
if (LastItem->IsDirectory())
LastItem->IncrementCounter(recording->IsNew());
}
Recordings.Sort();
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) {
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
Add(Item);
LastItem = Item;
free(LastItemText);
LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters!
}
else
delete Item;
if (LastItem) {
if (LastReplayed && strcmp(LastReplayed, recording->FileName()) == 0)
SetCurrent(LastItem);
if (LastItem->IsDirectory())
LastItem->IncrementCounter(recording->IsNew());
}
}
free(LastItemText);
if (Current() < 0)
SetCurrent(First());
else if (OpenSubMenus && Open(true))
return;
}
}
free(LastItemText);
if (Current() < 0)
SetCurrent(First());
else if (OpenSubMenus && Open(true))
return;
SetHelpKeys();
}
@ -2780,6 +2774,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
cStatus::MsgRecording(device, Recording.Name());
if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
cReplayControl::SetRecording(fileName, Recording.Name());
Recordings.AddByName(fileName);
}
else
DELETENULL(recorder);

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.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
@ -16,7 +16,6 @@
#include "osdbase.h"
#include "dvbplayer.h"
#include "recorder.h"
#include "recording.h"
#include "skins.h"
class cMenuText : public cOsdMenu {
@ -107,7 +106,6 @@ class cMenuRecordingItem;
class cMenuRecordings : public cOsdMenu {
private:
static cRecordings Recordings;
char *base;
int level;
static int helpKeys;

View File

@ -4,10 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@ -25,7 +26,7 @@
#define RECEXT ".rec"
#define DELEXT ".del"
/* 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
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.
@ -47,8 +48,6 @@
#define SUMMARYFILESUFFIX "/summary.vdr"
#define MARKSFILESUFFIX "/marks.vdr"
#define FINDCMD "cd '%s' && find '%s' -follow -type d -name '%s' 2> /dev/null"
#define MINDISKSPACE 1024 // MB
#define DELETEDLIFETIME 1 // hours after which a deleted recording will be actually removed
@ -70,14 +69,14 @@ void RemoveDeletedRecordings(void)
if (!LockFile.Lock())
return;
// Remove the oldest file that has been "deleted":
cRecordings Recordings;
if (Recordings.Load(true)) {
cRecording *r = Recordings.First();
cRecordings DeletedRecordings(true);
if (DeletedRecordings.Load()) {
cRecording *r = DeletedRecordings.First();
cRecording *r0 = r;
while (r) {
if (r->start < r0->start)
r0 = r;
r = Recordings.Next(r);
r = DeletedRecordings.Next(r);
}
if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) {
r0->Remove();
@ -105,14 +104,14 @@ void AssertFreeDiskSpace(int Priority)
return;
// Remove the oldest file that has been "deleted":
isyslog("low disk space while recording, trying to remove a deleted recording...");
cRecordings Recordings;
if (Recordings.Load(true)) {
cRecording *r = Recordings.First();
cRecordings DeletedRecordings(true);
if (DeletedRecordings.Load()) {
cRecording *r = DeletedRecordings.First();
cRecording *r0 = r;
while (r) {
if (r->start < r0->start)
r0 = r;
r = Recordings.Next(r);
r = DeletedRecordings.Next(r);
}
if (r0 && r0->Remove()) {
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:
isyslog("...no deleted recording found, trying to delete an old recording...");
if (Recordings.Load(false)) {
if (Recordings.Load()) {
cRecording *r = Recordings.First();
cRecording *r0 = NULL;
while (r) {
@ -138,8 +137,10 @@ void AssertFreeDiskSpace(int Priority)
}
r = Recordings.Next(r);
}
if (r0 && r0->Delete())
if (r0 && r0->Delete()) {
Recordings.Del(r0);
return;
}
}
// Unable to free disk space, but there's nothing we can do about that...
isyslog("...no old recording found, giving up");
@ -617,30 +618,75 @@ bool cRecording::Remove(void)
// --- cRecordings -----------------------------------------------------------
bool cRecordings::Load(bool Deleted)
cRecordings Recordings;
cRecordings::cRecordings(bool Deleted)
{
Clear();
bool result = false;
char *cmd = NULL;
asprintf(&cmd, FINDCMD, VideoDirectory, VideoDirectory, Deleted ? "*" DELEXT : "*" RECEXT);
FILE *p = popen(cmd, "r");
if (p) {
char *s;
while ((s = readline(p)) != NULL) {
cRecording *r = new cRecording(s);
if (r->Name())
Add(r);
else
delete r;
deleted = Deleted;
lastUpdate = 0;
}
bool cRecordings::ScanVideoDir(const char *DirName)
{
DIR *d = opendir(DirName);
if (d) {
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())
Add(r);
else
delete r;
}
else if (!ScanVideoDir(buffer))
return false;
}
}
else {
LOG_ERROR_STR(DirName);
return false;
}
free(buffer);
}
}
pclose(p);
Sort();
result = Count() > 0;
closedir(d);
}
else
Skins.Message(mtError, "Error while opening pipe!");
free(cmd);
return result;
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)
@ -652,6 +698,22 @@ cRecording *cRecordings::GetByName(const char *FileName)
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 -----------------------------------------------------------------
char *cMark::buffer = NULL;

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.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
@ -69,11 +69,22 @@ public:
};
class cRecordings : public cList<cRecording> {
private:
bool deleted;
time_t lastUpdate;
bool ScanVideoDir(const char *DirName);
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);
void AddByName(const char *FileName);
void DelByName(const char *FileName);
};
extern cRecordings Recordings;
class cMark : public cListObject {
private:
static char *buffer;

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.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"
@ -504,8 +504,10 @@ void cSVDRP::CmdDELR(const char *Option)
if (isnumber(Option)) {
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
if (recording) {
if (recording->Delete())
if (recording->Delete()) {
Reply(250, "Recording \"%s\" deleted", Option);
::Recordings.Load(); // must make sure the global recordings list is updated
}
else
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
* 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"
@ -481,6 +481,14 @@ bool SpinUpDisk(const char *FileName)
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)
{
static char buffer[4];

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.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
@ -83,6 +83,7 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false);
char *ReadLink(const char *FileName);
bool SpinUpDisk(const char *FileName);
time_t LastModifiedTime(const char *FileName);
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 *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
.\" 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"
.SH NAME
@ -176,6 +176,10 @@ The actual data files of a recording.
.I epg.data
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.
.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
.BR vdr (5)
.SH AUTHOR

8
vdr.c
View File

@ -22,7 +22,7 @@
*
* 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>
@ -475,6 +475,10 @@ int main(int argc, char *argv[])
else
cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
// Recordings:
Recordings.Load();
// Signal handlers:
if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
@ -606,6 +610,8 @@ int main(int argc, char *argv[])
TimerInVpsMargin = true;
}
}
if (!Menu && Recordings.NeedsUpdate())
Recordings.Load();
// CAM control:
if (!Menu && !cOsd::IsOpen())
Menu = CamControl();