2000-07-29 15:21:42 +02:00
|
|
|
/*
|
2013-09-11 12:20:37 +02:00
|
|
|
* videodir.c: Functions to maintain the video directory
|
2000-07-29 15:21:42 +02:00
|
|
|
*
|
|
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
|
|
* how to reach the author.
|
|
|
|
*
|
2021-12-24 10:56:47 +01:00
|
|
|
* $Id: videodir.c 5.2 2021/12/24 10:56:47 kls Exp $
|
2000-07-29 15:21:42 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "videodir.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2005-12-18 10:41:26 +01:00
|
|
|
#include "recording.h"
|
2000-07-29 15:21:42 +02:00
|
|
|
#include "tools.h"
|
|
|
|
|
2015-09-01 11:14:27 +02:00
|
|
|
cMutex cVideoDirectory::mutex;
|
2013-09-11 12:20:37 +02:00
|
|
|
cString cVideoDirectory::name;
|
|
|
|
cVideoDirectory *cVideoDirectory::current = NULL;
|
2000-07-29 15:21:42 +02:00
|
|
|
|
|
|
|
cVideoDirectory::cVideoDirectory(void)
|
|
|
|
{
|
2015-09-01 11:14:27 +02:00
|
|
|
mutex.Lock();
|
2013-09-11 12:20:37 +02:00
|
|
|
delete current;
|
|
|
|
current = this;
|
2015-09-01 11:14:27 +02:00
|
|
|
mutex.Unlock();
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cVideoDirectory::~cVideoDirectory()
|
|
|
|
{
|
2015-09-01 11:14:27 +02:00
|
|
|
mutex.Lock();
|
2013-09-11 12:20:37 +02:00
|
|
|
current = NULL;
|
2015-09-01 11:14:27 +02:00
|
|
|
mutex.Unlock();
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
cVideoDirectory *cVideoDirectory::Current(void)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2015-09-01 11:14:27 +02:00
|
|
|
mutex.Lock();
|
2013-09-11 12:20:37 +02:00
|
|
|
if (!current)
|
2015-09-01 11:14:27 +02:00
|
|
|
new cVideoDirectory;
|
|
|
|
mutex.Unlock();
|
2013-09-11 12:20:37 +02:00
|
|
|
return current;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
void cVideoDirectory::Destroy(void)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
delete current;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
int cVideoDirectory::FreeMB(int *UsedMB)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
return FreeDiskSpaceMB(Name(), UsedMB);
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
const char *cVideoDirectory::Name(void)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
return name;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
void cVideoDirectory::SetName(const char *Name)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
name = Name;
|
|
|
|
}
|
2000-07-29 15:21:42 +02:00
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::Register(const char *FileName)
|
|
|
|
{
|
2000-07-29 15:21:42 +02:00
|
|
|
// Incoming name must be in base video directory:
|
2013-09-11 12:20:37 +02:00
|
|
|
if (strstr(FileName, Name()) != FileName) {
|
|
|
|
esyslog("ERROR: %s not in %s", FileName, Name());
|
2000-07-29 15:21:42 +02:00
|
|
|
errno = ENOENT; // must set 'errno' - any ideas for a better value?
|
2013-09-11 12:20:37 +02:00
|
|
|
return false;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
2013-09-11 12:20:37 +02:00
|
|
|
return true;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::Rename(const char *OldName, const char *NewName)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-10-10 13:13:30 +02:00
|
|
|
dsyslog("renaming '%s' to '%s'", OldName, NewName);
|
2013-09-11 12:20:37 +02:00
|
|
|
if (rename(OldName, NewName) == -1) {
|
|
|
|
LOG_ERROR_STR(NewName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::Move(const char *FromName, const char *ToName)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-10-10 13:13:30 +02:00
|
|
|
dsyslog("moving '%s' to '%s'", FromName, ToName);
|
|
|
|
if (EntriesOnSameFileSystem(FromName, ToName)) {
|
|
|
|
if (rename(FromName, ToName) == -1) {
|
|
|
|
LOG_ERROR_STR(ToName);
|
|
|
|
return false;
|
|
|
|
}
|
2021-01-19 20:38:28 +01:00
|
|
|
// detect whether it's a real recording move inside same file system or a recording rename
|
|
|
|
if (strcmp(strgetbefore(FromName, '/', 2), strgetbefore(ToName, '/', 2)))
|
|
|
|
cRecordingUserCommand::InvokeCommand(RUC_MOVEDRECORDING, ToName, FromName);
|
|
|
|
else
|
|
|
|
cRecordingUserCommand::InvokeCommand(RUC_RENAMEDRECORDING, ToName, FromName);
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
2013-10-10 13:13:30 +02:00
|
|
|
else
|
|
|
|
return RecordingsHandler.Add(ruMove, FromName, ToName);
|
2000-07-29 15:21:42 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::Remove(const char *Name)
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
return RemoveFileOrDir(Name);
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
void cVideoDirectory::Cleanup(const char *IgnoreFiles[])
|
2000-07-29 15:21:42 +02:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
RemoveEmptyDirectories(Name(), false, IgnoreFiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cVideoDirectory::Contains(const char *Name)
|
|
|
|
{
|
|
|
|
return EntriesOnSameFileSystem(this->Name(), Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
cUnbufferedFile *cVideoDirectory::OpenVideoFile(const char *FileName, int Flags)
|
|
|
|
{
|
|
|
|
if (Current()->Register(FileName))
|
|
|
|
return cUnbufferedFile::Create(FileName, Flags, DEFFILEMODE);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cVideoDirectory::RenameVideoFile(const char *OldName, const char *NewName)
|
|
|
|
{
|
|
|
|
return Current()->Rename(OldName, NewName);
|
2000-07-29 15:21:42 +02:00
|
|
|
}
|
2000-12-28 12:57:16 +01:00
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::MoveVideoFile(const char *FromName, const char *ToName)
|
2002-01-27 13:11:23 +01:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
return Current()->Move(FromName, ToName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cVideoDirectory::RemoveVideoFile(const char *FileName)
|
|
|
|
{
|
|
|
|
return Current()->Remove(FileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cVideoDirectory::VideoFileSpaceAvailable(int SizeMB)
|
|
|
|
{
|
|
|
|
return Current()->FreeMB() >= SizeMB;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cVideoDirectory::VideoDiskSpace(int *FreeMB, int *UsedMB)
|
|
|
|
{
|
|
|
|
int used = 0;
|
|
|
|
int free = Current()->FreeMB(&used);
|
2015-09-01 11:14:27 +02:00
|
|
|
LOCK_DELETEDRECORDINGS_READ;
|
|
|
|
int deleted = DeletedRecordings->TotalFileSizeMB();
|
2005-12-18 10:41:26 +01:00
|
|
|
if (deleted > used)
|
|
|
|
deleted = used; // let's not get beyond 100%
|
|
|
|
free += deleted;
|
|
|
|
used -= deleted;
|
2002-01-27 13:11:23 +01:00
|
|
|
if (FreeMB)
|
|
|
|
*FreeMB = free;
|
|
|
|
if (UsedMB)
|
|
|
|
*UsedMB = used;
|
2021-12-24 10:56:47 +01:00
|
|
|
return (free + used) ? round(double(used) * 100 / (free + used)) : 0;
|
2002-01-27 13:11:23 +01:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
cString cVideoDirectory::PrefixVideoFileName(const char *FileName, char Prefix)
|
2000-12-28 12:57:16 +01:00
|
|
|
{
|
2004-12-26 12:45:22 +01:00
|
|
|
char PrefixedName[strlen(FileName) + 2];
|
|
|
|
|
|
|
|
const char *p = FileName + strlen(FileName); // p points at the terminating 0
|
|
|
|
int n = 2;
|
|
|
|
while (p-- > FileName && n > 0) {
|
|
|
|
if (*p == '/') {
|
|
|
|
if (--n == 0) {
|
|
|
|
int l = p - FileName + 1;
|
|
|
|
strncpy(PrefixedName, FileName, l);
|
|
|
|
PrefixedName[l] = Prefix;
|
|
|
|
strcpy(PrefixedName + l + 1, p + 1);
|
|
|
|
return PrefixedName;
|
2001-09-02 15:21:54 +02:00
|
|
|
}
|
|
|
|
}
|
2004-12-26 12:45:22 +01:00
|
|
|
}
|
2001-09-02 15:21:54 +02:00
|
|
|
return NULL;
|
2000-12-28 12:57:16 +01:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
void cVideoDirectory::RemoveEmptyVideoDirectories(const char *IgnoreFiles[])
|
2001-02-11 14:53:44 +01:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
Current()->Cleanup(IgnoreFiles);
|
2001-02-11 14:53:44 +01:00
|
|
|
}
|
|
|
|
|
2013-09-11 12:20:37 +02:00
|
|
|
bool cVideoDirectory::IsOnVideoDirectoryFileSystem(const char *FileName)
|
2008-02-16 13:38:22 +01:00
|
|
|
{
|
2013-09-11 12:20:37 +02:00
|
|
|
return Current()->Contains(FileName);
|
2008-02-16 13:38:22 +01:00
|
|
|
}
|
2012-04-23 09:07:55 +02:00
|
|
|
|
|
|
|
// --- cVideoDiskUsage -------------------------------------------------------
|
|
|
|
|
|
|
|
#define DISKSPACECHEK 5 // seconds between disk space checks
|
|
|
|
#define MB_PER_MINUTE 25.75 // this is just an estimate!
|
|
|
|
|
|
|
|
int cVideoDiskUsage::state = 0;
|
|
|
|
time_t cVideoDiskUsage::lastChecked = 0;
|
|
|
|
int cVideoDiskUsage::usedPercent = 0;
|
|
|
|
int cVideoDiskUsage::freeMB = 0;
|
|
|
|
int cVideoDiskUsage::freeMinutes = 0;
|
|
|
|
|
|
|
|
bool cVideoDiskUsage::HasChanged(int &State)
|
|
|
|
{
|
|
|
|
if (time(NULL) - lastChecked > DISKSPACECHEK) {
|
|
|
|
int FreeMB;
|
2013-09-11 12:20:37 +02:00
|
|
|
int UsedPercent = cVideoDirectory::VideoDiskSpace(&FreeMB);
|
2012-04-23 09:07:55 +02:00
|
|
|
if (FreeMB != freeMB) {
|
|
|
|
usedPercent = UsedPercent;
|
|
|
|
freeMB = FreeMB;
|
2015-09-01 11:14:27 +02:00
|
|
|
LOCK_RECORDINGS_READ;
|
|
|
|
double MBperMinute = Recordings->MBperMinute();
|
2012-04-23 09:07:55 +02:00
|
|
|
if (MBperMinute <= 0)
|
|
|
|
MBperMinute = MB_PER_MINUTE;
|
|
|
|
freeMinutes = int(double(FreeMB) / MBperMinute);
|
|
|
|
state++;
|
|
|
|
}
|
|
|
|
lastChecked = time(NULL);
|
|
|
|
}
|
|
|
|
if (State != state) {
|
|
|
|
State = state;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cString cVideoDiskUsage::String(void)
|
|
|
|
{
|
|
|
|
HasChanged(state);
|
|
|
|
return cString::sprintf("%s %d%% - %2d:%02d %s", tr("Disk"), usedPercent, freeMinutes / 60, freeMinutes % 60, tr("free"));
|
|
|
|
}
|