mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Implemented strict locking of global lists
This commit is contained in:
457
recording.c
457
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 4.2 2015/04/18 13:14:55 kls Exp $
|
||||
* $Id: recording.c 4.3 2015/08/29 14:42:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@@ -76,9 +76,6 @@ int DirectoryNameMax = NAME_MAX;
|
||||
bool DirectoryEncoding = false;
|
||||
int InstanceId = 0;
|
||||
|
||||
cRecordings DeletedRecordings(true);
|
||||
static cRecordings VanishedRecordings;
|
||||
|
||||
// --- cRemoveDeletedRecordingsThread ----------------------------------------
|
||||
|
||||
class cRemoveDeletedRecordingsThread : public cThread {
|
||||
@@ -100,8 +97,8 @@ void cRemoveDeletedRecordingsThread::Action(void)
|
||||
if (LockFile.Lock()) {
|
||||
time_t StartTime = time(NULL);
|
||||
bool deleted = false;
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
for (cRecording *r = DeletedRecordings.First(); r; ) {
|
||||
LOCK_DELETEDRECORDINGS_WRITE;
|
||||
for (cRecording *r = DeletedRecordings->First(); r; ) {
|
||||
if (cIoThrottle::Engaged())
|
||||
return;
|
||||
if (time(NULL) - StartTime > MAXREMOVETIME)
|
||||
@@ -109,14 +106,14 @@ void cRemoveDeletedRecordingsThread::Action(void)
|
||||
if (cRemote::HasKeys())
|
||||
return; // react immediately on user input
|
||||
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
|
||||
cRecording *next = DeletedRecordings.Next(r);
|
||||
cRecording *next = DeletedRecordings->Next(r);
|
||||
r->Remove();
|
||||
DeletedRecordings.Del(r);
|
||||
DeletedRecordings->Del(r);
|
||||
r = next;
|
||||
deleted = true;
|
||||
continue;
|
||||
}
|
||||
r = DeletedRecordings.Next(r);
|
||||
r = DeletedRecordings->Next(r);
|
||||
}
|
||||
if (deleted) {
|
||||
const char *IgnoreFiles[] = { SORTMODEFILE, NULL };
|
||||
@@ -134,8 +131,8 @@ void RemoveDeletedRecordings(void)
|
||||
static time_t LastRemoveCheck = 0;
|
||||
if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) {
|
||||
if (!RemoveDeletedRecordingsThread.Active()) {
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
for (cRecording *r = DeletedRecordings.First(); r; r = DeletedRecordings.Next(r)) {
|
||||
LOCK_DELETEDRECORDINGS_READ;
|
||||
for (const cRecording *r = DeletedRecordings->First(); r; r = DeletedRecordings->Next(r)) {
|
||||
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
|
||||
RemoveDeletedRecordingsThread.Start();
|
||||
break;
|
||||
@@ -163,37 +160,43 @@ void AssertFreeDiskSpace(int Priority, bool Force)
|
||||
return;
|
||||
// Remove the oldest file that has been "deleted":
|
||||
isyslog("low disk space while recording, trying to remove a deleted recording...");
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
if (DeletedRecordings.Count()) {
|
||||
cRecording *r = DeletedRecordings.First();
|
||||
cRecording *r0 = NULL;
|
||||
while (r) {
|
||||
if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space
|
||||
if (!r0 || r->Start() < r0->Start())
|
||||
r0 = r;
|
||||
}
|
||||
r = DeletedRecordings.Next(r);
|
||||
}
|
||||
if (r0) {
|
||||
if (r0->Remove())
|
||||
LastFreeDiskCheck += REMOVELATENCY / Factor;
|
||||
DeletedRecordings.Del(r0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int NumDeletedRecordings = 0;
|
||||
{
|
||||
LOCK_DELETEDRECORDINGS_WRITE;
|
||||
NumDeletedRecordings = DeletedRecordings->Count();
|
||||
if (NumDeletedRecordings) {
|
||||
cRecording *r = DeletedRecordings->First();
|
||||
cRecording *r0 = NULL;
|
||||
while (r) {
|
||||
if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space
|
||||
if (!r0 || r->Start() < r0->Start())
|
||||
r0 = r;
|
||||
}
|
||||
r = DeletedRecordings->Next(r);
|
||||
}
|
||||
if (r0) {
|
||||
if (r0->Remove())
|
||||
LastFreeDiskCheck += REMOVELATENCY / Factor;
|
||||
DeletedRecordings->Del(r0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NumDeletedRecordings == 0) {
|
||||
// DeletedRecordings was empty, so to be absolutely sure there are no
|
||||
// deleted recordings we need to double check:
|
||||
DeletedRecordings.Update(true);
|
||||
if (DeletedRecordings.Count())
|
||||
cRecordings::Update(true);
|
||||
LOCK_DELETEDRECORDINGS_READ;
|
||||
if (DeletedRecordings->Count())
|
||||
return; // the next call will actually remove it
|
||||
}
|
||||
// No "deleted" files to remove, so let's see if we can delete a recording:
|
||||
if (Priority > 0) {
|
||||
isyslog("...no deleted recording found, trying to delete an old recording...");
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
if (Recordings.Count()) {
|
||||
cRecording *r = Recordings.First();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->SetExplicitModify();
|
||||
if (Recordings->Count()) {
|
||||
cRecording *r = Recordings->First();
|
||||
cRecording *r0 = NULL;
|
||||
while (r) {
|
||||
if (r->IsOnVideoDirectoryFileSystem()) { // only delete recordings that will actually increase the free video disk space
|
||||
@@ -209,10 +212,11 @@ void AssertFreeDiskSpace(int Priority, bool Force)
|
||||
}
|
||||
}
|
||||
}
|
||||
r = Recordings.Next(r);
|
||||
r = Recordings->Next(r);
|
||||
}
|
||||
if (r0 && r0->Delete()) {
|
||||
Recordings.Del(r0);
|
||||
Recordings->Del(r0);
|
||||
Recordings->SetModified();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -227,14 +231,6 @@ void AssertFreeDiskSpace(int Priority, bool Force)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Clear vanished recordings ---------------------------------------------
|
||||
|
||||
void ClearVanishedRecordings(void)
|
||||
{
|
||||
cThreadLock RecordingsLock(&Recordings); // yes, it *is* Recordings!
|
||||
VanishedRecordings.Clear();
|
||||
}
|
||||
|
||||
// --- cResumeFile -----------------------------------------------------------
|
||||
|
||||
cResumeFile::cResumeFile(const char *FileName, bool IsPesRecording)
|
||||
@@ -309,7 +305,8 @@ bool cResumeFile::Save(int Index)
|
||||
if (safe_write(f, &Index, sizeof(Index)) < 0)
|
||||
LOG_ERROR_STR(fileName);
|
||||
close(f);
|
||||
Recordings.ResetResume(fileName);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->ResetResume(fileName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -318,7 +315,8 @@ bool cResumeFile::Save(int Index)
|
||||
if (f) {
|
||||
fprintf(f, "I %d\n", Index);
|
||||
fclose(f);
|
||||
Recordings.ResetResume(fileName);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->ResetResume(fileName);
|
||||
}
|
||||
else
|
||||
LOG_ERROR_STR(fileName);
|
||||
@@ -331,8 +329,10 @@ bool cResumeFile::Save(int Index)
|
||||
void cResumeFile::Delete(void)
|
||||
{
|
||||
if (fileName) {
|
||||
if (remove(fileName) == 0)
|
||||
Recordings.ResetResume(fileName);
|
||||
if (remove(fileName) == 0) {
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->ResetResume(fileName);
|
||||
}
|
||||
else if (errno != ENOENT)
|
||||
LOG_ERROR_STR(fileName);
|
||||
}
|
||||
@@ -787,10 +787,8 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event)
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (Timer->IsSingleEvent()) {
|
||||
if (Timer->IsSingleEvent())
|
||||
Timer->SetFile(name); // this was an instant recording, so let's set the actual data
|
||||
Timers.SetModified();
|
||||
}
|
||||
}
|
||||
else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
|
||||
name = strdup(Timer->File());
|
||||
@@ -1017,7 +1015,7 @@ int cRecording::Compare(const cListObject &ListObject) const
|
||||
return strcasecmp(SortName(), r->SortName());
|
||||
}
|
||||
|
||||
bool cRecording::IsInPath(const char *Path)
|
||||
bool cRecording::IsInPath(const char *Path) const
|
||||
{
|
||||
if (isempty(Path))
|
||||
return true;
|
||||
@@ -1154,20 +1152,14 @@ bool cRecording::IsOnVideoDirectoryFileSystem(void) const
|
||||
return isOnVideoDirectoryFileSystem;
|
||||
}
|
||||
|
||||
bool cRecording::HasMarks(void)
|
||||
bool cRecording::HasMarks(void) const
|
||||
{
|
||||
return access(cMarks::MarksFileName(this), F_OK) == 0;
|
||||
}
|
||||
|
||||
bool cRecording::DeleteMarks(void)
|
||||
{
|
||||
if (remove(cMarks::MarksFileName(this)) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_ERROR_STR(fileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return cMarks::DeleteMarksFile(this);
|
||||
}
|
||||
|
||||
void cRecording::ReadInfo(void)
|
||||
@@ -1219,8 +1211,6 @@ bool cRecording::ChangePriorityLifetime(int NewPriority, int NewLifetime)
|
||||
if (!WriteInfo())
|
||||
return false;
|
||||
}
|
||||
Recordings.ChangeState();
|
||||
Recordings.TouchUpdate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1245,8 +1235,6 @@ bool cRecording::ChangeName(const char *NewName)
|
||||
}
|
||||
isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
|
||||
ClearSortName();
|
||||
Recordings.ChangeState();
|
||||
Recordings.TouchUpdate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1360,59 +1348,53 @@ int cRecording::FileSizeMB(void) const
|
||||
return fileSizeMB;
|
||||
}
|
||||
|
||||
// --- cRecordings -----------------------------------------------------------
|
||||
// --- cVideoDirectoryScannerThread ------------------------------------------
|
||||
|
||||
cRecordings Recordings;
|
||||
class cVideoDirectoryScannerThread : public cThread {
|
||||
private:
|
||||
cRecordings *recordings;
|
||||
cRecordings *deletedRecordings;
|
||||
bool initial;
|
||||
void ScanVideoDir(const char *DirName, int LinkLevel = 0, int DirLevel = 0);
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings);
|
||||
~cVideoDirectoryScannerThread();
|
||||
};
|
||||
|
||||
char *cRecordings::updateFileName = NULL;
|
||||
|
||||
cRecordings::cRecordings(bool Deleted)
|
||||
cVideoDirectoryScannerThread::cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings)
|
||||
:cThread("video directory scanner", true)
|
||||
{
|
||||
deleted = Deleted;
|
||||
recordings = Recordings;
|
||||
deletedRecordings = DeletedRecordings;
|
||||
initial = true;
|
||||
lastUpdate = 0;
|
||||
state = 0;
|
||||
}
|
||||
|
||||
cRecordings::~cRecordings()
|
||||
cVideoDirectoryScannerThread::~cVideoDirectoryScannerThread()
|
||||
{
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cRecordings::Action(void)
|
||||
void cVideoDirectoryScannerThread::Action(void)
|
||||
{
|
||||
Refresh();
|
||||
cStateKey StateKey;
|
||||
recordings->Lock(StateKey);
|
||||
initial = recordings->Count() == 0; // no name checking if the list is initially empty
|
||||
StateKey.Remove();
|
||||
deletedRecordings->Lock(StateKey, true);
|
||||
deletedRecordings->Clear();
|
||||
StateKey.Remove();
|
||||
ScanVideoDir(cVideoDirectory::Name());
|
||||
}
|
||||
|
||||
const char *cRecordings::UpdateFileName(void)
|
||||
void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLevel, int DirLevel)
|
||||
{
|
||||
if (!updateFileName)
|
||||
updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update"));
|
||||
return updateFileName;
|
||||
}
|
||||
|
||||
void cRecordings::Refresh(bool Foreground)
|
||||
{
|
||||
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
|
||||
initial = Count() == 0; // no name checking if the list is initially empty
|
||||
if (deleted) {
|
||||
Lock();
|
||||
Clear();
|
||||
ChangeState();
|
||||
Unlock();
|
||||
}
|
||||
ScanVideoDir(cVideoDirectory::Name(), Foreground);
|
||||
}
|
||||
|
||||
bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel, int DirLevel)
|
||||
{
|
||||
bool DoChangeState = false;
|
||||
// Find any new recordings:
|
||||
cReadDir d(DirName);
|
||||
struct dirent *e;
|
||||
while ((Foreground || Running()) && (e = d.Next()) != NULL) {
|
||||
if (!Foreground && cIoThrottle::Engaged())
|
||||
while (Running() && (e = d.Next()) != NULL) {
|
||||
if (cIoThrottle::Engaged())
|
||||
cCondWait::SleepMs(100);
|
||||
cString buffer = AddDirectory(DirName, e->d_name);
|
||||
struct stat st;
|
||||
@@ -1428,57 +1410,73 @@ bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev
|
||||
continue;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
|
||||
if (deleted || initial || !GetByName(buffer)) {
|
||||
cRecordings *Recordings = NULL;
|
||||
if (endswith(buffer, RECEXT))
|
||||
Recordings = recordings;
|
||||
else if (endswith(buffer, DELEXT))
|
||||
Recordings = deletedRecordings;
|
||||
if (Recordings) {
|
||||
cStateKey StateKey;
|
||||
Recordings->Lock(StateKey, true);
|
||||
if (Recordings == deletedRecordings || initial || !Recordings->GetByName(buffer)) {
|
||||
cRecording *r = new cRecording(buffer);
|
||||
if (r->Name()) {
|
||||
r->NumFrames(); // initializes the numFrames member
|
||||
r->FileSizeMB(); // initializes the fileSizeMB member
|
||||
r->IsOnVideoDirectoryFileSystem(); // initializes the isOnVideoDirectoryFileSystem member
|
||||
if (deleted)
|
||||
r->deleted = time(NULL);
|
||||
Lock();
|
||||
Add(r);
|
||||
if (initial)
|
||||
ChangeState();
|
||||
else
|
||||
DoChangeState = true;
|
||||
Unlock();
|
||||
if (Recordings == deletedRecordings)
|
||||
r->SetDeleted();
|
||||
Recordings->Add(r);
|
||||
}
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
StateKey.Remove();
|
||||
}
|
||||
else
|
||||
DoChangeState |= ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
|
||||
ScanVideoDir(buffer, LinkLevel + Link, DirLevel + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle any vanished recordings:
|
||||
if (!deleted && !initial && DirLevel == 0) {
|
||||
for (cRecording *recording = First(); recording; ) {
|
||||
cRecording *r = recording;
|
||||
recording = Next(recording);
|
||||
if (access(r->FileName(), F_OK) != 0) {
|
||||
Lock();
|
||||
Del(r, false);
|
||||
VanishedRecordings.Add(r);
|
||||
DoChangeState = true;
|
||||
Unlock();
|
||||
}
|
||||
if (!initial && DirLevel == 0) {
|
||||
cStateKey StateKey;
|
||||
recordings->Lock(StateKey, true);
|
||||
for (cRecording *Recording = recordings->First(); Recording; ) {
|
||||
cRecording *r = Recording;
|
||||
Recording = recordings->Next(Recording);
|
||||
if (access(r->FileName(), F_OK) != 0)
|
||||
recordings->Del(r);
|
||||
}
|
||||
StateKey.Remove();
|
||||
}
|
||||
if (DoChangeState && DirLevel == 0)
|
||||
ChangeState();
|
||||
return DoChangeState;
|
||||
}
|
||||
|
||||
bool cRecordings::StateChanged(int &State)
|
||||
// --- cRecordings -----------------------------------------------------------
|
||||
|
||||
cRecordings cRecordings::recordings;
|
||||
cRecordings cRecordings::deletedRecordings(true);
|
||||
char *cRecordings::updateFileName = NULL;
|
||||
cVideoDirectoryScannerThread *cRecordings::videoDirectoryScannerThread = NULL;
|
||||
time_t cRecordings::lastUpdate = 0;
|
||||
|
||||
cRecordings::cRecordings(bool Deleted)
|
||||
:cList<cRecording>(Deleted ? "DelRecs" : "Recordings")
|
||||
{
|
||||
int NewState = state;
|
||||
bool Result = State != NewState;
|
||||
State = state;
|
||||
return Result;
|
||||
}
|
||||
|
||||
cRecordings::~cRecordings()
|
||||
{
|
||||
// The first one to be destructed deletes it:
|
||||
delete videoDirectoryScannerThread;
|
||||
videoDirectoryScannerThread = NULL;
|
||||
}
|
||||
|
||||
const char *cRecordings::UpdateFileName(void)
|
||||
{
|
||||
if (!updateFileName)
|
||||
updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update"));
|
||||
return updateFileName;
|
||||
}
|
||||
|
||||
void cRecordings::TouchUpdate(void)
|
||||
@@ -1497,24 +1495,24 @@ bool cRecordings::NeedsUpdate(void)
|
||||
return lastUpdate < lastModified;
|
||||
}
|
||||
|
||||
bool cRecordings::Update(bool Wait)
|
||||
void cRecordings::Update(bool Wait)
|
||||
{
|
||||
if (!videoDirectoryScannerThread)
|
||||
videoDirectoryScannerThread = new cVideoDirectoryScannerThread(&recordings, &deletedRecordings);
|
||||
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
|
||||
videoDirectoryScannerThread->Start();
|
||||
if (Wait) {
|
||||
Refresh(true);
|
||||
return Count() > 0;
|
||||
while (videoDirectoryScannerThread->Active())
|
||||
cCondWait::SleepMs(100);
|
||||
}
|
||||
else
|
||||
Start();
|
||||
return false;
|
||||
}
|
||||
|
||||
cRecording *cRecordings::GetByName(const char *FileName)
|
||||
const cRecording *cRecordings::GetByName(const char *FileName) const
|
||||
{
|
||||
if (FileName) {
|
||||
LOCK_THREAD;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (strcmp(recording->FileName(), FileName) == 0)
|
||||
return recording;
|
||||
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (strcmp(Recording->FileName(), FileName) == 0)
|
||||
return Recording;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -1522,12 +1520,8 @@ cRecording *cRecordings::GetByName(const char *FileName)
|
||||
|
||||
void cRecordings::AddByName(const char *FileName, bool TriggerUpdate)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
cRecording *recording = GetByName(FileName);
|
||||
if (!recording) {
|
||||
recording = new cRecording(FileName);
|
||||
Add(recording);
|
||||
ChangeState();
|
||||
if (!GetByName(FileName)) {
|
||||
Add(new cRecording(FileName));
|
||||
if (TriggerUpdate)
|
||||
TouchUpdate();
|
||||
}
|
||||
@@ -1535,58 +1529,52 @@ void cRecordings::AddByName(const char *FileName, bool TriggerUpdate)
|
||||
|
||||
void cRecordings::DelByName(const char *FileName)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
cRecording *recording = GetByName(FileName);
|
||||
cRecording *Recording = GetByName(FileName);
|
||||
cRecording *dummy = NULL;
|
||||
if (!recording)
|
||||
recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
if (!Recording)
|
||||
Recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list
|
||||
LOCK_DELETEDRECORDINGS_WRITE;
|
||||
if (!dummy)
|
||||
Del(recording, false);
|
||||
char *ext = strrchr(recording->fileName, '.');
|
||||
Del(Recording, false);
|
||||
char *ext = strrchr(Recording->fileName, '.');
|
||||
if (ext) {
|
||||
strncpy(ext, DELEXT, strlen(ext));
|
||||
if (access(recording->FileName(), F_OK) == 0) {
|
||||
recording->deleted = time(NULL);
|
||||
DeletedRecordings.Add(recording);
|
||||
recording = NULL; // to prevent it from being deleted below
|
||||
if (access(Recording->FileName(), F_OK) == 0) {
|
||||
Recording->SetDeleted();
|
||||
DeletedRecordings->Add(Recording);
|
||||
Recording = NULL; // to prevent it from being deleted below
|
||||
}
|
||||
}
|
||||
delete recording;
|
||||
ChangeState();
|
||||
delete Recording;
|
||||
TouchUpdate();
|
||||
}
|
||||
|
||||
void cRecordings::UpdateByName(const char *FileName)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
cRecording *recording = GetByName(FileName);
|
||||
if (recording)
|
||||
recording->ReadInfo();
|
||||
if (cRecording *Recording = GetByName(FileName))
|
||||
Recording->ReadInfo();
|
||||
}
|
||||
|
||||
int cRecordings::TotalFileSizeMB(void)
|
||||
int cRecordings::TotalFileSizeMB(void) const
|
||||
{
|
||||
int size = 0;
|
||||
LOCK_THREAD;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
int FileSizeMB = recording->FileSizeMB();
|
||||
if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
|
||||
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
int FileSizeMB = Recording->FileSizeMB();
|
||||
if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
|
||||
size += FileSizeMB;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
double cRecordings::MBperMinute(void)
|
||||
double cRecordings::MBperMinute(void) const
|
||||
{
|
||||
int size = 0;
|
||||
int length = 0;
|
||||
LOCK_THREAD;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (recording->IsOnVideoDirectoryFileSystem()) {
|
||||
int FileSizeMB = recording->FileSizeMB();
|
||||
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (Recording->IsOnVideoDirectoryFileSystem()) {
|
||||
int FileSizeMB = Recording->FileSizeMB();
|
||||
if (FileSizeMB > 0) {
|
||||
int LengthInSeconds = recording->LengthInSeconds();
|
||||
int LengthInSeconds = Recording->LengthInSeconds();
|
||||
if (LengthInSeconds > 0) {
|
||||
if (LengthInSeconds / FileSizeMB < LIMIT_SECS_PER_MB_RADIO) { // don't count radio recordings
|
||||
size += FileSizeMB;
|
||||
@@ -1599,23 +1587,21 @@ double cRecordings::MBperMinute(void)
|
||||
return (size && length) ? double(size) * 60 / length : -1;
|
||||
}
|
||||
|
||||
int cRecordings::PathIsInUse(const char *Path)
|
||||
int cRecordings::PathIsInUse(const char *Path) const
|
||||
{
|
||||
LOCK_THREAD;
|
||||
int Use = ruNone;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (recording->IsInPath(Path))
|
||||
Use |= recording->IsInUse();
|
||||
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (Recording->IsInPath(Path))
|
||||
Use |= Recording->IsInUse();
|
||||
}
|
||||
return Use;
|
||||
}
|
||||
|
||||
int cRecordings::GetNumRecordingsInPath(const char *Path)
|
||||
int cRecordings::GetNumRecordingsInPath(const char *Path) const
|
||||
{
|
||||
LOCK_THREAD;
|
||||
int n = 0;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (recording->IsInPath(Path))
|
||||
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (Recording->IsInPath(Path))
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
@@ -1624,36 +1610,35 @@ int cRecordings::GetNumRecordingsInPath(const char *Path)
|
||||
bool cRecordings::MoveRecordings(const char *OldPath, const char *NewPath)
|
||||
{
|
||||
if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
|
||||
LOCK_THREAD;
|
||||
dsyslog("moving '%s' to '%s'", OldPath, NewPath);
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (recording->IsInPath(OldPath)) {
|
||||
const char *p = recording->Name() + strlen(OldPath);
|
||||
bool Moved = false;
|
||||
for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (Recording->IsInPath(OldPath)) {
|
||||
const char *p = Recording->Name() + strlen(OldPath);
|
||||
cString NewName = cString::sprintf("%s%s", NewPath, p);
|
||||
if (!recording->ChangeName(NewName))
|
||||
if (!Recording->ChangeName(NewName))
|
||||
return false;
|
||||
ChangeState();
|
||||
Moved = true;
|
||||
}
|
||||
}
|
||||
if (Moved)
|
||||
TouchUpdate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cRecordings::ResetResume(const char *ResumeFileName)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording)) {
|
||||
if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
|
||||
recording->ResetResume();
|
||||
for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
|
||||
if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
|
||||
Recording->ResetResume();
|
||||
}
|
||||
ChangeState();
|
||||
}
|
||||
|
||||
void cRecordings::ClearSortNames(void)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
for (cRecording *recording = First(); recording; recording = Next(recording))
|
||||
recording->ClearSortName();
|
||||
for (cRecording *Recording = First(); Recording; Recording = Next(Recording))
|
||||
Recording->ClearSortName();
|
||||
}
|
||||
|
||||
// --- cDirCopier ------------------------------------------------------------
|
||||
@@ -1812,8 +1797,9 @@ void cDirCopier::Stop(void)
|
||||
Cancel(3);
|
||||
if (error) {
|
||||
cVideoDirectory::RemoveVideoFile(dirNameDst);
|
||||
Recordings.AddByName(dirNameSrc);
|
||||
Recordings.DelByName(dirNameDst);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->AddByName(dirNameSrc);
|
||||
Recordings->DelByName(dirNameDst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1893,17 +1879,19 @@ bool cRecordingsHandlerEntry::Active(bool &Error)
|
||||
copier->Start();
|
||||
}
|
||||
ClearPending();
|
||||
Recordings.ChangeState();
|
||||
LOCK_RECORDINGS_WRITE; // to trigger a state change
|
||||
return true;
|
||||
}
|
||||
// Clean up:
|
||||
if (CopierFinishedOk && (Usage() & ruMove) != 0) {
|
||||
cRecording Recording(FileNameSrc());
|
||||
if (Recording.Delete())
|
||||
Recordings.DelByName(Recording.FileName());
|
||||
if (Recording.Delete()) {
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->DelByName(Recording.FileName());
|
||||
}
|
||||
}
|
||||
Recordings.ChangeState();
|
||||
Recordings.TouchUpdate();
|
||||
LOCK_RECORDINGS_WRITE; // to trigger a state change
|
||||
Recordings->TouchUpdate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1947,7 +1935,7 @@ bool cRecordingsHandler::Add(int Usage, const char *FileNameSrc, const char *Fil
|
||||
operations.Add(new cRecordingsHandlerEntry(Usage, FileNameSrc, FileNameDst));
|
||||
finished = false;
|
||||
Active(); // start it right away if possible
|
||||
Recordings.ChangeState();
|
||||
LOCK_RECORDINGS_WRITE; // to trigger a state change
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -1969,7 +1957,7 @@ void cRecordingsHandler::Del(const char *FileName)
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (cRecordingsHandlerEntry *r = Get(FileName)) {
|
||||
operations.Del(r);
|
||||
Recordings.ChangeState();
|
||||
LOCK_RECORDINGS_WRITE; // to trigger a state change
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1977,7 +1965,7 @@ void cRecordingsHandler::DelAll(void)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
operations.Clear();
|
||||
Recordings.ChangeState();
|
||||
LOCK_RECORDINGS_WRITE; // to trigger a state change
|
||||
}
|
||||
|
||||
int cRecordingsHandler::GetUsage(const char *FileName)
|
||||
@@ -2059,9 +2047,19 @@ cString cMarks::MarksFileName(const cRecording *Recording)
|
||||
return AddDirectory(Recording->FileName(), Recording->IsPesRecording() ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
|
||||
}
|
||||
|
||||
bool cMarks::DeleteMarksFile(const cRecording *Recording)
|
||||
{
|
||||
if (remove(cMarks::MarksFileName(Recording)) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_ERROR_STR(Recording->FileName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
recordingFileName = RecordingFileName;
|
||||
fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
|
||||
framesPerSecond = FramesPerSecond;
|
||||
@@ -2074,7 +2072,6 @@ bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool Is
|
||||
|
||||
bool cMarks::Update(void)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
time_t t = time(NULL);
|
||||
if (t > nextUpdate && *fileName) {
|
||||
time_t LastModified = LastModifiedTime(fileName);
|
||||
@@ -2106,7 +2103,6 @@ bool cMarks::Update(void)
|
||||
|
||||
bool cMarks::Save(void)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
if (cConfig<cMark>::Save()) {
|
||||
lastFileTime = LastModifiedTime(fileName);
|
||||
return true;
|
||||
@@ -2116,7 +2112,6 @@ bool cMarks::Save(void)
|
||||
|
||||
void cMarks::Align(void)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
cIndexFile IndexFile(recordingFileName, false, isPesRecording);
|
||||
for (cMark *m = First(); m; m = Next(m)) {
|
||||
int p = IndexFile.GetClosestIFrame(m->Position());
|
||||
@@ -2129,7 +2124,6 @@ void cMarks::Align(void)
|
||||
|
||||
void cMarks::Sort(void)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
for (cMark *m1 = First(); m1; m1 = Next(m1)) {
|
||||
for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
|
||||
if (m2->Position() < m1->Position()) {
|
||||
@@ -2142,43 +2136,42 @@ void cMarks::Sort(void)
|
||||
|
||||
void cMarks::Add(int Position)
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
cConfig<cMark>::Add(new cMark(Position, NULL, framesPerSecond));
|
||||
Sort();
|
||||
}
|
||||
|
||||
cMark *cMarks::Get(int Position)
|
||||
const cMark *cMarks::Get(int Position) const
|
||||
{
|
||||
for (cMark *mi = First(); mi; mi = Next(mi)) {
|
||||
for (const cMark *mi = First(); mi; mi = Next(mi)) {
|
||||
if (mi->Position() == Position)
|
||||
return mi;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cMark *cMarks::GetPrev(int Position)
|
||||
const cMark *cMarks::GetPrev(int Position) const
|
||||
{
|
||||
for (cMark *mi = Last(); mi; mi = Prev(mi)) {
|
||||
for (const cMark *mi = Last(); mi; mi = Prev(mi)) {
|
||||
if (mi->Position() < Position)
|
||||
return mi;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cMark *cMarks::GetNext(int Position)
|
||||
const cMark *cMarks::GetNext(int Position) const
|
||||
{
|
||||
for (cMark *mi = First(); mi; mi = Next(mi)) {
|
||||
for (const cMark *mi = First(); mi; mi = Next(mi)) {
|
||||
if (mi->Position() > Position)
|
||||
return mi;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cMark *cMarks::GetNextBegin(cMark *EndMark)
|
||||
const cMark *cMarks::GetNextBegin(const cMark *EndMark) const
|
||||
{
|
||||
cMark *BeginMark = EndMark ? Next(EndMark) : First();
|
||||
const cMark *BeginMark = EndMark ? Next(EndMark) : First();
|
||||
if (BeginMark && EndMark && BeginMark->Position() == EndMark->Position()) {
|
||||
while (cMark *NextMark = Next(BeginMark)) {
|
||||
while (const cMark *NextMark = Next(BeginMark)) {
|
||||
if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position
|
||||
if (!(BeginMark = Next(NextMark)))
|
||||
break;
|
||||
@@ -2190,13 +2183,13 @@ cMark *cMarks::GetNextBegin(cMark *EndMark)
|
||||
return BeginMark;
|
||||
}
|
||||
|
||||
cMark *cMarks::GetNextEnd(cMark *BeginMark)
|
||||
const cMark *cMarks::GetNextEnd(const cMark *BeginMark) const
|
||||
{
|
||||
if (!BeginMark)
|
||||
return NULL;
|
||||
cMark *EndMark = Next(BeginMark);
|
||||
const cMark *EndMark = Next(BeginMark);
|
||||
if (EndMark && BeginMark && BeginMark->Position() == EndMark->Position()) {
|
||||
while (cMark *NextMark = Next(EndMark)) {
|
||||
while (const cMark *NextMark = Next(EndMark)) {
|
||||
if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position
|
||||
if (!(EndMark = Next(NextMark)))
|
||||
break;
|
||||
@@ -2208,12 +2201,11 @@ cMark *cMarks::GetNextEnd(cMark *BeginMark)
|
||||
return EndMark;
|
||||
}
|
||||
|
||||
int cMarks::GetNumSequences(void)
|
||||
int cMarks::GetNumSequences(void) const
|
||||
{
|
||||
cMutexLock MutexLock(this);
|
||||
int NumSequences = 0;
|
||||
if (cMark *BeginMark = GetNextBegin()) {
|
||||
while (cMark *EndMark = GetNextEnd(BeginMark)) {
|
||||
if (const cMark *BeginMark = GetNextBegin()) {
|
||||
while (const cMark *EndMark = GetNextEnd(BeginMark)) {
|
||||
NumSequences++;
|
||||
BeginMark = GetNextBegin(EndMark);
|
||||
}
|
||||
@@ -2403,7 +2395,8 @@ void cIndexFileGenerator::Action(void)
|
||||
if (FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) {
|
||||
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
|
||||
RecordingInfo.Write();
|
||||
Recordings.UpdateByName(recordingName);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
}
|
||||
}
|
||||
Skins.QueueMessage(mtInfo, tr("Index file regeneration complete"));
|
||||
|
||||
Reference in New Issue
Block a user