diff --git a/HISTORY b/HISTORY index 3c3d859b..3f880fce 100644 --- a/HISTORY +++ b/HISTORY @@ -7943,3 +7943,26 @@ Video Disk Recorder Revision History respectively, during replay (reported by Thomas Maass). - The Yellow button in the main menu no longer acts as "Pause" if "Pause key handling" is set to "do not pause live video" (suggested by Ulf Kiener). +- The code for distributing recordings over several video directories has been + removed. VDR now by default assumes that the video directory is one big disk. + If you absolutely need to use several separate disks to store recordings, you can + write a plugin that uses the new cVideoDirectory API to implement the necessary + functionality (see PLUGINS.html, section "The video directory"). You can copy the + respective code from previous versions of videodir.c. + IMPORTANT NOTE: If you write a plugin that implements a distributed video directory, + =============== be sure to make cVideoDirectory::Rename() follow symbolic links! + This functionality was never implemented in VDR and it therefore + used a workaround in cutter.c. See the section marked with + // XXX this can be removed once RenameVideoFile() follows symlinks + in previous versions of cutter.c. + + CloseVideoFile() is obsolete and has been removed. + + The functions OpenVideoFile(), RenameVideoFile(), RemoveVideoFile(), VideoFileSpaceAvailable(), + VideoDiskSpace(), RemoveEmptyVideoDirectories(), IsOnVideoDirectoryFileSystem() and + PrefixVideoFileName() are now static members of cVideoDirectory and need to be called + with the proper prefix. + + The name of the video directory is now available through cVideoDirectory::Name(). + The former global variable VideoDirectory is still there for backwards compatibility, + but will be removed in a future version. Comment out the line + #define DEPRECATED_VIDEODIR + in videodir.h and recompile your plugins to see whether your code will work without + this variable. If you get a compile error, replace it with cVideoDirectory::Name(). diff --git a/PLUGINS.html b/PLUGINS.html index 5260c4bf..71d840ea 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -104,6 +104,7 @@ structures and allows it to hook itself into specific areas to perform special a
  • Remote Control
  • Conditional Access
  • Electronic Program Guide +
  • The video directory @@ -2300,5 +2301,41 @@ to signal VDR that no other EPG handlers shall be queried after this one.

    See VDR/epg.h for details. +

    +

    The video directory

    + +
    Bits and pieces...

    + +By default VDR assumes that the video directory consists of one large +volume, on which it can store its recordings. If you want to distribute your +recordings over several physical drives, you can derive from cVideoDirectory, +as in + +

    +#include <vdr/videodir.h>
    +
    +class cMyVideoDirectory : public cVideoDirectory {
    +public:
    +  cMyVideoDirectory(void);
    +  virtual ~cMyVideoDirectory();
    +  virtual int FreeMB(int *UsedMB = NULL);
    +  virtual bool Register(const char *FileName);
    +  virtual bool Rename(const char *OldName, const char *NewName);
    +  virtual bool Move(const char *FromName, const char *ToName);
    +  virtual bool Remove(const char *Name);
    +  virtual void Cleanup(const char *IgnoreFiles[] = NULL);
    +  virtual bool Contains(const char *Name);
    +  };
    +

    + +See the description in videodir.h for details. +

    +You should create your derived video directory object in the +Start() function of your plugin. +Note that the object has to be created on the heap (using new), +and you shall not delete it at any point (it will be deleted automatically +when the program ends). +

    + diff --git a/cutter.c b/cutter.c index 5d007c48..ccb145a3 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 3.2 2013/08/21 13:15:24 kls Exp $ + * $Id: cutter.c 3.3 2013/09/10 14:51:45 kls Exp $ */ #include "cutter.h" @@ -664,19 +664,7 @@ bool cCutter::Start(const char *FileName) Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60); const char *evn = Recording.PrefixFileName('%'); - if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) { - // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c) - // remove a possible deleted recording with the same name to avoid symlink mixups: - char *s = strdup(evn); - char *e = strrchr(s, '.'); - if (e) { - if (strcmp(e, ".rec") == 0) { - strcpy(e, ".del"); - RemoveVideoFile(s); - } - } - free(s); - // XXX + if (evn && cVideoDirectory::RemoveVideoFile(evn) && MakeDirs(evn, true)) { editedVersionName = evn; Recording.WriteInfo(); Recordings.AddByName(editedVersionName, false); @@ -701,7 +689,7 @@ void cCutter::Stop(void) esyslog("ERROR: '%s' during editing process", Error); if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), editedVersionName) == 0) cControl::Shutdown(); - RemoveVideoFile(editedVersionName); + cVideoDirectory::RemoveVideoFile(editedVersionName); Recordings.DelByName(editedVersionName); } } diff --git a/menu.c b/menu.c index 6224ef71..f7629092 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 3.5 2013/09/07 12:43:26 kls Exp $ + * $Id: menu.c 3.6 2013/09/10 13:16:40 kls Exp $ */ #include "menu.h" @@ -2316,7 +2316,7 @@ void cMenuRecordings::Set(bool Refresh) cString cMenuRecordings::DirectoryName(void) { - cString d(VideoDirectory); + cString d(cVideoDirectory::Name()); if (base) { char *s = ExchangeChars(strdup(base), true); d = AddDirectory(d, s); @@ -4342,7 +4342,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) AssertFreeDiskSpace(Timer->Priority(), !Timer->Pending()); Timer->SetPending(true); } - VideoDiskSpace(&FreeMB); + cVideoDirectory::VideoDiskSpace(&FreeMB); if (FreeMB < MINFREEDISK) { if (!Timer || time(NULL) - LastNoDiskSpaceMessage > NODISKSPACEDELTA) { isyslog("not enough disk space to start recording%s%s", Timer ? " timer " : "", Timer ? *Timer->ToDescr() : ""); diff --git a/recording.c b/recording.c index f11a892e..5cbf1070 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 3.2 2013/08/21 13:56:33 kls Exp $ + * $Id: recording.c 3.3 2013/09/11 08:28:27 kls Exp $ */ #include "recording.h" @@ -90,7 +90,7 @@ cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void) void cRemoveDeletedRecordingsThread::Action(void) { // Make sure only one instance of VDR does this: - cLockFile LockFile(VideoDirectory); + cLockFile LockFile(cVideoDirectory::Name()); if (LockFile.Lock()) { bool deleted = false; cThreadLock DeletedRecordingsLock(&DeletedRecordings); @@ -109,7 +109,7 @@ void cRemoveDeletedRecordingsThread::Action(void) } if (deleted) { const char *IgnoreFiles[] = { SORTMODEFILE, NULL }; - RemoveEmptyVideoDirectories(IgnoreFiles); + cVideoDirectory::RemoveEmptyVideoDirectories(IgnoreFiles); } } } @@ -145,9 +145,9 @@ void AssertFreeDiskSpace(int Priority, bool Force) static time_t LastFreeDiskCheck = 0; int Factor = (Priority == -1) ? 10 : 1; if (Force || time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA / Factor) { - if (!VideoFileSpaceAvailable(MINDISKSPACE)) { + if (!cVideoDirectory::VideoFileSpaceAvailable(MINDISKSPACE)) { // Make sure only one instance of VDR does this: - cLockFile LockFile(VideoDirectory); + cLockFile LockFile(cVideoDirectory::Name()); if (!LockFile.Lock()) return; // Remove the oldest file that has been "deleted": @@ -800,8 +800,8 @@ cRecording::cRecording(const char *FileName) FileName = fileName = strdup(FileName); if (*(fileName + strlen(fileName) - 1) == '/') *(fileName + strlen(fileName) - 1) = 0; - if (strstr(FileName, VideoDirectory) == FileName) - FileName += strlen(VideoDirectory) + 1; + if (strstr(FileName, cVideoDirectory::Name()) == FileName) + FileName += strlen(cVideoDirectory::Name()) + 1; const char *p = strrchr(FileName, '/'); name = NULL; @@ -949,7 +949,7 @@ char *cRecording::SortName(void) const { char **sb = (RecordingsSortMode == rsmName) ? &sortBufferName : &sortBufferTime; if (!*sb) { - char *s = strdup(FileName() + strlen(VideoDirectory)); + char *s = strdup(FileName() + strlen(cVideoDirectory::Name())); if (RecordingsSortMode != rsmName || Setup.AlwaysSortFoldersFirst) s = StripEpisodeName(s, RecordingsSortMode != rsmName); strreplace(s, '/', '0'); // some locales ignore '/' when sorting @@ -990,11 +990,11 @@ const char *cRecording::FileName(void) const const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS; int ch = isPesRecording ? priority : channel; int ri = isPesRecording ? lifetime : instanceId; - char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(VideoDirectory) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve + char *Name = LimitNameLengths(strdup(name), DirectoryPathMax - strlen(cVideoDirectory::Name()) - 1 - 42, DirectoryNameMax); // 42 = length of an actual recording directory name (generated with DATAFORMATTS) plus some reserve if (strcmp(Name, name) != 0) dsyslog("recording file name '%s' truncated to '%s'", name, Name); Name = ExchangeChars(Name, true); - fileName = strdup(cString::sprintf(fmt, VideoDirectory, Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri)); + fileName = strdup(cString::sprintf(fmt, cVideoDirectory::Name(), Name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri)); free(Name); } return fileName; @@ -1063,7 +1063,7 @@ const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) cons const char *cRecording::PrefixFileName(char Prefix) { - cString p = PrefixVideoFileName(FileName(), Prefix); + cString p = cVideoDirectory::PrefixVideoFileName(FileName(), Prefix); if (*p) { free(fileName); fileName = strdup(p); @@ -1093,7 +1093,7 @@ bool cRecording::IsEdited(void) const bool cRecording::IsOnVideoDirectoryFileSystem(void) const { if (isOnVideoDirectoryFileSystem < 0) - isOnVideoDirectoryFileSystem = ::IsOnVideoDirectoryFileSystem(FileName()); + isOnVideoDirectoryFileSystem = cVideoDirectory::IsOnVideoDirectoryFileSystem(FileName()); return isOnVideoDirectoryFileSystem; } @@ -1135,11 +1135,11 @@ bool cRecording::Delete(void) if (access(NewName, F_OK) == 0) { // the new name already exists, so let's remove that one first: isyslog("removing recording '%s'", NewName); - RemoveVideoFile(NewName); + cVideoDirectory::RemoveVideoFile(NewName); } isyslog("deleting recording '%s'", FileName()); if (access(FileName(), F_OK) == 0) { - result = RenameVideoFile(FileName(), NewName); + result = cVideoDirectory::RenameVideoFile(FileName(), NewName); cRecordingUserCommand::InvokeCommand(RUC_DELETERECORDING, NewName); } else { @@ -1159,7 +1159,7 @@ bool cRecording::Remove(void) return false; } isyslog("removing recording %s", FileName()); - return RemoveVideoFile(FileName()); + return cVideoDirectory::RemoveVideoFile(FileName()); } bool cRecording::Undelete(void) @@ -1177,7 +1177,7 @@ bool cRecording::Undelete(void) else { isyslog("undeleting recording '%s'", FileName()); if (access(FileName(), F_OK) == 0) - result = RenameVideoFile(FileName(), NewName); + result = cVideoDirectory::RenameVideoFile(FileName(), NewName); else { isyslog("deleted recording '%s' vanished", FileName()); result = false; @@ -1250,7 +1250,7 @@ void cRecordings::Action(void) const char *cRecordings::UpdateFileName(void) { if (!updateFileName) - updateFileName = strdup(AddDirectory(VideoDirectory, ".update")); + updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update")); return updateFileName; } @@ -1261,7 +1261,7 @@ void cRecordings::Refresh(bool Foreground) Clear(); ChangeState(); Unlock(); - ScanVideoDir(VideoDirectory, Foreground); + ScanVideoDir(cVideoDirectory::Name(), Foreground); } void cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel) @@ -2274,7 +2274,7 @@ cUnbufferedFile *cFileName::Open(void) int BlockingFlag = blocking ? 0 : O_NONBLOCK; if (record) { dsyslog("recording to '%s'", fileName); - file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_LARGEFILE | BlockingFlag); + file = cVideoDirectory::OpenVideoFile(fileName, O_RDWR | O_CREAT | O_LARGEFILE | BlockingFlag); if (!file) LOG_ERROR_STR(fileName); } @@ -2295,8 +2295,9 @@ cUnbufferedFile *cFileName::Open(void) void cFileName::Close(void) { if (file) { - if (CloseVideoFile(file) < 0) + if (file->Close() < 0) LOG_ERROR_STR(fileName); + delete file; file = NULL; } } diff --git a/svdrp.c b/svdrp.c index 8a50daeb..ef1296d9 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 2.24 2013/02/17 13:18:01 kls Exp $ + * $Id: svdrp.c 3.1 2013/09/10 13:21:38 kls Exp $ */ #include "svdrp.h" @@ -1550,7 +1550,7 @@ void cSVDRP::CmdSTAT(const char *Option) if (*Option) { if (strcasecmp(Option, "DISK") == 0) { int FreeMB, UsedMB; - int Percent = VideoDiskSpace(&FreeMB, &UsedMB); + int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB, &UsedMB); Reply(250, "%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent); } else diff --git a/vdr.c b/vdr.c index bf581a6e..3fc44d35 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 3.1 2013/06/10 14:28:43 kls Exp $ + * $Id: vdr.c 3.2 2013/09/10 13:58:34 kls Exp $ */ #include @@ -663,7 +663,7 @@ int main(int argc, char *argv[]) // Directories: - SetVideoDirectory(VideoDirectory); + cVideoDirectory::SetName(VideoDirectory); if (!ConfigDirectory) ConfigDirectory = DEFAULTCONFDIR; cPlugin::SetConfigDirectory(ConfigDirectory); @@ -1406,6 +1406,7 @@ Exit: } cDevice::Shutdown(); cPositioner::DestroyPositioner(); + cVideoDirectory::Destroy(); EpgHandlers.Clear(); PluginManager.Shutdown(true); cSchedules::Cleanup(true); diff --git a/videodir.c b/videodir.c index 9ad31b6f..cd739ea1 100644 --- a/videodir.c +++ b/videodir.c @@ -1,10 +1,10 @@ /* - * videodir.c: Functions to maintain a distributed video directory + * videodir.c: Functions to maintain the video directory * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.c 3.1 2013/08/23 12:28:06 kls Exp $ + * $Id: videodir.c 3.2 2013/09/11 12:20:37 kls Exp $ */ #include "videodir.h" @@ -19,213 +19,129 @@ #include "recording.h" #include "tools.h" -//#define DEPRECATED_DISTRIBUTED_VIDEODIR // Code enclosed with this macro is deprecated and will be removed in a future version - +#ifdef DEPRECATED_VIDEODIR const char *VideoDirectory = VIDEODIR; - -void SetVideoDirectory(const char *Directory) -{ - VideoDirectory = strdup(Directory); -} - -class cVideoDirectory { -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR -private: - char *name, *stored, *adjusted; - int length, number, digits; #endif -public: - cVideoDirectory(void); - ~cVideoDirectory(); - int FreeMB(int *UsedMB = NULL); - const char *Name(void) { return -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - name ? name : -#endif - VideoDirectory; } -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - const char *Stored(void) { return stored; } - int Length(void) { return length; } - bool IsDistributed(void) { return name != NULL; } - bool Next(void); - void Store(void); - const char *Adjust(const char *FileName); -#endif - }; +cString cVideoDirectory::name; +cVideoDirectory *cVideoDirectory::current = NULL; cVideoDirectory::cVideoDirectory(void) { -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - length = strlen(VideoDirectory); - name = (VideoDirectory[length - 1] == '0') ? strdup(VideoDirectory) : NULL; - stored = adjusted = NULL; - number = -1; - digits = 0; -#endif + delete current; + current = this; } cVideoDirectory::~cVideoDirectory() { -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - free(name); - free(stored); - free(adjusted); -#endif + current = NULL; +} + +cVideoDirectory *cVideoDirectory::Current(void) +{ + if (!current) + current = new cVideoDirectory; + return current; +} + +void cVideoDirectory::Destroy(void) +{ + delete current; } int cVideoDirectory::FreeMB(int *UsedMB) { - return FreeDiskSpaceMB( -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - name ? name : + return FreeDiskSpaceMB(Name(), UsedMB); +} + +const char *cVideoDirectory::Name(void) +{ + return name; +} + +void cVideoDirectory::SetName(const char *Name) +{ + name = Name; +#ifdef DEPRECATED_VIDEODIR + VideoDirectory = Name; #endif - VideoDirectory, UsedMB); } -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR -bool cVideoDirectory::Next(void) +bool cVideoDirectory::Register(const char *FileName) { - if (name) { - if (number < 0) { - int l = length; - while (l-- > 0 && isdigit(name[l])) - ; - l++; - digits = length - l; - int n = atoi(&name[l]); - if (n == 0) - number = n; - else - return false; // base video directory must end with zero - } - if (++number > 0) { - char buf[16]; - if (sprintf(buf, "%0*d", digits, number) == digits) { - strcpy(&name[length - digits], buf); - return DirectoryOk(name); - } - } - } - return false; -} - -void cVideoDirectory::Store(void) -{ - if (name) { - free(stored); - stored = strdup(name); - } -} - -const char *cVideoDirectory::Adjust(const char *FileName) -{ - if (stored) { - free(adjusted); - adjusted = strdup(FileName); - return strncpy(adjusted, stored, length); - } - return NULL; -} -#endif - -cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags) -{ - const char *ActualFileName = FileName; - // Incoming name must be in base video directory: - if (strstr(FileName, VideoDirectory) != FileName) { - esyslog("ERROR: %s not in %s", FileName, VideoDirectory); + if (strstr(FileName, Name()) != FileName) { + esyslog("ERROR: %s not in %s", FileName, Name()); errno = ENOENT; // must set 'errno' - any ideas for a better value? - return NULL; - } -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - // Are we going to create a new file? - if ((Flags & O_CREAT) != 0) { - cVideoDirectory Dir; - if (Dir.IsDistributed()) { - // Find the directory with the most free space: - int MaxFree = Dir.FreeMB(); - while (Dir.Next()) { - int Free = FreeDiskSpaceMB(Dir.Name()); - if (Free > MaxFree) { - Dir.Store(); - MaxFree = Free; - } - } - if (Dir.Stored()) { - ActualFileName = Dir.Adjust(FileName); - if (!MakeDirs(ActualFileName, false)) - return NULL; // errno has been set by MakeDirs() - if (symlink(ActualFileName, FileName) < 0) { - LOG_ERROR_STR(FileName); - return NULL; - } - ActualFileName = strdup(ActualFileName); // must survive Dir! - } - } - } -#endif - cUnbufferedFile *File = cUnbufferedFile::Create(ActualFileName, Flags, DEFFILEMODE); - if (ActualFileName != FileName) - free((char *)ActualFileName); - return File; -} - -int CloseVideoFile(cUnbufferedFile *File) -{ - int Result = File->Close(); - delete File; - return Result; -} - -bool RenameVideoFile(const char *OldName, const char *NewName) -{ - // Only the base video directory entry will be renamed, leaving the - // possible symlinks untouched. Going through all the symlinks and disks - // would be unnecessary work - maybe later... - if (rename(OldName, NewName) == -1) { - LOG_ERROR_STR(OldName); return false; } return true; } -bool RemoveVideoFile(const char *FileName) +bool cVideoDirectory::Rename(const char *OldName, const char *NewName) { - return RemoveFileOrDir(FileName, true); -} - -bool VideoFileSpaceAvailable(int SizeMB) -{ - cVideoDirectory Dir; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - if (Dir.IsDistributed()) { - if (Dir.FreeMB() >= SizeMB * 2) // base directory needs additional space - return true; - while (Dir.Next()) { - if (Dir.FreeMB() >= SizeMB) - return true; - } + if (rename(OldName, NewName) == -1) { + LOG_ERROR_STR(NewName); return false; } -#endif - return Dir.FreeMB() >= SizeMB; + return true; } -int VideoDiskSpace(int *FreeMB, int *UsedMB) +bool cVideoDirectory::Move(const char *FromName, const char *ToName) { - int free = 0, used = 0; + if (rename(FromName, ToName) == -1) { + LOG_ERROR_STR(ToName); + return false; + } + return true; +} + +bool cVideoDirectory::Remove(const char *Name) +{ + return RemoveFileOrDir(Name); +} + +void cVideoDirectory::Cleanup(const char *IgnoreFiles[]) +{ + 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); +} + +bool cVideoDirectory::MoveVideoFile(const char *FromName, const char *ToName) +{ + 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); int deleted = DeletedRecordings.TotalFileSizeMB(); - cVideoDirectory Dir; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - do { -#endif - int u; - free += Dir.FreeMB(&u); - used += u; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - } while (Dir.Next()); -#endif if (deleted > used) deleted = used; // let's not get beyond 100% free += deleted; @@ -237,7 +153,7 @@ int VideoDiskSpace(int *FreeMB, int *UsedMB) return (free + used) ? used * 100 / (free + used) : 0; } -cString PrefixVideoFileName(const char *FileName, char Prefix) +cString cVideoDirectory::PrefixVideoFileName(const char *FileName, char Prefix) { char PrefixedName[strlen(FileName) + 2]; @@ -257,30 +173,14 @@ cString PrefixVideoFileName(const char *FileName, char Prefix) return NULL; } -void RemoveEmptyVideoDirectories(const char *IgnoreFiles[]) +void cVideoDirectory::RemoveEmptyVideoDirectories(const char *IgnoreFiles[]) { - cVideoDirectory Dir; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - do { -#endif - RemoveEmptyDirectories(Dir.Name(), false, IgnoreFiles); -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - } while (Dir.Next()); -#endif + Current()->Cleanup(IgnoreFiles); } -bool IsOnVideoDirectoryFileSystem(const char *FileName) +bool cVideoDirectory::IsOnVideoDirectoryFileSystem(const char *FileName) { - cVideoDirectory Dir; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - do { -#endif - if (EntriesOnSameFileSystem(Dir.Name(), FileName)) - return true; -#ifdef DEPRECATED_DISTRIBUTED_VIDEODIR - } while (Dir.Next()); -#endif - return false; + return Current()->Contains(FileName); } // --- cVideoDiskUsage ------------------------------------------------------- @@ -298,7 +198,7 @@ bool cVideoDiskUsage::HasChanged(int &State) { if (time(NULL) - lastChecked > DISKSPACECHEK) { int FreeMB; - int UsedPercent = VideoDiskSpace(&FreeMB); + int UsedPercent = cVideoDirectory::VideoDiskSpace(&FreeMB); if (FreeMB != freeMB) { usedPercent = UsedPercent; freeMB = FreeMB; diff --git a/videodir.h b/videodir.h index a25ac319..c604d046 100644 --- a/videodir.h +++ b/videodir.h @@ -1,10 +1,10 @@ /* - * videodir.h: Functions to maintain a distributed video directory + * videodir.h: Functions to maintain the video directory * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.h 2.3 2012/09/30 11:01:15 kls Exp $ + * $Id: videodir.h 3.1 2013/09/11 12:19:47 kls Exp $ */ #ifndef __VIDEODIR_H @@ -13,18 +13,80 @@ #include #include "tools.h" +#define DEPRECATED_VIDEODIR +#ifdef DEPRECATED_VIDEODIR extern const char *VideoDirectory; +#endif -void SetVideoDirectory(const char *Directory); -cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags); -int CloseVideoFile(cUnbufferedFile *File); -bool RenameVideoFile(const char *OldName, const char *NewName); -bool RemoveVideoFile(const char *FileName); -bool VideoFileSpaceAvailable(int SizeMB); -int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent -cString PrefixVideoFileName(const char *FileName, char Prefix); -void RemoveEmptyVideoDirectories(const char *IgnoreFiles[] = NULL); -bool IsOnVideoDirectoryFileSystem(const char *FileName); +class cVideoDirectory { +private: + static cString name; + static cVideoDirectory *current; + static cVideoDirectory *Current(void); +public: + cVideoDirectory(void); + virtual ~cVideoDirectory(); + virtual int FreeMB(int *UsedMB = NULL); + ///< Returns the total amount (in MB) of free disk space for recording. + ///< If UsedMB is given, it returns the amount of disk space in use by + ///< existing recordings (or anything else) on that disk. + virtual bool Register(const char *FileName); + ///< By default VDR assumes that the video directory consists of one large + ///< volume, on which it can store its recordings. A derived cVideoDirectory + ///< may, for instance, use several separate disks to store recordings. + ///< The given FileName is the full path name (including the video directory) of + ///< a recording file ('*.ts') that is about to be opened for writing. If the actual + ///< file shall be put on an other disk, the derived cVideoDirectory should + ///< create a symbolic link from the given FileName to the other location. + ///< Returns true if the operation was successful. + ///< The default implementation just checks whether the incoming file name really + ///< is under the video directory. + virtual bool Rename(const char *OldName, const char *NewName); + ///< Renames the directory OldName to NewName. + ///< OldName and NewName are full path names that begin with the name of the + ///< video directory and end with '*.rec' or '*.del'. Only the base name (the + ///< rightmost component) of the two names may be different. + ///< Returns true if the operation was successful. + ///< The default implementation just calls the system's rename() function. + virtual bool Move(const char *FromName, const char *ToName); + ///< Moves the directory FromName to the location ToName. FromName is the full + ///< path name of a recording's '*.rec' directory. ToName has the same '*.rec' + ///< part as FromName, but a different directory path above it. + ///< Returns true if the operation was successful. + ///< The default implementation just calls the system's rename() function. + virtual bool Remove(const char *Name); + ///< Removes the directory with the given Name and everything it contains. + ///< Name is a full path name that begins with the name of the video directory. + ///< Returns true if the operation was successful. + ///< The default implementation calls RemoveFileOrDir(). + virtual void Cleanup(const char *IgnoreFiles[] = NULL); + ///< Recursively removes all empty directories under the video directory. + ///< If IgnoreFiles is given, the file names in this (NULL terminated) array + ///< are ignored when checking whether a directory is empty. These are + ///< typically "dot files", like e.g. ".sort". + ///< The default implementation calls RemoveEmptyDirectories(). + virtual bool Contains(const char *Name); + ///< Checks whether the directory Name is on the same file system as the + ///< video directory. Name is the full path name of a recording's '*.rec' + ///< directory. This function is usually called when an ongoing recording + ///< is about to run out of disk space, and an existing (old) recording needs + ///< to be deleted. It shall make sure that deleting this old recording will + ///< actually free up space in the video directory, and not on some other + ///< device that just happens to be mounted. + ///< The default implementation calls EntriesOnSameFileSystem(). + static const char *Name(void); + static void SetName(const char *Name); + static void Destroy(void); + static cUnbufferedFile *OpenVideoFile(const char *FileName, int Flags); + static bool RenameVideoFile(const char *OldName, const char *NewName); + static bool MoveVideoFile(const char *FromName, const char *ToName); + static bool RemoveVideoFile(const char *FileName); + static bool VideoFileSpaceAvailable(int SizeMB); + static int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent + static cString PrefixVideoFileName(const char *FileName, char Prefix); + static void RemoveEmptyVideoDirectories(const char *IgnoreFiles[] = NULL); + static bool IsOnVideoDirectoryFileSystem(const char *FileName); + }; class cVideoDiskUsage { private: