mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	Removed the code for distributing recordings over several video directories; added the cVideoDirectory plugin API
This commit is contained in:
		
							
								
								
									
										23
									
								
								HISTORY
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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(). | ||||
|   | ||||
							
								
								
									
										37
									
								
								PLUGINS.html
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								PLUGINS.html
									
									
									
									
									
								
							| @@ -104,6 +104,7 @@ structures and allows it to hook itself into specific areas to perform special a | ||||
| <li><a href="#Remote Control">Remote Control</a> | ||||
| <li><a href="#Conditional Access">Conditional Access</a> | ||||
| <li><a href="#Electronic Program Guide">Electronic Program Guide</a> | ||||
| <li><modified><a href="#The video directory">The video directory</a></modified> | ||||
| </ul> | ||||
| </ul> | ||||
|  | ||||
| @@ -2300,5 +2301,41 @@ to signal VDR that no other EPG handlers shall be queried after this one. | ||||
| <p> | ||||
| See <tt>VDR/epg.h</tt> for details. | ||||
|  | ||||
| <div class="modified"> | ||||
| <hr><h2><a name="The video directory">The video directory</a></h2> | ||||
|  | ||||
| <div class="blurb">Bits and pieces...</div><p> | ||||
|  | ||||
| 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 <tt>cVideoDirectory</tt>, | ||||
| as in | ||||
|  | ||||
| <p><table><tr><td class="code"><pre> | ||||
| #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); | ||||
|   }; | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| See the description in <tt>videodir.h</tt> for details. | ||||
| <p> | ||||
| You should create your derived video directory object in the | ||||
| <a href="#Getting started"><tt>Start()</tt></a> function of your plugin. | ||||
| Note that the object has to be created on the heap (using <tt>new</tt>), | ||||
| and you shall not delete it at any point (it will be deleted automatically | ||||
| when the program ends). | ||||
| </div modified> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										18
									
								
								cutter.c
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								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); | ||||
|      } | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								menu.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								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() : ""); | ||||
|   | ||||
							
								
								
									
										41
									
								
								recording.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								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; | ||||
|      } | ||||
| } | ||||
|   | ||||
							
								
								
									
										4
									
								
								svdrp.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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 | ||||
|   | ||||
							
								
								
									
										5
									
								
								vdr.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								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 <getopt.h> | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										300
									
								
								videodir.c
									
									
									
									
									
								
							
							
						
						
									
										300
									
								
								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; | ||||
|   | ||||
							
								
								
									
										86
									
								
								videodir.h
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								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 <stdlib.h> | ||||
| #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: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user