From 57e0533f20b14e208e41e56f80fd15b3d18f90f1 Mon Sep 17 00:00:00 2001 From: Martin Prochnow Date: Mon, 8 Oct 2007 20:36:03 +0200 Subject: Version 1.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 --- tools.c | 727 ++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 502 insertions(+), 225 deletions(-) (limited to 'tools.c') diff --git a/tools.c b/tools.c index 439051e..a2aa4bf 100644 --- a/tools.c +++ b/tools.c @@ -8,15 +8,18 @@ #include #include #include +#include #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_writeDelete(); - 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