summaryrefslogtreecommitdiff
path: root/tools.c
diff options
context:
space:
mode:
authorMartin Prochnow <nordlicht@martins-kabuff.de>2007-10-08 20:36:03 +0200
committerAndreas Mair <andreas@vdr-developer.org>2007-10-08 20:36:03 +0200
commit57e0533f20b14e208e41e56f80fd15b3d18f90f1 (patch)
tree2e75d86377b4fbc29c9fd76fabd2a5e291a9f204 /tools.c
parent8adfaaf81b104b8e981026da593e40f9d780c251 (diff)
downloadvdr-plugin-extrecmenu-57e0533f20b14e208e41e56f80fd15b3d18f90f1.tar.gz
vdr-plugin-extrecmenu-57e0533f20b14e208e41e56f80fd15b3d18f90f1.tar.bz2
Version 1.0v1.0
- it is now possible to cancel a moving-between-filesystems-process - applied changes for MainMenuHooksPatch - added MainMenuHooksPatch to contrib/-dir; removed old one, which is now obsolete - free disk space is shown for the filesystem of the current directory (can be switched of in plugin's setup menu) - added support for hidding PIN-protected recordings in co-work with PIN-Plugin - added queue for moving recordings between filesystems - added cutter queue - added #ifdef's to switch of font patching for vdr >= 1.5.3 - added setup option to switch of font patching
Diffstat (limited to 'tools.c')
-rw-r--r--tools.c727
1 files changed, 502 insertions, 225 deletions
diff --git a/tools.c b/tools.c
index 439051e..a2aa4bf 100644
--- a/tools.c
+++ b/tools.c
@@ -8,15 +8,18 @@
#include <vdr/plugin.h>
#include <vdr/videodir.h>
#include <vdr/recording.h>
+#include <vdr/cutter.h>
#include "tools.h"
#include "mymenusetup.h"
using namespace std;
+extern bool VfatFileSytem;
#define CONFIGFILE "/extrecmenu.sort.conf"
#define BUFFERSIZE 20972 // (2*1024*1024)/100
-MoveBetweenFileSystems MoveThread;
+SortList *mySortList;
+WorkerThread *MoveCutterThread;
string myStrEscape(string S,const char *Chars)
{
@@ -48,20 +51,20 @@ string myStrReplace(string S,char C1,char C2)
// --- SortList ---------------------------------------------------------------
void SortList::ReadConfigFile()
{
- string configfile(cPlugin::ConfigDirectory());
- configfile+=CONFIGFILE;
+ string configfile(cPlugin::ConfigDirectory());
+ configfile+=CONFIGFILE;
- ifstream in(configfile.c_str());
- if(in)
- {
- string buf;
- while(!in.eof())
+ ifstream in(configfile.c_str());
+ if(in)
{
- getline(in,buf);
- if(buf.length()>0)
- Add(new SortListItem(buf.c_str()));
+ string buf;
+ while(!in.eof())
+ {
+ getline(in,buf);
+ if(buf.length()>0)
+ Add(new SortListItem(buf));
+ }
}
- }
}
void SortList::WriteConfigFile()
@@ -75,183 +78,16 @@ void SortList::WriteConfigFile()
outfile << item->Path() << endl;
}
-bool SortList::Find(char *Path)
+bool SortList::Find(string Path)
{
for(SortListItem *item=First();item;item=Next(item))
{
- if(!strcmp(item->Path(),Path))
+ if(item->Path()==Path)
return true;
}
return false;
}
-// --- MoveBetweenFileSystems -------------------------------------------------
-MoveBetweenFileSystems::MoveBetweenFileSystems():cThread("moving files between filesystems")
-{
-}
-
-bool MoveBetweenFileSystems::Start(string OldName,string NewName,cRecording *Recording)
-{
- oldname=OldName;
- newname=NewName;
- recording=Recording;
-
- return cThread::Start();
-}
-
-bool MoveBetweenFileSystems::IsMoving(string Path)
-{
- if(Active())
- {
- if(recording)
- {
- if(Path==oldname)
- return true;
- }
- else
- {
- if(!strncmp(oldname.c_str(),Path.c_str(),oldname.length()))
- return true;
- }
- }
- return false;
-}
-
-bool MoveBetweenFileSystems::Move(string From,string To,cRecording *Recording)
-{
- if(Recording)
- {
- isyslog("[extrecmenu] moving %s to %s",From.c_str(),To.c_str());
-
- DIR *dir;
- struct dirent *entry;
- dir=opendir(From.c_str());
- while((entry=readdir(dir))!=NULL)
- {
- string from,to;
- from=From+'/'+entry->d_name;
- to=To+'/'+entry->d_name;
-
- struct stat st;
- stat(from.c_str(),&st);
- if(S_ISREG(st.st_mode))
- {
- isyslog("[extrecmenu] moving %s",entry->d_name);
-
- time_t copytime=time(NULL);
-
- char buf[BUFFERSIZE];
- int infile=-1,outfile=-1;
- struct stat from_stat;
- ssize_t sz,sz_read=1,sz_write;
-
- if(stat(from.c_str(),&from_stat)!=0 ||
- (infile=open(from.c_str(),O_RDONLY))<0 ||
- (outfile=open(to.c_str(),O_WRONLY|O_CREAT|O_EXCL,from_stat.st_mode))<0)
- {
- if(infile>=0)
- close(infile);
- closedir(dir);
-
- Skins.Message(mtError,strerror(errno));
- return false;
- }
-
- while(sz_read>0 && (sz_read=read(infile,buf,BUFFERSIZE))>0)
- {
- sz_write=0;
- do
- {
- if((sz=write(outfile,buf+sz_write,sz_read-sz_write))<0)
- {
- close(infile);
- close(outfile);
- closedir(dir);
-
- Skins.Message(mtError,tr("Rename/Move failed!"));
- esyslog("[extrecmenu] %s",strerror(errno));
- return false;
- }
- sz_write+=sz;
- }
- while(sz_write<sz_read);
-
- if(mysetup.LimitBandwidth)
- usleep(10);
- }
- close(infile);
- close(outfile);
-
- copytime=time(NULL)-copytime;
- isyslog("[extrecmenu] needed %d secs for %d bytes",(int)copytime,(int)st.st_size);
- }
- }
- closedir(dir);
-
- Recordings.AddByName(To.c_str());
- Recording->Delete();
- Recordings.DelByName(From.c_str());
-
- string buf="move \"";
- buf+=myStrEscape(From,"'\\\"$");
- buf+="\"";
- cRecordingUserCommand::InvokeCommand(buf.c_str(),To.c_str());
- }
- else
- {
- string buf;
- buf=From+'/';
- if(!strncmp(buf.c_str(),To.c_str(),buf.length()))
- {
- Skins.Message(mtError,tr("Moving into own sub-directory not allowed!"));
- return false;
- }
-
- myRecList *list=new myRecList();
- for(cRecording *recording=Recordings.First();recording;recording=Recordings.Next(recording))
- list->Add(new myRecListItem(recording));
-
- myRecListItem *item=list->First();
- while(item)
- {
- if(!strncmp(From.c_str(),item->recording->FileName(),From.length()))
- {
- char *buffer=NULL;
- buffer=strdup(From.c_str()+strlen(VideoDirectory)+1);
- buffer=ExchangeChars(buffer,false);
-
- if(strcmp(item->recording->Name(),buffer))
- {
- buf=To+(item->recording->FileName()+From.length());
- if(!MakeDirs(buf.c_str(),true))
- {
- Skins.Message(mtError,tr("Rename/Move failed!"));
- esyslog("[extrecmenu] %s",strerror(errno));
- free(buffer);
- delete list;
- return false;
- }
- if(Move(item->recording->FileName(),buf,item->recording)==false)
- {
- free(buffer);
- delete list;
- return false;
- }
- }
- free(buffer);
- }
- item=list->Next(item);
- }
- delete list;
- }
- return true;
-}
-
-void MoveBetweenFileSystems::Action()
-{
- Move(oldname,newname,recording);
-}
-
// --- MoveRename -------------------------------------------------------------
// creates the necassery directories and renames the given old name to the new name
bool MoveRename(const char *OldName,const char *NewName,cRecording *Recording,bool Move)
@@ -265,16 +101,23 @@ bool MoveRename(const char *OldName,const char *NewName,cRecording *Recording,bo
{
isyslog("[extrecmenu] moving %s to %s",OldName,NewName);
+ if(!MakeDirs(NewName,true))
+ {
+ Skins.Message(mtError,tr("Creating directories failed!"));
+ return false;
+ }
+
if(rename(OldName,NewName)==-1)
{
remove(NewName); // remove created directory
Skins.Message(mtError,tr("Rename/Move failed!"));
- esyslog("[extrecmenu] %s",strerror(errno));
+ esyslog("[extrecmenu] MoveRename() - rename() - %s",strerror(errno));
return false;
}
+ cThreadLock RecordingsLock(&Recordings);
+ Recordings.DelByName(OldName);
Recordings.AddByName(NewName);
- Recordings.Del(Recording);
// set user command for '-r'-option of VDR
asprintf(&buf,"%s \"%s\"",Move?"move":"rename",*strescape(OldName,"'\\\"$"));
@@ -312,7 +155,6 @@ bool MoveRename(const char *OldName,const char *NewName,cRecording *Recording,bo
if(!MakeDirs(buf,true))
{
Skins.Message(mtError,tr("Creating directories failed!"));
- esyslog("[extrecmenu] %s",strerror(errno));
free(buf);
delete list;
return false;
@@ -338,66 +180,66 @@ bool myRecListItem::SortByName=false;
myRecListItem::myRecListItem(cRecording *Recording)
{
- recording=Recording;
- filename=strdup(recording->FileName());
+ recording=Recording;
+ filename=strdup(recording->FileName());
}
myRecListItem::~myRecListItem()
{
- free(filename);
+ free(filename);
}
char *myRecListItem::StripEpisodeName(char *s)
{
- char *t=s,*s1=NULL,*s2=NULL;
- while(*t)
- {
- if(*t=='/')
+ char *t=s,*s1=NULL,*s2=NULL;
+ while(*t)
{
- if(s1)
- {
- if(s2)
- s1=s2;
- s2=t;
- }
- else
- s1=t;
+ if(*t=='/')
+ {
+ if(s1)
+ {
+ if(s2)
+ s1=s2;
+ s2=t;
+ }
+ else
+ s1=t;
+ }
+ t++;
}
- t++;
- }
- if(mysetup.DescendSorting)
- {
- if(SortByName)
- *s1=1;
- else
- *(s2+1)=255;
- }
- else
- *s1=255;
+ if(mysetup.DescendSorting)
+ {
+ if(SortByName)
+ *s1=1;
+ else
+ *(s2+1)=255;
+ }
+ else
+ *s1=255;
- if(s1&&s2&&!SortByName)
- memmove(s1+1,s2,t-s2+1);
+ if(s1 && s2 && !SortByName)
+ memmove(s1+1,s2,t-s2+1);
- return s;
+ return s;
}
int myRecListItem::Compare(const cListObject &ListObject)const
{
- myRecListItem *item=(myRecListItem*)&ListObject;
+ myRecListItem *item=(myRecListItem*)&ListObject;
- char *s1=StripEpisodeName(strdup(filename+strlen(VideoDirectory)));
- char *s2=StripEpisodeName(strdup(item->filename+strlen(VideoDirectory)));
-
- int compare;
- if(mysetup.DescendSorting)
- compare=strcasecmp(s2,s1);
- else
- compare=strcasecmp(s1,s2);
+ char *s1=StripEpisodeName(strdup(filename+strlen(VideoDirectory)));
+ char *s2=StripEpisodeName(strdup(item->filename+strlen(VideoDirectory)));
+
+ int compare;
+ if(mysetup.DescendSorting)
+ compare=strcasecmp(s2,s1);
+ else
+ compare=strcasecmp(s1,s2);
- free(s1);
- free(s2);
+ free(s1);
+ free(s2);
- return compare;
+ return compare;
}
// --- myRecList --------------------------------------------------------------
@@ -406,3 +248,438 @@ void myRecList::Sort(bool SortByName)
myRecListItem::SortByName=SortByName;
cListBase::Sort();
}
+
+// --- WorkerThread -----------------------------------------------------------
+WorkerThread::WorkerThread():cThread("extrecmenu worker thread")
+{
+ cancelmove=cancelcut=false;
+ CutterQueue=new CutterList();
+ MoveBetweenFileSystemsList=new MoveList();
+
+ Start();
+}
+
+WorkerThread::~WorkerThread()
+{
+ Cancel(3);
+
+ delete CutterQueue;
+ delete MoveBetweenFileSystemsList;
+}
+
+const char *WorkerThread::Working()
+{
+ if(CutterQueue->First()!=NULL)
+ return tr("Cutter queue not empty");
+
+ if(MoveBetweenFileSystemsList->First()!=NULL)
+ return tr("Move recordings in progress");
+
+ return NULL;
+}
+
+void WorkerThread::Action()
+{
+ CutterListItem *cutteritem=NULL;
+ MoveListItem *moveitem=NULL;
+
+ while(Running())
+ {
+ if((cutteritem=CutterQueue->First())!=NULL)
+ {
+ cutteritem->SetCutInProgress();
+
+ // create filename for edited recording, check for recordings with this name, if exists -> delete recording
+ // (based upon VDR's code (cutter.c))
+ cRecording rec(cutteritem->FileName().c_str());
+ const char *editedfilename=rec.PrefixFileName('%');
+ if(editedfilename && RemoveVideoFile(editedfilename) && MakeDirs(editedfilename,true))
+ {
+ char *s=strdup(editedfilename);
+ char *e=strrchr(s,'.');
+ if(e)
+ {
+ if(!strcmp(e,".rec"))
+ {
+ strcpy(e,".del");
+ RemoveVideoFile(s);
+ }
+ }
+ free(s);
+ rec.WriteInfo(); // don't know why, but VDR also does it
+ Recordings.AddByName(editedfilename);
+ cutteritem->SetNewFileName(editedfilename);
+ Cut(cutteritem->FileName(),editedfilename);
+ }
+ else
+ Skins.QueueMessage(mtError,tr("Can't start editing process!"));
+ CutterQueue->Del(cutteritem);
+
+ Recordings.ChangeState();
+ }
+
+ if((moveitem=MoveBetweenFileSystemsList->First())!=NULL)
+ {
+ moveitem->SetMoveInProgress();
+ if(Move(moveitem->From(),moveitem->To()))
+ MoveBetweenFileSystemsList->Del(moveitem);
+ else
+ // error occured -> empty move queue
+ MoveBetweenFileSystemsList->Clear();
+
+ Recordings.ChangeState();
+ }
+
+ sleep(1);
+ }
+}
+
+void WorkerThread::AddToCutterQueue(std::string Path)
+{
+ CutterQueue->Add(new CutterListItem(Path));
+}
+
+bool WorkerThread::IsCutting(string Path)
+{
+ for(CutterListItem *item=CutterQueue->First();item;item=CutterQueue->Next(item))
+ {
+ if(Path==item->FileName() || Path==item->NewFileName())
+ return true;
+ }
+ return false;
+}
+
+void WorkerThread::CancelCut(string Path)
+{
+ for(CutterListItem *item=CutterQueue->First();item;item=CutterQueue->Next(item))
+ {
+ if(item->FileName()==Path || item->NewFileName()==Path)
+ {
+ if(item->GetCutInProgress())
+ cancelcut=true;
+ else
+ CutterQueue->Del(item);
+
+ return;
+ }
+ }
+}
+
+// this based mainly upon VDR's code (cutter.c)
+void WorkerThread::Cut(string From,string To)
+{
+ cUnbufferedFile *fromfile=NULL,*tofile=NULL;
+ cFileName *fromfilename=NULL,*tofilename=NULL;
+ cIndexFile *fromindex=NULL,*toindex=NULL;
+ cMarks frommarks,tomarks;
+ cMark *mark;
+ const char *error=NULL;
+ uchar filenumber,picturetype,buffer[MAXFRAMESIZE];
+ int fileoffset,length,index,currentfilenumber=0,filesize=0,lastiframe=0;
+ bool lastmark=false,cutin=true;
+
+ if(frommarks.Load(From.c_str()) && frommarks.Count())
+ {
+ fromfilename=new cFileName(From.c_str(),false,true);
+ tofilename=new cFileName(To.c_str(),true,false);
+ fromindex=new cIndexFile(From.c_str(),false);
+ toindex=new cIndexFile(To.c_str(),true);
+ tomarks.Load(To.c_str());
+ }
+ else
+ {
+ esyslog("[extrecmenu] no editing marks found for %s",From.c_str());
+ return;
+ }
+
+ if((mark=frommarks.First())!=NULL)
+ {
+ if(!(fromfile=fromfilename->Open()) || !(tofile=tofilename->Open()))
+ return;
+ fromfile->SetReadAhead(MEGABYTE(20));
+ index=mark->position;
+ mark=frommarks.Next(mark);
+ tomarks.Add(0);
+ tomarks.Save();
+ }
+ else
+ {
+ esyslog("[extrecmenu] no editing marks found for %s",From.c_str());
+ return;
+ }
+
+ isyslog("[extecmenu] editing %s",From.c_str());
+ while(fromindex->Get(index++,&filenumber,&fileoffset,&picturetype,&length) && Running() && !cancelcut)
+ {
+ AssertFreeDiskSpace(-1);
+
+ if(filenumber!=currentfilenumber)
+ {
+ fromfile=fromfilename->SetOffset(filenumber,fileoffset);
+ fromfile->SetReadAhead(MEGABYTE(20));
+ currentfilenumber=filenumber;
+ }
+ if(fromfile)
+ {
+ int len=ReadFrame(fromfile,buffer,length,sizeof(buffer));
+ if(len<0)
+ {
+ error="ReadFrame";
+ break;
+ }
+ if(len!=length)
+ {
+ currentfilenumber=0;
+ length=len;
+ }
+ }
+ else
+ {
+ error="fromfile";
+ break;
+ }
+ if(picturetype==I_FRAME)
+ {
+ if(lastmark)
+ break;
+ if(filesize > MEGABYTE(Setup.MaxVideoFileSize))
+ {
+ tofile=tofilename->NextFile();
+ if(!tofile)
+ {
+ error="tofile 1";
+ break;
+ }
+ filesize=0;
+ }
+ lastiframe=0;
+ if(cutin)
+ {
+ cRemux::SetBrokenLink(buffer,length);
+ cutin=false;
+ }
+ }
+ if(tofile->Write(buffer,length)<0)
+ {
+ error="safe_write";
+ break;
+ }
+ if(!toindex->Write(picturetype,tofilename->Number(),filesize))
+ {
+ error="toindex";
+ break;
+ }
+ filesize+=length;
+ if(!lastiframe)
+ lastiframe=toindex->Last();
+
+ if(mark && index >= mark->position)
+ {
+ mark=frommarks.Next(mark);
+ tomarks.Add(lastiframe);
+ if(mark)
+ tomarks.Add(toindex->Last()+1);
+ tomarks.Save();
+ if(mark)
+ {
+ index=mark->position;
+ mark=frommarks.Next(mark);
+ currentfilenumber=0;
+ cutin=true;
+ if(Setup.SplitEditedFiles)
+ {
+ tofile=tofilename->NextFile();
+ if(!tofile)
+ {
+ error="tofile 2";
+ break;
+ }
+ filesize=0;
+ }
+ }
+ else
+ lastmark=true;
+ }
+ }
+ if(!Running() || cancelcut || error)
+ {
+ if(error)
+ esyslog("[extrecmenu] ERROR: '%s' during editing process",error);
+ else
+ isyslog("[extrecmenu] editing process canceled, deleting edited recording");
+
+ cancelcut=false;
+ RemoveVideoFile(To.c_str());
+ Recordings.DelByName(To.c_str());
+ }
+ else
+ {
+ isyslog("[extrecmenu] editing process ended");
+ cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING,To.c_str());
+ }
+ Recordings.TouchUpdate();
+ delete fromfilename;
+ delete tofilename;
+ delete fromindex;
+ delete toindex;
+}
+
+bool WorkerThread::IsMoving(string Path)
+{
+ for(MoveListItem *item=MoveBetweenFileSystemsList->First();item;item=MoveBetweenFileSystemsList->Next(item))
+ {
+ if(Path==item->From() && !item->GetMoveCanceled())
+ return true;
+ }
+ return false;
+}
+
+void WorkerThread::CancelMove(string Path)
+{
+ for(MoveListItem *item=MoveBetweenFileSystemsList->First();item;item=MoveBetweenFileSystemsList->Next(item))
+ {
+ if(Path==item->From())
+ {
+ if(item->GetMoveInProgress())
+ {
+ cancelmove=true;
+ item->SetMoveCanceled();
+ }
+ else
+ MoveBetweenFileSystemsList->Del(item);
+
+ return;
+ }
+ }
+}
+
+void WorkerThread::AddToMoveList(string From,string To)
+{
+ MoveBetweenFileSystemsList->Add(new MoveListItem(From,To));
+ Recordings.ChangeState();
+}
+
+bool WorkerThread::Move(string From,string To)
+{
+ if(!MakeDirs(To.c_str(),true))
+ {
+ Skins.QueueMessage(mtError,tr("Creating directories failed!"));
+ return false;
+ }
+
+ isyslog("[extrecmenu] moving '%s' to '%s'",From.c_str(),To.c_str());
+
+ DIR *dir=NULL;
+ struct dirent *entry;
+ int infile=-1,outfile=-1;
+
+ if((dir=opendir(From.c_str()))!=NULL)
+ {
+ bool ok=true;
+ // copy each file in this dir, except sub-dirs
+ while((entry=readdir(dir))!=NULL)
+ {
+ string from,to;
+ from=From+"/"+entry->d_name;
+ to=To+"/"+entry->d_name;
+
+ AssertFreeDiskSpace(-1);
+
+ struct stat st;
+ if(stat(from.c_str(),&st)==0)
+ {
+ if(S_ISREG(st.st_mode))
+ {
+ isyslog("[extrecmenu] moving '%s'",entry->d_name);
+
+ ssize_t sz,sz_read=1,sz_write;
+ if(stat(from.c_str(),&st)==0 && (infile=open(from.c_str(),O_RDONLY))!=-1 && (outfile=open(to.c_str(),O_WRONLY|O_CREAT|O_EXCL,st.st_mode))!=-1)
+ {
+ char buf[BUFFERSIZE];
+ while(sz_read>0 && (sz_read=read(infile,buf,BUFFERSIZE))>0)
+ {
+ AssertFreeDiskSpace(-1);
+
+ sz_write=0;
+ do
+ {
+ if(cancelmove || !Running())
+ {
+ cancelmove=false;
+
+ close(infile);
+ close(outfile);
+ closedir(dir);
+
+ isyslog("[extrecmenu] moving canceled");
+
+ RemoveVideoFile(To.c_str());
+
+ return true;
+ }
+
+ if((sz=write(outfile,buf+sz_write,sz_read-sz_write))<0)
+ {
+ close(infile);
+ close(outfile);
+ closedir(dir);
+
+ Skins.Message(mtError,tr("Rename/Move failed!"));
+ esyslog("[extrecmenu] WorkerThread::Move() - write() - %s",strerror(errno));
+ return false;
+ }
+ sz_write+=sz;
+ }
+ while(sz_write<sz_read);
+
+ if(mysetup.LimitBandwidth)
+ usleep(10);
+ }
+ close(infile);
+ close(outfile);
+ }
+ else
+ {
+ ok=false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ ok=false;
+ break;
+ }
+ }
+ if(ok)
+ {
+ closedir(dir);
+
+ cThreadLock RecordingsLock(&Recordings);
+ cRecording rec(From.c_str());
+ rec.Delete();
+ Recordings.DelByName(From.c_str());
+ Recordings.AddByName(To.c_str());
+
+ string cmdstring="move \"";
+ cmdstring+=myStrEscape(From,"'\\\"$");
+ cmdstring+="\"";
+ cRecordingUserCommand::InvokeCommand(cmdstring.c_str(),To.c_str());
+
+ Recordings.TouchUpdate();
+
+ return true;
+ }
+ }
+ if(dir)
+ closedir(dir);
+ if(infile!=-1)
+ close(infile);
+ if(outfile!=-1)
+ close(outfile);
+
+ Skins.QueueMessage(mtError,tr("Rename/Move failed!"));
+ esyslog("[extrecmenu] WorkerThread::Move() - %s",strerror(errno));
+ return false;
+}
+