diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 5ba76e0c..775455d3 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1170,6 +1170,7 @@ Rolf Ahrenberg for a patch that was used to rename the "plp id" to a more general "stream id" and add support for DVB-S2 "Input Stream Identifier" (ISI) for helping to debug and understand subtitle page refreshes + for a patch that was used to implement the SVDRP command RENR Ralf Klueber for reporting a bug in cutting a recording if there is only a single editing mark @@ -3180,3 +3181,7 @@ Manfred V Thomas Maass for reporting a difference in the internal sequence of actions when pressing the Blue and the Back key, respectively, during replay + +Martin Prochnow + for writing the "extrecmenu" plugin, which inspired the implementation of editing + recording properties diff --git a/HISTORY b/HISTORY index 3f880fce..80e01096 100644 --- a/HISTORY +++ b/HISTORY @@ -7920,7 +7920,7 @@ Video Disk Recorder Revision History - Fixed cleaning up old EPG events in case no epg data file is given (reported by Dave Pickles). -2013-09-07: Version 2.1.2 +2013-10-10: Version 2.1.2 - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed displaying DVB subtitles (thanks to Rolf Ahrenberg for helping to debug and @@ -7966,3 +7966,23 @@ Video Disk Recorder Revision History #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(). +- Added renaming and moving recordings and folders, editing a recording's priority and + lifetime, and queueing cutting jobs (inspired by the "extrecmenu" plugin from Martin + Prochnow). + + The "Recording info" menu now has a new Blue button named "Edit", which opens a + dialog in which several properties of the selected recording can be changed. It can + be renamed or moved into another folder and its priority and lifetime can be + modified (inspired by the "extrecmenu" plugin from Martin Prochnow). + The new blue "Edit" button in the "Recordings" menu opens a dialog in which a folder + can be renamed or moved. See MANUAL, section "Managing folders". + + In the "Edit recording" menu the Yellow button ("Delete marks") allows you to delete + all editing marks of the selected recording. + + cCutter is no longer a static class. Cutting requests should now be invoked by + calling RecordingsHandler.Add(ruCut, FileName). See the new cRecordingsHandler + class in recording.h. + + Cutting jobs are now placed in a queue (together with any move or copy jobs) and + are processed one by one. + + The new SVDRP command RENR can be used to rename a recording (suggested by Rolf + Ahrenberg). + + Note that in several places in the source code a "copy" operation is mentioned, + however there is no user interface for this, yet. diff --git a/MANUAL b/MANUAL index e698085d..5ba44448 100644 --- a/MANUAL +++ b/MANUAL @@ -504,6 +504,30 @@ Version 2.0 folder, or enters a sub folder. Once a folder has been selected, the entire path of the timer's file name will be replaced with the selected folder. + In the "Recordings" menu the folders of existing recordings can be renamed or + moved by pressing the "Blue" key ("Edit") while the cursor is positioned on + a folder. This will open a menu in which the folder's name and location (the + "parent" folder) can be edited. If such an operation will result in moving + more than one recording, you will be asked for confirmation. + The name, folder, priority and lifetime of an individual recording can be + changed by pressing the "Blue" key ("Info") while the cursor is positioned + on a recording, and in the resulting Info menu pressing the "Blue" key again + to bring up the "Edit recording" menu. + In the "Edit recording" menu the Red button ("Folder") allows you to select one + of your predefined folders. The Green button has multiple functions, depending + on what is currently going on with the recording. It can either stop or cancel + a cut, move or copy operation. If the button reads "Stop..." it means that the + respective operation is already happening, while "Cancel..." means that the + operation is still pending execution. If no operation is currently happening + and the recording has editing marks, the Button will read "Cut" and triggers + cutting the recording (same as pressing '2' while replaying the recording). + The Yellow button ("Delete marks") allows you to delete all editing marks from + the selected recording (if there are any and the recording is not currently + being cut). To directly edit the folder or name of the recording, position the + cursor to the respective line and press the Right key to start editing (press + Ok to confirm the edit, or Back to return to the previous value). Once you are + finished with editing the recording properties, press Ok to confirm the changes. + * Parameters in the "Setup" menu Select "Setup" from the "VDR" menu to enter the setup menu. From there you can diff --git a/cutter.c b/cutter.c index ccb145a3..9aee66b9 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.3 2013/09/10 14:51:45 kls Exp $ + * $Id: cutter.c 3.4 2013/10/02 13:18:02 kls Exp $ */ #include "cutter.h" @@ -642,34 +642,47 @@ void cCuttingThread::Action(void) // --- cCutter --------------------------------------------------------------- -cMutex cCutter::mutex; -cString cCutter::originalVersionName; -cString cCutter::editedVersionName; -cCuttingThread *cCutter::cuttingThread = NULL; -bool cCutter::error = false; -bool cCutter::ended = false; - -bool cCutter::Start(const char *FileName) +cCutter::cCutter(const char *FileName) +{ + cuttingThread = NULL; + error = false; + originalVersionName = FileName; +} + +cCutter::~cCutter() +{ + Stop(); +} + +cString cCutter::EditedFileName(const char *FileName) +{ + cRecording Recording(FileName); + cMarks Marks; + if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording())) { + if (cMark *First = Marks.GetNextBegin()) + Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60); + return Recording.PrefixFileName('%'); + } + return NULL; +} + +bool cCutter::Start(void) { - cMutexLock MutexLock(&mutex); if (!cuttingThread) { error = false; - ended = false; - originalVersionName = FileName; - cRecording Recording(FileName); - - cMarks FromMarks; - FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()); - if (cMark *First = FromMarks.GetNextBegin()) - Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60); - - const char *evn = Recording.PrefixFileName('%'); - if (evn && cVideoDirectory::RemoveVideoFile(evn) && MakeDirs(evn, true)) { - editedVersionName = evn; - Recording.WriteInfo(); - Recordings.AddByName(editedVersionName, false); - cuttingThread = new cCuttingThread(FileName, editedVersionName); - return true; + if (*originalVersionName) { + cRecording Recording(originalVersionName); + editedVersionName = EditedFileName(originalVersionName); + if (*editedVersionName) { + if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different! + if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) { + Recording.WriteInfo(editedVersionName); + Recordings.AddByName(editedVersionName, false); + cuttingThread = new cCuttingThread(originalVersionName, editedVersionName); + return true; + } + } + } } } return false; @@ -677,7 +690,6 @@ bool cCutter::Start(const char *FileName) void cCutter::Stop(void) { - cMutexLock MutexLock(&mutex); bool Interrupted = cuttingThread && cuttingThread->Active(); const char *Error = cuttingThread ? cuttingThread->Error() : NULL; delete cuttingThread; @@ -694,37 +706,22 @@ void cCutter::Stop(void) } } -bool cCutter::Active(const char *FileName) +bool cCutter::Active(void) { - cMutexLock MutexLock(&mutex); if (cuttingThread) { if (cuttingThread->Active()) - return !FileName || strcmp(FileName, originalVersionName) == 0 || strcmp(FileName, editedVersionName) == 0; + return true; error = cuttingThread->Error(); Stop(); if (!error) cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName, originalVersionName); - originalVersionName = NULL; - editedVersionName = NULL; - ended = true; } return false; } bool cCutter::Error(void) { - cMutexLock MutexLock(&mutex); - bool result = error; - error = false; - return result; -} - -bool cCutter::Ended(void) -{ - cMutexLock MutexLock(&mutex); - bool result = ended; - ended = false; - return result; + return error; } #define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process @@ -737,10 +734,13 @@ bool CutRecording(const char *FileName) cMarks Marks; if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) { if (Marks.GetNumSequences()) { - if (cCutter::Start(FileName)) { - while (cCutter::Active()) + cCutter Cutter(FileName); + if (Cutter.Start()) { + while (Cutter.Active()) cCondWait::SleepMs(CUTTINGCHECKINTERVAL); - return true; + if (!Cutter.Error()) + return true; + fprintf(stderr, "error while cutting\n"); } else fprintf(stderr, "can't start editing process\n"); diff --git a/cutter.h b/cutter.h index f48ae3fc..c67429a1 100644 --- a/cutter.h +++ b/cutter.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: cutter.h 2.3 2012/02/16 12:05:33 kls Exp $ + * $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $ */ #ifndef __CUTTER_H @@ -17,21 +17,31 @@ class cCuttingThread; class cCutter { private: - static cMutex mutex; - static cString originalVersionName; - static cString editedVersionName; - static cCuttingThread *cuttingThread; - static bool error; - static bool ended; + cString originalVersionName; + cString editedVersionName; + cCuttingThread *cuttingThread; + bool error; public: - static bool Start(const char *FileName); - static void Stop(void); - static bool Active(const char *FileName = NULL); - ///< Returns true if the cutter is currently active. - ///< If a FileName is given, true is only returned if either the - ///< original or the edited file name is equal to FileName. - static bool Error(void); - static bool Ended(void); + cCutter(const char *FileName); + ///< Sets up a new cutter for the given FileName, which must be the full path + ///< name of an existing recording directory. + ~cCutter(); + static cString EditedFileName(const char *FileName); + ///< Returns the full path name of the edited version of the recording with + ///< the given FileName. This static function can be used independent of any + ///< cCutter object, to determine the file name beforehand. + ///< Returns NULL in case of error. + bool Start(void); + ///< Starts the actual cutting process. + ///< Returns true if successful. + ///< If Start() is called while the cutting process is already active, nothing + ///< happens and false will be returned. + void Stop(void); + ///< Stops an ongoing cutting process. + bool Active(void); + ///< Returns true if the cutter is currently active. + bool Error(void); + ///< Returns true if an error occurred while cutting the recording. }; bool CutRecording(const char *FileName); diff --git a/menu.c b/menu.c index f7629092..99ca4a66 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.6 2013/09/10 13:16:40 kls Exp $ + * $Id: menu.c 3.7 2013/10/10 12:31:08 kls Exp $ */ #include "menu.h" @@ -16,7 +16,6 @@ #include #include "channels.h" #include "config.h" -#include "cutter.h" #include "eitscan.h" #include "i18n.h" #include "interface.h" @@ -828,8 +827,7 @@ eOSState cMenuFolder::Edit(void) eOSState cMenuFolder::SetFolder(void) { - cMenuEditFolder *mef = (cMenuEditFolder *)SubMenu(); - if (mef) { + if (cMenuEditFolder *mef = dynamic_cast(SubMenu())) { Set(mef->GetFolder()); SetHelpKeys(); Display(); @@ -843,8 +841,7 @@ cString cMenuFolder::GetFolder(void) if (firstFolder) { cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current()); if (Folder) { - cMenuFolder *mf = (cMenuFolder *)SubMenu(); - if (mf) + if (cMenuFolder *mf = dynamic_cast(SubMenu())) return cString::sprintf("%s%c%s", Folder->Folder()->Text(), FOLDERDELIMCHAR, *mf->GetFolder()); return Folder->Folder()->Text(); } @@ -930,8 +927,7 @@ void cMenuEditTimer::SetFirstDayItem(void) eOSState cMenuEditTimer::SetFolder(void) { - cMenuFolder *mf = (cMenuFolder *)SubMenu(); - if (mf) { + if (cMenuFolder *mf = dynamic_cast(SubMenu())) { cString Folder = mf->GetFolder(); char *p = strrchr(data.file, FOLDERDELIMCHAR); if (p) @@ -2105,26 +2101,302 @@ bool CamMenuActive(void) return CamMenuIsOpen; } +// --- cMenuPathEdit --------------------------------------------------------- + +class cMenuPathEdit : public cOsdMenu { +private: + cString path; + char folder[PATH_MAX]; + char name[NAME_MAX]; + cMenuEditStrItem *folderItem; + int pathIsInUse; + eOSState SetFolder(void); + eOSState Folder(void); + eOSState ApplyChanges(void); +public: + cMenuPathEdit(const char *Path); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuPathEdit::cMenuPathEdit(const char *Path) +:cOsdMenu(tr("Edit path"), 12) +{ + SetMenuCategory(mcRecording); + path = Path; + *folder = 0; + *name = 0; + const char *s = strrchr(path, FOLDERDELIMCHAR); + if (s) { + strn0cpy(folder, cString(path, s), sizeof(folder)); + s++; + } + else + s = path; + strn0cpy(name, s, sizeof(name)); + pathIsInUse = Recordings.PathIsInUse(path); + if (pathIsInUse) { + Add(new cOsdItem(tr("This folder is currently in use - no changes are possible!"), osUnknown, false)); + Add(new cOsdItem("", osUnknown, false)); + } + cOsdItem *p; + Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder))); + p->SetSelectable(!pathIsInUse); + Add(p = new cMenuEditStrItem(tr("Name"), name, sizeof(name))); + p->SetSelectable(!pathIsInUse); + Display(); + if (!pathIsInUse) + SetHelp(tr("Button$Folder")); +} + +eOSState cMenuPathEdit::SetFolder(void) +{ + if (cMenuFolder *mf = dynamic_cast(SubMenu())) { + strn0cpy(folder, mf->GetFolder(), sizeof(folder)); + SetCurrent(folderItem); + Display(); + } + return CloseSubMenu(); +} + +eOSState cMenuPathEdit::Folder(void) +{ + return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, path)); +} + +eOSState cMenuPathEdit::ApplyChanges(void) +{ + if (!*name) + *name = ' '; // name must not be empty! + cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; + NewPath.CompactChars(FOLDERDELIMCHAR); + if (strcmp(NewPath, path)) { + int NumRecordings = Recordings.GetNumRecordingsInPath(path); + if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings))) + return osContinue; + if (!Recordings.MoveRecordings(path, NewPath)) { + Skins.Message(mtError, tr("Error while moving folder!")); + return osContinue; + } + cMenuRecordings::SetPath(NewPath); // makes sure the Recordings menu will reposition to the new path + return osUser1; + } + return osBack; +} + +eOSState cMenuPathEdit::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + if (state == osUnknown) { + if (!pathIsInUse) { + switch (Key) { + case kRed: return Folder(); + case kOk: return ApplyChanges(); + default: break; + } + } + else if (Key == kOk) + return osBack; + } + else if (state == osEnd && HasSubMenu()) + state = SetFolder(); + return state; +} + +// --- cMenuRecordingEdit ---------------------------------------------------- + +class cMenuRecordingEdit : public cOsdMenu { +private: + cRecording *recording; + char folder[PATH_MAX]; + char name[NAME_MAX]; + int priority; + int lifetime; + cMenuEditStrItem *folderItem; + const char *buttonFolder; + const char *buttonAction; + const char *buttonDeleteMarks; + const char *actionCancel; + const char *doCut; + int recordingIsInUse; + void Set(void); + void SetHelpKeys(void); + eOSState SetFolder(void); + eOSState Folder(void); + eOSState Action(void); + eOSState DeleteMarks(void); + eOSState ApplyChanges(void); +public: + cMenuRecordingEdit(cRecording *Recording); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuRecordingEdit::cMenuRecordingEdit(cRecording *Recording) +:cOsdMenu(tr("Edit recording"), 12) +{ + SetMenuCategory(mcRecording); + recording = Recording; + strn0cpy(folder, recording->Folder(), sizeof(folder)); + strn0cpy(name, recording->BaseName(), sizeof(name)); + priority = recording->Priority(); + lifetime = recording->Lifetime(); + folderItem = NULL; + buttonFolder = NULL; + buttonAction = NULL; + buttonDeleteMarks = NULL; + actionCancel = NULL; + doCut = NULL; + recordingIsInUse = recording->IsInUse(); + Set(); +} + +void cMenuRecordingEdit::Set(void) +{ + Clear(); + if (recordingIsInUse) { + Add(new cOsdItem(tr("This recording is currently in use - no changes are possible!"), osUnknown, false)); + Add(new cOsdItem("", osUnknown, false)); + } + cOsdItem *p; + Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder))); + p->SetSelectable(!recordingIsInUse); + Add(p = new cMenuEditStrItem(tr("Name"), name, sizeof(name))); + p->SetSelectable(!recordingIsInUse); + Add(p = new cMenuEditIntItem(tr("Priority"), &priority, 0, MAXPRIORITY)); + p->SetSelectable(!recordingIsInUse); + Add(p = new cMenuEditIntItem(tr("Lifetime"), &lifetime, 0, MAXLIFETIME)); + p->SetSelectable(!recordingIsInUse); + Display(); + SetHelpKeys(); +} + +void cMenuRecordingEdit::SetHelpKeys(void) +{ + buttonFolder = !recordingIsInUse ? tr("Button$Folder") : NULL; + buttonAction = NULL; + buttonDeleteMarks = NULL; + actionCancel = NULL; + doCut = NULL; + if ((recordingIsInUse & ruCut) != 0) + buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Stop cutting") : tr("Button$Cancel cutting"); + else if ((recordingIsInUse & ruMove) != 0) + buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Stop moving") : tr("Button$Cancel moving"); + else if ((recordingIsInUse & ruCopy) != 0) + buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Stop copying") : tr("Button$Cancel copying"); + else if (recording->HasMarks()) { + buttonAction = doCut = tr("Button$Cut"); + buttonDeleteMarks = tr("Button$Delete marks"); + } + SetHelp(buttonFolder, buttonAction, buttonDeleteMarks); +} + +eOSState cMenuRecordingEdit::SetFolder(void) +{ + if (cMenuFolder *mf = dynamic_cast(SubMenu())) { + strn0cpy(folder, mf->GetFolder(), sizeof(folder)); + SetCurrent(folderItem); + Display(); + } + return CloseSubMenu(); +} + +eOSState cMenuRecordingEdit::Folder(void) +{ + return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, recording->Name())); +} + +eOSState cMenuRecordingEdit::Action(void) +{ + if (actionCancel) + RecordingsHandler.Del(recording->FileName()); + else if (doCut) { + if (!RecordingsHandler.Add(ruCut, recording->FileName())) + Skins.Message(mtError, tr("Error while queueing recording for cutting!")); + } + recordingIsInUse = recording->IsInUse(); + SetHelpKeys(); + return osContinue; +} + +eOSState cMenuRecordingEdit::DeleteMarks(void) +{ + if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) { + if (recording->DeleteMarks()) + SetHelpKeys(); + else + Skins.Message(mtError, tr("Error while deleting editing marks!")); + } + return osContinue; +} + +eOSState cMenuRecordingEdit::ApplyChanges(void) +{ + bool Modified = false; + if (priority != recording->Priority() || lifetime != recording->Lifetime()) { + if (!recording->ChangePriorityLifetime(priority, lifetime)) { + Skins.Message(mtError, tr("Error while changing priority/lifetime!")); + return osContinue; + } + Modified = true; + } + if (!*name) + *name = ' '; // name must not be empty! + cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; + NewName.CompactChars(FOLDERDELIMCHAR); + if (strcmp(NewName, recording->Name())) { + if (!recording->ChangeName(NewName)) { + Skins.Message(mtError, tr("Error while changing folder/name!")); + return osContinue; + } + Modified = true; + } + if (Modified) { + cMenuRecordings::SetRecording(recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording + return osUser1; + } + return osBack; +} + +eOSState cMenuRecordingEdit::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + if (state == osUnknown) { + if (!recordingIsInUse) { + switch (Key) { + case kRed: return Folder(); + case kGreen: return Action(); + case kYellow: return DeleteMarks(); + case kOk: return ApplyChanges(); + default: break; + } + } + else if (Key == kOk) + return osBack; + } + else if (state == osEnd && HasSubMenu()) + state = SetFolder(); + return state; +} + // --- cMenuRecording -------------------------------------------------------- class cMenuRecording : public cOsdMenu { private: - const cRecording *recording; + cRecording *recording; bool withButtons; public: - cMenuRecording(const cRecording *Recording, bool WithButtons = false); + cMenuRecording(cRecording *Recording, bool WithButtons = false); virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; -cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons) +cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons) :cOsdMenu(tr("Recording info")) { SetMenuCategory(mcRecordingInfo); recording = Recording; withButtons = WithButtons; if (withButtons) - SetHelp(tr("Button$Play"), tr("Button$Rewind")); + SetHelp(tr("Button$Play"), tr("Button$Rewind"), NULL, tr("Button$Edit")); } void cMenuRecording::Display(void) @@ -2137,6 +2409,12 @@ void cMenuRecording::Display(void) eOSState cMenuRecording::ProcessKey(eKeys Key) { + if (HasSubMenu()) { + eOSState state = cOsdMenu::ProcessKey(Key); + if (state == osUser1) + CloseSubMenu(); + return state; + } switch (int(Key)) { case kUp|k_Repeat: case kUp: @@ -2164,6 +2442,9 @@ eOSState cMenuRecording::ProcessKey(eKeys Key) cRemote::Put(Key, true); // continue with osBack to close the info menu and process the key case kOk: return osBack; + case kBlue: if (withButtons) + return AddSubMenu(new cMenuRecordingEdit(recording)); + break; default: break; } } @@ -2183,6 +2464,7 @@ public: ~cMenuRecordingItem(); void IncrementCounter(bool New); const char *Name(void) { return name; } + int Level(void) { return level; } cRecording *Recording(void) { return recording; } bool IsDirectory(void) { return name != NULL; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); @@ -2220,6 +2502,9 @@ void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, b // --- cMenuRecordings ------------------------------------------------------- +cString cMenuRecordings::path; +cString cMenuRecordings::fileName; + cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus) :cOsdMenu(Base ? Base : tr("Recordings"), 9, 6, 6) { @@ -2232,8 +2517,12 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus) Set(); if (Current() < 0) SetCurrent(First()); - else if (OpenSubMenus && cReplayControl::LastReplayed() && Open(true)) - return; + else if (OpenSubMenus && (cReplayControl::LastReplayed() || *path || *fileName)) { + if (!*path || Level < strcountchr(path, FOLDERDELIMCHAR)) { + if (Open(true)) + return; + } + } Display(); SetHelpKeys(); } @@ -2251,18 +2540,14 @@ void cMenuRecordings::SetHelpKeys(void) if (ri) { if (ri->IsDirectory()) NewHelpKeys = 1; - else { + else NewHelpKeys = 2; - if (ri->Recording()->Info()->Title()) - NewHelpKeys = 3; - } } if (NewHelpKeys != helpKeys) { switch (NewHelpKeys) { case 0: SetHelp(NULL); break; - case 1: SetHelp(tr("Button$Open")); break; - case 2: - case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), NewHelpKeys == 3 ? tr("Button$Info") : NULL); + case 1: SetHelp(tr("Button$Open"), NULL, NULL, tr("Button$Edit")); break; + case 2: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), tr("Button$Info")); default: ; } helpKeys = NewHelpKeys; @@ -2271,7 +2556,7 @@ void cMenuRecordings::SetHelpKeys(void) void cMenuRecordings::Set(bool Refresh) { - const char *CurrentRecording = cReplayControl::LastReplayed(); + const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); cMenuRecordingItem *LastItem = NULL; cThreadLock RecordingsLock(&Recordings); if (Refresh) { @@ -2303,7 +2588,11 @@ void cMenuRecordings::Set(bool Refresh) else delete Item; if (LastItem || LastDir) { - if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0) + if (*path) { + if (strcmp(path, recording->Folder()) == 0) + SetCurrent(LastDir ? LastDir : LastItem); + } + else if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0) SetCurrent(LastDir ? LastDir : LastItem); } if (LastDir) @@ -2314,6 +2603,16 @@ void cMenuRecordings::Set(bool Refresh) Display(); } +void cMenuRecordings::SetPath(const char *Path) +{ + path = Path; +} + +void cMenuRecordings::SetRecording(const char *FileName) +{ + fileName = FileName; +} + cString cMenuRecordings::DirectoryName(void) { cString d(cVideoDirectory::Name()); @@ -2328,11 +2627,11 @@ cString cMenuRecordings::DirectoryName(void) bool cMenuRecordings::Open(bool OpenSubMenus) { cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); - if (ri && ri->IsDirectory()) { + if (ri && ri->IsDirectory() && (!*path || strcountchr(path, FOLDERDELIMCHAR) > 0)) { const char *t = ri->Name(); cString buffer; if (base) { - buffer = cString::sprintf("%s~%s", base, t); + buffer = cString::sprintf("%s%c%s", base, FOLDERDELIMCHAR, t); t = buffer; } AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus)); @@ -2395,10 +2694,10 @@ eOSState cMenuRecordings::Delete(void) } cRecording *recording = ri->Recording(); cString FileName = recording->FileName(); - if (cCutter::Active(ri->Recording()->FileName())) { + if (RecordingsHandler.GetUsage(FileName)) { if (Interface->Confirm(tr("Recording is being edited - really delete?"))) { - cCutter::Stop(); - recording = Recordings.GetByName(FileName); // cCutter::Stop() might have deleted it if it was the edited version + RecordingsHandler.Del(FileName); + recording = Recordings.GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version // we continue with the code below even if recording is NULL, // in order to have the menu updated etc. } @@ -2428,9 +2727,12 @@ eOSState cMenuRecordings::Info(void) { if (HasSubMenu() || Count() == 0) return osContinue; - cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); - if (ri && !ri->IsDirectory() && ri->Recording()->Info()->Title()) - return AddSubMenu(new cMenuRecording(ri->Recording(), true)); + if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) { + if (ri->IsDirectory()) + return AddSubMenu(new cMenuPathEdit(cString(ri->Recording()->Name(), strchrn(ri->Recording()->Name(), FOLDERDELIMCHAR, ri->Level() + 1)))); + else + return AddSubMenu(new cMenuRecording(ri->Recording(), true)); + } return osContinue; } @@ -2481,6 +2783,17 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key) default: break; } } + else if (state == osUser1) { + // a recording or path was renamed, so let's refresh the menu + CloseSubMenu(false); + if (base) + return state; // closes all recording menus except for the top one + Set(); // this is the top level menu, so we refresh it... + Open(true); // ...and open any necessary submenus to show the new name + Display(); + path = NULL; + fileName = NULL; + } if (Key == kYellow && HadSubMenu && !HasSubMenu()) { // the last recording in a subdirectory was deleted, so let's go back up cOsdMenu::Del(Current()); @@ -3462,13 +3775,13 @@ bool cMenuMain::Update(bool Force) } // Editing control: - bool CutterActive = cCutter::Active(); - if (CutterActive && !cancelEditingItem) { + bool EditingActive = RecordingsHandler.Active(); + if (EditingActive && !cancelEditingItem) { // TRANSLATORS: note the leading blank! Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit)); result = true; } - else if (cancelEditingItem && !CutterActive) { + else if (cancelEditingItem && !EditingActive) { Del(cancelEditingItem->Index()); cancelEditingItem = NULL; result = true; @@ -3518,7 +3831,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) } break; case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) { - cCutter::Stop(); + RecordingsHandler.DelAll(); return osEnd; } break; @@ -4853,12 +5166,12 @@ void cReplayControl::EditCut(void) { if (*fileName) { Hide(); - if (!cCutter::Active()) { + if (!RecordingsHandler.GetUsage(fileName)) { if (!marks.Count()) Skins.Message(mtError, tr("No editing marks defined!")); else if (!marks.GetNumSequences()) Skins.Message(mtError, tr("No editing sequences defined!")); - else if (!cCutter::Start(fileName)) + else if (!RecordingsHandler.Add(ruCut, fileName)) Skins.Message(mtError, tr("Can't start editing process!")); else Skins.Message(mtInfo, tr("Editing process started")); diff --git a/menu.h b/menu.h index db17f983..03e91715 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 3.1 2013/06/01 13:44:57 kls Exp $ + * $Id: menu.h 3.2 2013/09/22 10:47:32 kls Exp $ */ #ifndef __MENU_H @@ -198,6 +198,8 @@ private: int level; int recordingsState; int helpKeys; + static cString path; + static cString fileName; void SetHelpKeys(void); void Set(bool Refresh = false); bool Open(bool OpenSubMenus = false); @@ -213,6 +215,8 @@ public: cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false); ~cMenuRecordings(); virtual eOSState ProcessKey(eKeys Key); + static void SetPath(const char *Path); + static void SetRecording(const char *FileName); }; class cRecordControl { diff --git a/osdbase.c b/osdbase.c index baadc0e6..b788edb0 100644 --- a/osdbase.c +++ b/osdbase.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.c 3.1 2013/05/24 10:19:31 kls Exp $ + * $Id: osdbase.c 3.2 2013/09/22 14:01:17 kls Exp $ */ #include "osdbase.h" @@ -502,12 +502,14 @@ eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) return osContinue; // convenience return value } -eOSState cOsdMenu::CloseSubMenu() +eOSState cOsdMenu::CloseSubMenu(bool ReDisplay) { delete subMenu; subMenu = NULL; - RefreshCurrent(); - Display(); + if (ReDisplay) { + RefreshCurrent(); + Display(); + } return osContinue; // convenience return value } diff --git a/osdbase.h b/osdbase.h index 46351841..07dce352 100644 --- a/osdbase.h +++ b/osdbase.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.h 2.5 2012/12/07 09:49:35 kls Exp $ + * $Id: osdbase.h 3.1 2013/09/22 14:00:47 kls Exp $ */ #ifndef __OSDBASE_H @@ -119,7 +119,7 @@ protected: void Mark(void); eOSState HotKey(eKeys Key); eOSState AddSubMenu(cOsdMenu *SubMenu); - eOSState CloseSubMenu(); + eOSState CloseSubMenu(bool ReDisplay = true); bool HasSubMenu(void) { return subMenu; } cOsdMenu *SubMenu(void) { return subMenu; } void SetStatus(const char *s); diff --git a/po/ar.po b/po/ar.po index 1676dcbe..224bbe98 100644 --- a/po/ar.po +++ b/po/ar.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-10-16 11:16-0400\n" "Last-Translator: Osama Alrawab \n" "Language-Team: Arabic \n" @@ -727,6 +727,67 @@ msgstr "الرجاء ادخال %d رقم!" msgid "CAM not responding!" msgstr "الكامة لا تستجيب" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "معلومات التسجبل" diff --git a/po/ca_ES.po b/po/ca_ES.po index 0362cd83..a1808cf8 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n" "Last-Translator: Luca Olivetti \n" "Language-Team: Catalan \n" @@ -726,6 +726,67 @@ msgstr "Si us plau introdueixi %d digitos" msgid "CAM not responding!" msgstr "CAM no respon" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Informaci de la gravaci" diff --git a/po/cs_CZ.po b/po/cs_CZ.po index 2bd4a8e4..899559aa 100644 --- a/po/cs_CZ.po +++ b/po/cs_CZ.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2010-05-06 11:00+0200\n" "Last-Translator: Aleš Juřík \n" "Language-Team: Czech \n" @@ -726,6 +726,67 @@ msgstr "Prosím vložte %d znaků!" msgid "CAM not responding!" msgstr "CAM neodpovídá!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Detail nahrávky" diff --git a/po/da_DK.po b/po/da_DK.po index edd0f5f4..a9bb5dea 100644 --- a/po/da_DK.po +++ b/po/da_DK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Mogens Elneff \n" "Language-Team: Danish \n" @@ -723,6 +723,67 @@ msgstr "Indtast venligst %d cifre!" msgid "CAM not responding!" msgstr "CAM svarer ikke!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Optagelses info" diff --git a/po/de_DE.po b/po/de_DE.po index 4cc8e844..54ec37e7 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2010-01-16 16:46+0100\n" "Last-Translator: Klaus Schmidinger \n" "Language-Team: German \n" @@ -723,6 +723,67 @@ msgstr "Bitte geben Sie %d Ziffern ein!" msgid "CAM not responding!" msgstr "CAM antwortet nicht!" +msgid "Edit path" +msgstr "Pfad editieren" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "Dieser Ordner ist zur Zeit in Verwendung - es sind keine nderungen mglich!" + +msgid "Folder" +msgstr "Ordner" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "Ganzen Ordner mit %d Aufnahmen verschieben?" + +msgid "Error while moving folder!" +msgstr "Fehler beim Verschieben des Ordners!" + +msgid "Edit recording" +msgstr "Aufnahme editieren" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "Diese Aufnahme ist zur Zeit in Verwendung - es sind keine nderungen mglich!" + +msgid "Button$Stop cutting" +msgstr "Schnitt beenden" + +msgid "Button$Cancel cutting" +msgstr "Schnitt abbrechen" + +msgid "Button$Stop moving" +msgstr "Verschieben beenden" + +msgid "Button$Cancel moving" +msgstr "Verschieben abbrechen" + +msgid "Button$Stop copying" +msgstr "Kopieren beenden" + +msgid "Button$Cancel copying" +msgstr "Kopieren abbrechen" + +msgid "Button$Cut" +msgstr "Schneiden" + +msgid "Button$Delete marks" +msgstr "Marken lschen" + +msgid "Error while queueing recording for cutting!" +msgstr "Fehler beim Hinzufgen der Aufnahme zur Schnittwarteschlange" + +msgid "Delete editing marks for this recording?" +msgstr "Schnittmarken fr diese Aufnahme lschen?" + +msgid "Error while deleting editing marks!" +msgstr "Fehler beim Lschen der Schnittmarken!" + +msgid "Error while changing priority/lifetime!" +msgstr "Fehler beim ndern der Prioritt bzw. Lebensdauer!" + +msgid "Error while changing folder/name!" +msgstr "Fehler beim ndern des Ordners bzw. Namens!" + msgid "Recording info" msgstr "Aufzeichnung" diff --git a/po/el_GR.po b/po/el_GR.po index 8df5b786..37486a2f 100644 --- a/po/el_GR.po +++ b/po/el_GR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Dimitrios Dimitrakos \n" "Language-Team: Greek \n" @@ -723,6 +723,67 @@ msgstr "" msgid "CAM not responding!" msgstr "" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr " E" diff --git a/po/es_ES.po b/po/es_ES.po index 023871ec..9b87401c 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n" "Last-Translator: Luca Olivetti \n" "Language-Team: Spanish \n" @@ -724,6 +724,67 @@ msgstr " msgid "CAM not responding!" msgstr "CAM no responde!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Informacin de grabacin" diff --git a/po/et_EE.po b/po/et_EE.po index 5ab2d101..94b65fc1 100644 --- a/po/et_EE.po +++ b/po/et_EE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Arthur Konovalov \n" "Language-Team: Estonian \n" @@ -723,6 +723,67 @@ msgstr "Palun sisestada %d numbrit!" msgid "CAM not responding!" msgstr "CAM ei vasta" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Salvestuse info" diff --git a/po/fi_FI.po b/po/fi_FI.po index d67f1f10..c159accb 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2007-08-15 15:52+0200\n" "Last-Translator: Matti Lehtimäki \n" "Language-Team: Finnish \n" @@ -727,6 +727,67 @@ msgstr "Syötä %d numeroa!" msgid "CAM not responding!" msgstr "CA-moduuli ei vastaa!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Tallenteen tiedot" diff --git a/po/fr_FR.po b/po/fr_FR.po index a4eda2fb..b7b4f40b 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-02-24 12:56+0100\n" "Last-Translator: Dominique Plu \n" "Language-Team: French \n" @@ -733,6 +733,67 @@ msgstr "Veuillez entrer %d chiffres !" msgid "CAM not responding!" msgstr "Pas de réponse du CAM" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Infos sur l'enregistrement" diff --git a/po/hr_HR.po b/po/hr_HR.po index baafe9d5..09298ee8 100644 --- a/po/hr_HR.po +++ b/po/hr_HR.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-03-17 19:00+0100\n" "Last-Translator: Adrian Caval \n" "Language-Team: Croatian \n" @@ -725,6 +725,67 @@ msgstr "Molim unesite %d znamenki!" msgid "CAM not responding!" msgstr "CAM ne odgovara!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Detalji snimanja" diff --git a/po/hu_HU.po b/po/hu_HU.po index b3c17b0c..c341ce0a 100644 --- a/po/hu_HU.po +++ b/po/hu_HU.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-01 19:22+0200\n" "Last-Translator: István Füley \n" "Language-Team: Hungarian \n" @@ -727,6 +727,67 @@ msgstr "Üssön be %d számot!" msgid "CAM not responding!" msgstr "A CAM nem válaszol!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Felvétel infó" diff --git a/po/it_IT.po b/po/it_IT.po index e579885f..0ff2a37d 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-02-11 23:46+0100\n" "Last-Translator: Diego Pierotto \n" "Language-Team: Italian \n" @@ -730,6 +730,67 @@ msgstr "Inserisci %d cifre!" msgid "CAM not responding!" msgstr "La CAM non risponde!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Info registrazione" diff --git a/po/lt_LT.po b/po/lt_LT.po index 34e273c7..a6fb50e1 100644 --- a/po/lt_LT.po +++ b/po/lt_LT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2010-10-30 11:55+0200\n" "Last-Translator: Valdemaras Pipiras \n" "Language-Team: Lithuanian \n" @@ -723,6 +723,67 @@ msgstr "Įveskite %d skaičius!" msgid "CAM not responding!" msgstr "Dekodavimo modulis (CAM) neveikia!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Informacija apie įrašus" diff --git a/po/mk_MK.po b/po/mk_MK.po index a398b3ca..d4600fbb 100644 --- a/po/mk_MK.po +++ b/po/mk_MK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2012-11-19 15:18+0100\n" "Last-Translator: Dimitar Petrovski \n" "Language-Team: Macedonian \n" @@ -724,6 +724,67 @@ msgstr "Внеси %d цифри!" msgid "CAM not responding!" msgstr "CAM не одговара!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Детали на снимката" diff --git a/po/nl_NL.po b/po/nl_NL.po index e9b9a001..56ff33b3 100644 --- a/po/nl_NL.po +++ b/po/nl_NL.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-02-26 17:20+0100\n" "Last-Translator: Cedric Dewijs \n" "Language-Team: Dutch \n" @@ -728,6 +728,67 @@ msgstr "Vul %d cijfers in!" msgid "CAM not responding!" msgstr "CAM reageert niet!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Opname info" diff --git a/po/nn_NO.po b/po/nn_NO.po index a05410fa..29f1486b 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Truls Slevigen \n" "Language-Team: Norwegian Nynorsk \n" @@ -724,6 +724,67 @@ msgstr "" msgid "CAM not responding!" msgstr "" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "" diff --git a/po/pl_PL.po b/po/pl_PL.po index f240894f..9ad56a3b 100644 --- a/po/pl_PL.po +++ b/po/pl_PL.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-03-09 12:59+0100\n" "Last-Translator: Marek Nazarko \n" "Language-Team: Polish \n" @@ -725,6 +725,67 @@ msgstr "Prosz msgid "CAM not responding!" msgstr "CAM nie reaguje!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Informacje o nagraniu" diff --git a/po/pt_PT.po b/po/pt_PT.po index 7dde8c56..62b8a9fa 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2010-03-28 22:49+0100\n" "Last-Translator: Cris Silva \n" "Language-Team: Portuguese \n" @@ -724,6 +724,67 @@ msgstr "Por favor introduza %d d msgid "CAM not responding!" msgstr "A CAM no responde!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Informao da gravao" diff --git a/po/ro_RO.po b/po/ro_RO.po index f49c4523..b4e5c284 100644 --- a/po/ro_RO.po +++ b/po/ro_RO.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-02-09 23:01+0100\n" "Last-Translator: Lucian Muresan \n" "Language-Team: Romanian \n" @@ -725,6 +725,67 @@ msgstr "Vă rog introduceţi %d cifre!" msgid "CAM not responding!" msgstr "CAM-ul nu reacţionează!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Detaliile înregistrării" diff --git a/po/ru_RU.po b/po/ru_RU.po index 78565601..42a7b007 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-10 17:13+0100\n" "Last-Translator: Oleg Roitburd \n" "Language-Team: Russian \n" @@ -724,6 +724,67 @@ msgstr " msgid "CAM not responding!" msgstr "CAM " +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr " " diff --git a/po/sk_SK.po b/po/sk_SK.po index 6e849b0b..208c2419 100644 --- a/po/sk_SK.po +++ b/po/sk_SK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-04 21:24+0100\n" "Last-Translator: Milan Hrala \n" "Language-Team: Slovak \n" @@ -723,6 +723,67 @@ msgstr "Pros msgid "CAM not responding!" msgstr "CAM neodpoved!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Podrobnosti nahrvky" diff --git a/po/sl_SI.po b/po/sl_SI.po index 6e9ca323..0d40510f 100644 --- a/po/sl_SI.po +++ b/po/sl_SI.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-04 12:46+0100\n" "Last-Translator: Matjaz Thaler \n" "Language-Team: Slovenian \n" @@ -724,6 +724,67 @@ msgstr "Prosim vnesite %d msgid "CAM not responding!" msgstr "CAM se ne odziva!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Podatki o snemanju" diff --git a/po/sr_RS.po b/po/sr_RS.po index 12586f77..36c9dbba 100644 --- a/po/sr_RS.po +++ b/po/sr_RS.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-16 15:05+0100\n" "Last-Translator: Zoran Turalija \n" "Language-Team: Serbian \n" @@ -724,6 +724,67 @@ msgstr "Molimo unesite %d brojeve!" msgid "CAM not responding!" msgstr "CAM ne reaguje!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Detalji snimanja" diff --git a/po/sv_SE.po b/po/sv_SE.po index 58492c49..cee61560 100644 --- a/po/sv_SE.po +++ b/po/sv_SE.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-02-18 17:04+0100\n" "Last-Translator: Richard Lithvall \n" "Language-Team: Swedish \n" @@ -727,6 +727,67 @@ msgstr "Mata in %d siffror!" msgid "CAM not responding!" msgstr "CAM svarar inte!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Inspelningsinformation" diff --git a/po/tr_TR.po b/po/tr_TR.po index d9201803..d672db68 100644 --- a/po/tr_TR.po +++ b/po/tr_TR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2008-02-28 00:33+0100\n" "Last-Translator: Oktay Yolgeen \n" "Language-Team: Turkish \n" @@ -723,6 +723,67 @@ msgstr "L msgid "CAM not responding!" msgstr "CAM yant vermiyor!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Kayt bilgisi" diff --git a/po/uk_UA.po b/po/uk_UA.po index 829921d4..80087568 100644 --- a/po/uk_UA.po +++ b/po/uk_UA.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-02-09 16:00+0100\n" "Last-Translator: Yarema aka Knedlyk \n" "Language-Team: Ukrainian \n" @@ -724,6 +724,67 @@ msgstr "Введіть %d цифри!" msgid "CAM not responding!" msgstr "CAM не відповідає!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "Про запис" diff --git a/po/zh_CN.po b/po/zh_CN.po index 8ba5e4dd..0d7c9755 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-10 12:16+0200\n" +"POT-Creation-Date: 2013-10-10 14:32+0200\n" "PO-Revision-Date: 2013-03-04 14:52+0800\n" "Last-Translator: NFVDR \n" "Language-Team: Chinese (simplified) \n" @@ -725,6 +725,67 @@ msgstr "请输入 %d 数字!" msgid "CAM not responding!" msgstr "CAM 没有响应!" +msgid "Edit path" +msgstr "" + +msgid "This folder is currently in use - no changes are possible!" +msgstr "" + +msgid "Folder" +msgstr "" + +#, c-format +msgid "Move entire folder containing %d recordings?" +msgstr "" + +msgid "Error while moving folder!" +msgstr "" + +msgid "Edit recording" +msgstr "" + +msgid "This recording is currently in use - no changes are possible!" +msgstr "" + +msgid "Button$Stop cutting" +msgstr "" + +msgid "Button$Cancel cutting" +msgstr "" + +msgid "Button$Stop moving" +msgstr "" + +msgid "Button$Cancel moving" +msgstr "" + +msgid "Button$Stop copying" +msgstr "" + +msgid "Button$Cancel copying" +msgstr "" + +msgid "Button$Cut" +msgstr "" + +msgid "Button$Delete marks" +msgstr "" + +msgid "Error while queueing recording for cutting!" +msgstr "" + +msgid "Delete editing marks for this recording?" +msgstr "" + +msgid "Error while deleting editing marks!" +msgstr "" + +msgid "Error while changing priority/lifetime!" +msgstr "" + +msgid "Error while changing folder/name!" +msgstr "" + msgid "Recording info" msgstr "录像信息" diff --git a/recording.c b/recording.c index 5cbf1070..31eb25b5 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.3 2013/09/11 08:28:27 kls Exp $ + * $Id: recording.c 3.4 2013/10/09 11:53:37 kls Exp $ */ #include "recording.h" @@ -20,8 +20,10 @@ #include #include #include "channels.h" +#include "cutter.h" #include "i18n.h" #include "interface.h" +#include "menu.h" #include "remux.h" #include "ringbuffer.h" #include "skins.h" @@ -422,6 +424,13 @@ void cRecordingInfo::SetFramesPerSecond(double FramesPerSecond) framesPerSecond = FramesPerSecond; } +void cRecordingInfo::SetFileName(const char *FileName) +{ + bool IsPesRecording = fileName && endswith(fileName, ".vdr"); + free(fileName); + fileName = strdup(cString::sprintf("%s%s", FileName, IsPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX)); +} + bool cRecordingInfo::Read(FILE *f) { if (ownEvent) { @@ -769,7 +778,7 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) else if (Timer->IsSingleEvent() || !Setup.UseSubtitle) name = strdup(Timer->File()); else - name = strdup(cString::sprintf("%s~%s", Timer->File(), Subtitle)); + name = strdup(cString::sprintf("%s%c%s", Timer->File(), FOLDERDELIMCHAR, Subtitle)); // substitute characters that would cause problems in file names: strreplace(name, '\n', ' '); start = Timer->StartTime(); @@ -963,8 +972,9 @@ char *cRecording::SortName(void) const void cRecording::ClearSortName(void) { - DELETENULL(sortBufferName); - DELETENULL(sortBufferTime); + free(sortBufferName); + free(sortBufferTime); + sortBufferName = sortBufferTime = NULL; } int cRecording::GetResume(void) const @@ -982,6 +992,28 @@ int cRecording::Compare(const cListObject &ListObject) const return strcasecmp(SortName(), r->SortName()); } +bool cRecording::IsInPath(const char *Path) +{ + if (isempty(Path)) + return true; + int l = strlen(Path); + return strncmp(Path, name, l) == 0 && (name[l] == FOLDERDELIMCHAR); +} + +cString cRecording::Folder(void) const +{ + if (char *s = strrchr(name, FOLDERDELIMCHAR)) + return cString(name, s); + return ""; +} + +cString cRecording::BaseName(void) const +{ + if (char *s = strrchr(name, FOLDERDELIMCHAR)) + return cString(s + 1); + return name; +} + const char *cRecording::FileName(void) const { if (!fileName) { @@ -1097,6 +1129,22 @@ bool cRecording::IsOnVideoDirectoryFileSystem(void) const return isOnVideoDirectoryFileSystem; } +bool cRecording::HasMarks(void) +{ + return access(cMarks::MarksFileName(this), F_OK) == 0; +} + +bool cRecording::DeleteMarks(void) +{ + if (remove(cMarks::MarksFileName(this)) < 0) { + if (errno != ENOENT) { + LOG_ERROR_STR(fileName); + return false; + } + } + return true; +} + void cRecording::ReadInfo(void) { info->Read(); @@ -1105,13 +1153,13 @@ void cRecording::ReadInfo(void) framesPerSecond = info->framesPerSecond; } -bool cRecording::WriteInfo(void) +bool cRecording::WriteInfo(const char *OtherFileName) { - cString InfoFileName = cString::sprintf("%s%s", fileName, isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX); - FILE *f = fopen(InfoFileName, "w"); - if (f) { + cString InfoFileName = cString::sprintf("%s%s", OtherFileName ? OtherFileName : FileName(), isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX); + cSafeFile f(InfoFileName); + if (f.Open()) { info->Write(f); - fclose(f); + f.Close(); } else LOG_ERROR_STR(*InfoFileName); @@ -1125,6 +1173,58 @@ void cRecording::SetStartTime(time_t Start) fileName = NULL; } +bool cRecording::ChangePriorityLifetime(int NewPriority, int NewLifetime) +{ + if (NewPriority != Priority() || NewLifetime != Lifetime()) { + dsyslog("changing priority/lifetime of '%s' to %d/%d", Name(), NewPriority, NewLifetime); + if (IsPesRecording()) { + cString OldFileName = FileName(); + priority = NewPriority; + lifetime = NewLifetime; + free(fileName); + fileName = NULL; + cString NewFileName = FileName(); + if (!cVideoDirectory::RenameVideoFile(OldFileName, NewFileName)) + return false; + info->SetFileName(NewFileName); + } + else { + priority = info->priority = NewPriority; + lifetime = info->lifetime = NewLifetime; + if (!WriteInfo()) + return false; + } + Recordings.ChangeState(); + Recordings.TouchUpdate(); + } + return true; +} + +bool cRecording::ChangeName(const char *NewName) +{ + if (strcmp(NewName, Name())) { + dsyslog("changing name of '%s' to '%s'", Name(), NewName); + cString OldName = Name(); + cString OldFileName = FileName(); + free(fileName); + fileName = NULL; + free(name); + name = strdup(NewName); + cString NewFileName = FileName(); + if (!(MakeDirs(NewFileName, true) && cVideoDirectory::MoveVideoFile(OldFileName, NewFileName))) { + free(name); + name = strdup(OldName); + free(fileName); + fileName = strdup(OldFileName); + return false; + } + ClearSortName(); + Recordings.ChangeState(); + Recordings.TouchUpdate(); + } + return true; +} + bool cRecording::Delete(void) { bool result = true; @@ -1188,6 +1288,17 @@ bool cRecording::Undelete(void) return result; } +int cRecording::IsInUse(void) const +{ + int Use = ruNone; + if (cRecordControls::GetRecordControl(FileName())) + Use |= ruTimer; + if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName()) == 0) + Use |= ruReplay; + Use |= RecordingsHandler.GetUsage(FileName()); + return Use; +} + void cRecording::ResetResume(void) const { resume = RESUME_NOT_INITIALIZED; @@ -1426,6 +1537,46 @@ double cRecordings::MBperMinute(void) return (size && length) ? double(size) * 60 / length : -1; } +int cRecordings::PathIsInUse(const char *Path) +{ + LOCK_THREAD; + int Use = ruNone; + for (cRecording *recording = First(); recording; recording = Next(recording)) { + if (recording->IsInPath(Path)) + Use |= recording->IsInUse(); + } + return Use; +} + +int cRecordings::GetNumRecordingsInPath(const char *Path) +{ + LOCK_THREAD; + int n = 0; + for (cRecording *recording = First(); recording; recording = Next(recording)) { + if (recording->IsInPath(Path)) + n++; + } + return n; +} + +bool cRecordings::MoveRecordings(const char *OldPath, const char *NewPath) +{ + if (OldPath && NewPath && strcmp(OldPath, NewPath)) { + LOCK_THREAD; + dsyslog("moving '%s' to '%s'", OldPath, NewPath); + for (cRecording *recording = First(); recording; recording = Next(recording)) { + if (recording->IsInPath(OldPath)) { + const char *p = recording->Name() + strlen(OldPath); + cString NewName = cString::sprintf("%s%s", NewPath, p); + if (!recording->ChangeName(NewName)) + return false; + ChangeState(); + } + } + } + return true; +} + void cRecordings::ResetResume(const char *ResumeFileName) { LOCK_THREAD; @@ -1443,6 +1594,356 @@ void cRecordings::ClearSortNames(void) recording->ClearSortName(); } +// --- cDirCopier ------------------------------------------------------------ + +class cDirCopier : public cThread { +private: + cString dirNameSrc; + cString dirNameDst; + bool error; + bool suspensionLogged; + bool Throttled(void); + virtual void Action(void); +public: + cDirCopier(const char *DirNameSrc, const char *DirNameDst); + virtual ~cDirCopier(); + void Stop(void); + bool Error(void) { return error; } + }; + +cDirCopier::cDirCopier(const char *DirNameSrc, const char *DirNameDst) +:cThread("file copier", true) +{ + dirNameSrc = DirNameSrc; + dirNameDst = DirNameDst; + error = false; + suspensionLogged = false; +} + +cDirCopier::~cDirCopier() +{ + Stop(); +} + +bool cDirCopier::Throttled(void) +{ + if (cIoThrottle::Engaged()) { + if (!suspensionLogged) { + dsyslog("suspending copy thread"); + suspensionLogged = true; + } + return true; + } + else if (suspensionLogged) { + dsyslog("resuming copy thread"); + suspensionLogged = false; + } + return false; +} + +void cDirCopier::Action(void) +{ + if (DirectoryOk(dirNameDst, true)) { + cReadDir d(dirNameSrc); + if (d.Ok()) { + dsyslog("copying directory '%s' to '%s'", *dirNameSrc, *dirNameDst); + dirent *e = NULL; + cString FileNameSrc; + cString FileNameDst; + int From = -1; + int To = -1; + size_t BufferSize = BUFSIZ; + while (Running()) { + // Suspend cutting if we have severe throughput problems: + if (Throttled()) { + cCondWait::SleepMs(100); + continue; + } + // Copy all files in the source directory to the destination directory: + if (e) { + // We're currently copying a file: + uchar Buffer[BufferSize]; + size_t Read = safe_read(From, Buffer, sizeof(Buffer)); + if (Read > 0) { + size_t Written = safe_write(To, Buffer, Read); + if (Written != Read) { + esyslog("ERROR: can't write to destination file '%s': %m", *FileNameDst); + break; + } + } + else if (Read == 0) { // EOF on From + e = NULL; // triggers switch to next entry + if (fsync(To) < 0) { + esyslog("ERROR: can't sync destination file '%s': %m", *FileNameDst); + break; + } + if (close(From) < 0) { + esyslog("ERROR: can't close source file '%s': %m", *FileNameSrc); + break; + } + if (close(To) < 0) { + esyslog("ERROR: can't close destination file '%s': %m", *FileNameDst); + break; + } + // Plausibility check: + off_t FileSizeSrc = FileSize(FileNameSrc); + off_t FileSizeDst = FileSize(FileNameDst); + if (FileSizeSrc != FileSizeDst) { + esyslog("ERROR: file size discrepancy: %lld != %lld", FileSizeSrc, FileSizeDst); + break; + } + } + else { + esyslog("ERROR: can't read from source file '%s': %m", *FileNameSrc); + break; + } + } + else if ((e = d.Next()) != NULL) { + // We're switching to the next directory entry: + FileNameSrc = AddDirectory(dirNameSrc, e->d_name); + FileNameDst = AddDirectory(dirNameDst, e->d_name); + struct stat st; + if (stat(FileNameSrc, &st) < 0) { + esyslog("ERROR: can't access source file '%s': %m", *FileNameSrc); + break; + } + if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) { + esyslog("ERROR: source file '%s' is neither a regular file nor a symbolic link", *FileNameSrc); + break; + } + dsyslog("copying file '%s' to '%s'", *FileNameSrc, *FileNameDst); + BufferSize = max(size_t(st.st_blksize * 10), size_t(BUFSIZ)); + if (access(FileNameDst, F_OK) == 0) { + esyslog("ERROR: destination file '%s' already exists", *FileNameDst); + break; + } + if ((From = open(FileNameSrc, O_RDONLY)) < 0) { + esyslog("ERROR: can't open source file '%s': %m", *FileNameSrc); + break; + } + if ((To = open(FileNameDst, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE)) < 0) { + esyslog("ERROR: can't open destination file '%s': %m", *FileNameDst); + close(From); + break; + } + } + else { + // We're done: + dsyslog("done copying directory '%s' to '%s'", *dirNameSrc, *dirNameDst); + return; + } + } + close(From); // just to be absolutely sure + close(To); + esyslog("ERROR: copying directory '%s' to '%s' ended prematurely", *dirNameSrc, *dirNameDst); + } + else + esyslog("ERROR: can't open '%s'", *dirNameSrc); + } + else + esyslog("ERROR: can't access '%s'", *dirNameDst); + error = true; +} + +void cDirCopier::Stop(void) +{ + Cancel(3); + if (error) { + cVideoDirectory::RemoveVideoFile(dirNameDst); + Recordings.DelByName(dirNameDst); + } +} + +// --- cRecordingsHandlerEntry ----------------------------------------------- + +class cRecordingsHandlerEntry : public cListObject { +private: + int usage; + cString fileNameSrc; + cString fileNameDst; + cCutter *cutter; + cDirCopier *copier; + void ClearPending(void) { usage &= ~ruPending; } +public: + cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst); + ~cRecordingsHandlerEntry(); + int Usage(const char *FileName = NULL) const; + const char *FileNameSrc(void) const { return fileNameSrc; } + const char *FileNameDst(void) const { return fileNameDst; } + bool Active(bool &Error); + }; + +cRecordingsHandlerEntry::cRecordingsHandlerEntry(int Usage, const char *FileNameSrc, const char *FileNameDst) +{ + usage = Usage; + fileNameSrc = FileNameSrc; + fileNameDst = FileNameDst; + cutter = NULL; + copier = NULL; +} + +cRecordingsHandlerEntry::~cRecordingsHandlerEntry() +{ + delete cutter; + delete copier; +} + +int cRecordingsHandlerEntry::Usage(const char *FileName) const +{ + int u = usage; + if (FileName && *FileName) { + if (strcmp(FileName, fileNameSrc) == 0) + u |= ruSrc; + else if (strcmp(FileName, fileNameDst) == 0) + u |= ruDst; + } + return u; +} + +bool cRecordingsHandlerEntry::Active(bool &Error) +{ + bool CopierFinishedOk = false; + // First test whether there is an ongoing operation: + if (cutter) { + if (cutter->Active()) + return true; + Error |= cutter->Error(); + delete cutter; + cutter = NULL; + } + else if (copier) { + if (copier->Active()) + return true; + Error |= copier->Error(); + CopierFinishedOk = !copier->Error(); + delete copier; + copier = NULL; + } + // Now check if there is something to start: + if ((Usage() & ruPending) != 0) { + if ((Usage() & ruCut) != 0) { + cutter = new cCutter(FileNameSrc()); + cutter->Start(); + } + else if ((Usage() & (ruMove | ruCopy)) != 0) { + copier = new cDirCopier(FileNameSrc(), FileNameDst()); + copier->Start(); + } + ClearPending(); + return true; + } + else { + if (CopierFinishedOk && (Usage() & ruMove) != 0) { + cRecording Recording(FileNameSrc()); + Recording.Delete(); + Recordings.ChangeState(); + Recordings.TouchUpdate(); + } + } + return false; +} + +// --- cRecordingsHandler ---------------------------------------------------- + +cRecordingsHandler RecordingsHandler; + +cRecordingsHandler::cRecordingsHandler(void) +{ + finished = true; + error = false; +} + +cRecordingsHandler::~cRecordingsHandler() +{ +} + +cRecordingsHandlerEntry *cRecordingsHandler::Get(const char *FileName) +{ + if (FileName && *FileName) { + for (cRecordingsHandlerEntry *r = operations.First(); r; r = operations.Next(r)) { + if (strcmp(FileName, r->FileNameSrc()) == 0 || strcmp(FileName, r->FileNameDst()) == 0) + return r; + } + } + return NULL; +} + +bool cRecordingsHandler::Add(int Usage, const char *FileNameSrc, const char *FileNameDst) +{ + dsyslog("recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst); + cMutexLock MutexLock(&mutex); + if (Usage == ruCut || Usage == ruMove || Usage == ruCopy) { + if (FileNameSrc && *FileNameSrc) { + if (Usage == ruCut || FileNameDst && *FileNameDst) { + cString fnd; + if (Usage == ruCut && !FileNameDst) + FileNameDst = fnd = cCutter::EditedFileName(FileNameSrc); + if (!Get(FileNameSrc) && !Get(FileNameDst)) { + Usage |= ruPending; + operations.Add(new cRecordingsHandlerEntry(Usage, FileNameSrc, FileNameDst)); + finished = false; + Active(); // start it right away if possible + return true; + } + else + esyslog("ERROR: file name already present in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst); + } + else + esyslog("ERROR: missing dst file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst); + } + else + esyslog("ERROR: missing src file name in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst); + } + else + esyslog("ERROR: invalid usage in recordings handler add %d '%s' '%s'", Usage, FileNameSrc, FileNameDst); + return false; +} + +void cRecordingsHandler::Del(const char *FileName) +{ + cMutexLock MutexLock(&mutex); + if (cRecordingsHandlerEntry *r = Get(FileName)) + operations.Del(r); +} + +void cRecordingsHandler::DelAll(void) +{ + cMutexLock MutexLock(&mutex); + operations.Clear(); +} + +int cRecordingsHandler::GetUsage(const char *FileName) +{ + cMutexLock MutexLock(&mutex); + if (cRecordingsHandlerEntry *r = Get(FileName)) + return r->Usage(FileName); + return ruNone; +} + +bool cRecordingsHandler::Active(void) +{ + cMutexLock MutexLock(&mutex); + while (cRecordingsHandlerEntry *r = operations.First()) { + if (r->Active(error)) + return true; + else + operations.Del(r); + } + return false; +} + +bool cRecordingsHandler::Finished(bool &Error) +{ + cMutexLock MutexLock(&mutex); + if (!finished && operations.Count() == 0) { + finished = true; + Error = error; + error = false; + return true; + } + return false; +} + // --- cMark ----------------------------------------------------------------- double MarkFramesPerSecond = DEFAULTFRAMESPERSECOND; @@ -1485,6 +1986,11 @@ bool cMark::Save(FILE *f) // --- cMarks ---------------------------------------------------------------- +cString cMarks::MarksFileName(const cRecording *Recording) +{ + return AddDirectory(Recording->FileName(), Recording->IsPesRecording() ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX); +} + bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording) { recordingFileName = RecordingFileName; diff --git a/recording.h b/recording.h index ff3119da..3b00c71b 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.h 2.46 2013/03/04 14:01:23 kls Exp $ + * $Id: recording.h 3.1 2013/10/10 12:08:15 kls Exp $ */ #ifndef __RECORDING_H @@ -25,6 +25,21 @@ extern int DirectoryNameMax; extern bool DirectoryEncoding; extern int InstanceId; +enum eRecordingUsage { + ruNone = 0x0000, // the recording is currently unused + ruTimer = 0x0001, // the recording is currently written to by a timer + ruReplay = 0x0002, // the recording is being replayed + // mutually exclusive: + ruCut = 0x0004, // the recording is being cut + ruMove = 0x0008, // the recording is being moved + ruCopy = 0x0010, // the recording is being copied + // mutually exclusive: + ruSrc = 0x0020, // the recording is the source of a cut, move or copy process + ruDst = 0x0040, // the recording is the destination of a cut, move or copy process + // + ruPending = 0x0080, // the recording is pending a cut, move or copy process + }; + void RemoveDeletedRecordings(void); void AssertFreeDiskSpace(int Priority = 0, bool Force = false); ///< The special Priority value -1 means that we shall get rid of any @@ -73,6 +88,7 @@ public: const char *Aux(void) const { return aux; } double FramesPerSecond(void) const { return framesPerSecond; } void SetFramesPerSecond(double FramesPerSecond); + void SetFileName(const char *FileName); bool Write(FILE *f, const char *Prefix = "") const; bool Read(void); bool Write(void) const; @@ -114,8 +130,21 @@ public: int Lifetime(void) const { return lifetime; } time_t Deleted(void) const { return deleted; } virtual int Compare(const cListObject &ListObject) const; + bool IsInPath(const char *Path); + ///< Returns true if this recording is stored anywhere under the given Path. + ///< If Path is NULL or an empty string, the entire video directory is checked. + cString Folder(void) const; + ///< Returns the name of the folder this recording is stored in (without the + ///< video directory). For use in menus etc. + cString BaseName(void) const; + ///< Returns the base name of this recording (without the + ///< video directory and folder). For use in menus etc. const char *Name(void) const { return name; } + ///< Returns the full name of the recording (without the video directory. + ///< For use in menus etc. const char *FileName(void) const; + ///< Returns the full path name to the recording directory, including the + ///< video directory and the actual '*.rec'. For disk file access use. const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const; const cRecordingInfo *Info(void) const { return info; } const char *PrefixFileName(char Prefix); @@ -134,8 +163,17 @@ public: bool IsEdited(void) const; bool IsPesRecording(void) const { return isPesRecording; } bool IsOnVideoDirectoryFileSystem(void) const; + bool HasMarks(void); + ///< Returns true if this recording has any editing marks. + bool DeleteMarks(void); + ///< Deletes the editing marks from this recording (if any). + ///< Returns true if the operation was successful. If there is no marks file + ///< for this recording, it also returns true. void ReadInfo(void); - bool WriteInfo(void); + bool WriteInfo(const char *OtherFileName = NULL); + ///< Writes in info file of this recording. If OtherFileName is given, the info + ///< file will be written under that recording file name instead of this + ///< recording's file name. void SetStartTime(time_t Start); ///< Sets the start time of this recording to the given value. ///< If a filename has already been set for this recording, it will be @@ -144,6 +182,17 @@ public: ///< Use this function with care - it does not check whether a recording with ///< this new name already exists, and if there is one, results may be ///< unexpected! + bool ChangePriorityLifetime(int NewPriority, int NewLifetime); + ///< Changes the priority and lifetime of this recording to the given values. + ///< If the new values are the same as the old ones, nothing happens. + ///< Returns false in case of error. + bool ChangeName(const char *NewName); + ///< Changes the name of this recording to the given value. NewName is in the + ///< same format as the one returned by Name(), i.e. without the video directory + ///< and the actual '*.rec' part, and using FOLDERDELIMCHAR as the directory + ///< delimiter. + ///< If the new name is the same as the old one, nothing happens. + ///< Returns false in case of error. bool Delete(void); ///< Changes the file name so that it will no longer be visible in the "Recordings" menu ///< Returns false in case of error @@ -154,6 +203,14 @@ public: ///< Changes the file name so that it will be visible in the "Recordings" menu again and ///< not processed by cRemoveDeletedRecordingsThread. ///< Returns false in case of error + int IsInUse(void) const; + ///< Checks whether this recording is currently in use and therefore shall not + ///< be tampered with. Returns 0 (ruNone) if the recording is not in use. + ///< The return value may consist of several or'd eRecordingUsage flags. If the + ///< caller is just interested in whether the recording is in use or not, the + ///< return value can be used like a boolean value. + ///< A recording may be in use for several reasons (like being recorded and replayed, + ///< as in time-shift). }; class cRecordings : public cList, public cThread { @@ -197,11 +254,76 @@ public: double MBperMinute(void); ///< Returns the average data rate (in MB/min) of all recordings, or -1 if ///< this value is unknown. + int PathIsInUse(const char *Path); + ///< Checks whether any recording in the given Path is currently in use and therefore + ///< the whole Path shall not be tampered with. Returns 0 (ruNone) if no recording + ///< is in use. + ///< See cRecording::IsInUse() for details about the possible non-zero return values. + ///< If several recordings in the Path are currently in use, the return value will + ///< be the combination of all individual recordings' flags. + ///< If Path is NULL or an empty string, the entire video directory is checked. + int GetNumRecordingsInPath(const char *Path); + ///< Returns the total number of recordings in the given Path, including all + ///< sub-folders of Path. + ///< If Path is NULL or an empty string, the entire video directory is checked. + bool MoveRecordings(const char *OldPath, const char *NewPath); + ///< Moves all recordings in OldPath to NewPath. + ///< Returns true if all recordings were successfully moved. + ///< As soon as the operation fails for one recording, the whole + ///< action is aborted and false will be returned. Any recordings that + ///< have been successfully moved thus far will keep their new name. + ///< If OldPath and NewPath are on different file systems, the recordings + ///< will be moved in a background process and this function returns true + ///< if all recordings have been successfully added to the RecordingsHandler. }; extern cRecordings Recordings; extern cRecordings DeletedRecordings; +class cRecordingsHandlerEntry; + +class cRecordingsHandler { +private: + cMutex mutex; + cList operations; + bool finished; + bool error; + cRecordingsHandlerEntry *Get(const char *FileName); +public: + cRecordingsHandler(void); + ~cRecordingsHandler(); + bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst = NULL); + ///< Adds the given FileNameSrc to the recordings handler for (later) + ///< processing. Usage can be either ruCut, ruMove or ruCopy. FileNameDst + ///< is only applicable for ruMove and ruCopy. + ///< At any given time there can be only one operation for any FileNameSrc + ///< or FileNameDst in the list. An attempt to add a file name twice will + ///< result in an error. + ///< Returns true if the operation was successfully added to the list. + void Del(const char *FileName); + ///< Deletes the given FileName from the list of operations. + ///< If an action is already in progress, it will be terminated. + ///< FileName can be either the FileNameSrc or FileNameDst (if applicable) + ///< that was given when the operation was added with Add(). + void DelAll(void); + ///< Deletes/terminates all operations. + int GetUsage(const char *FileName); + ///< Returns the usage type for the given FileName. + bool Active(void); + ///< Checks whether there is currently any operation running and starts + ///> the next one form the list if the previous one has finished. + ///< This function must be called regularly to trigger switching to the + ///< next operation in the list. + ///< Returns true if there are any operations in the list. + bool Finished(bool &Error); + ///< Returns true if all operations in the list have been finished. + ///< If there have been any errors, Errors will be set to true. + ///< This function will only return true once if the list of operations + ///< has actually become empty since the last call. + }; + +extern cRecordingsHandler RecordingsHandler; + #define DEFAULTFRAMESPERSECOND 25.0 class cMark : public cListObject { @@ -232,6 +354,9 @@ private: time_t lastFileTime; time_t lastChange; public: + static cString MarksFileName(const cRecording *Recording); + ///< Returns the marks file name for the given Recording (regardless whether such + ///< a file actually exists). bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false); bool Update(void); bool Save(void); diff --git a/shutdown.c b/shutdown.c index 445ed6f2..97d056e9 100644 --- a/shutdown.c +++ b/shutdown.c @@ -6,7 +6,7 @@ * * Original version written by Udo Richter . * - * $Id: shutdown.c 2.1 2013/02/18 10:33:26 kls Exp $ + * $Id: shutdown.c 3.1 2013/10/02 09:02:01 kls Exp $ */ #include "shutdown.h" @@ -16,11 +16,11 @@ #include #include "channels.h" #include "config.h" -#include "cutter.h" #include "i18n.h" #include "interface.h" #include "menu.h" #include "plugin.h" +#include "recording.h" #include "timers.h" #include "tools.h" @@ -167,7 +167,7 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive) Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!")); return false; } - if (cCutter::Active()) { + if (RecordingsHandler.Active()) { if (!Interactive || !Interface->Confirm(tr("Editing - shut down anyway?"))) return false; } @@ -210,7 +210,7 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive) bool cShutdownHandler::ConfirmRestart(bool Interactive) { - if (cCutter::Active()) { + if (RecordingsHandler.Active()) { if (!Interactive || !Interface->Confirm(tr("Editing - restart anyway?"))) return false; } diff --git a/svdrp.c b/svdrp.c index ef1296d9..1d2593a2 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 3.1 2013/09/10 13:21:38 kls Exp $ + * $Id: svdrp.c 3.2 2013/10/10 12:18:12 kls Exp $ */ #include "svdrp.h" @@ -28,7 +28,6 @@ #include #include "channels.h" #include "config.h" -#include "cutter.h" #include "device.h" #include "eitscan.h" #include "keys.h" @@ -305,6 +304,11 @@ const char *HelpPages[] = { "REMO [ on | off ]\n" " Turns the remote control on or off. Without a parameter, the current\n" " status of the remote control is reported.", + "RENR \n" + " Rename the recording with the given number. Before a recording can be\n" + " renamed, an LSTR command must have been executed in order to retrieve\n" + " the recording numbers. The numbers don't change during subsequent RENR\n" + " commands.n", "SCAN\n" " Forces an EPG scan. If this is a single DVB device system, the scan\n" " will be done on the primary device unless it is currently recording.", @@ -659,27 +663,38 @@ void cSVDRP::CmdDELC(const char *Option) Reply(501, "Missing channel number"); } +static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecording *Recording) +{ + cRecordControl *rc; + if ((Reason & ruTimer) != 0 && (rc = cRecordControls::GetRecordControl(Recording->FileName())) != NULL) + return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Index() + 1); + else if ((Reason & ruReplay) != 0) + return cString::sprintf("Recording \"%s\" is being replayed", RecordingId); + else if ((Reason & ruCut) != 0) + return cString::sprintf("Recording \"%s\" is being edited", RecordingId); + else if ((Reason & (ruMove | ruCopy)) != 0) + return cString::sprintf("Recording \"%s\" is being copied/moved", RecordingId); + else if (Reason) + return cString::sprintf("Recording \"%s\" is in use", RecordingId); + return NULL; +} + void cSVDRP::CmdDELR(const char *Option) { if (*Option) { if (isnumber(Option)) { cRecording *recording = recordings.Get(strtol(Option, NULL, 10) - 1); if (recording) { - cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); - if (!rc) { - if (!cCutter::Active(recording->FileName())) { - if (recording->Delete()) { - Reply(250, "Recording \"%s\" deleted", Option); - Recordings.DelByName(recording->FileName()); - } - else - Reply(554, "Error while deleting recording!"); + if (int RecordingInUse = recording->IsInUse()) + Reply(550, RecordingInUseMessage(RecordingInUse, Option, recording)); + else { + if (recording->Delete()) { + Reply(250, "Recording \"%s\" deleted", Option); + Recordings.DelByName(recording->FileName()); } else - Reply(550, "Recording \"%s\" is being edited", Option); + Reply(554, "Error while deleting recording!"); } - else - Reply(550, "Recording \"%s\" is in use by timer %d", Option, rc->Timer()->Index() + 1); } else Reply(550, "Recording \"%s\" not found%s", Option, recordings.Count() ? "" : " (use LSTR before deleting)"); @@ -728,14 +743,10 @@ void cSVDRP::CmdEDIT(const char *Option) if (recording) { cMarks Marks; if (Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && Marks.Count()) { - if (!cCutter::Active()) { - if (cCutter::Start(recording->FileName())) - Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title()); - else - Reply(554, "Can't start editing process"); - } + if (RecordingsHandler.Add(ruCut, recording->FileName())) + Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title()); else - Reply(554, "Editing process already active"); + Reply(554, "Can't start editing process"); } else Reply(554, "No editing marks defined"); @@ -1539,6 +1550,46 @@ void cSVDRP::CmdREMO(const char *Option) Reply(250, "Remote control is %s", cRemote::Enabled() ? "enabled" : "disabled"); } +void cSVDRP::CmdRENR(const char *Option) +{ + if (*Option) { + char *opt = strdup(Option); + char *num = skipspace(opt); + char *option = num; + while (*option && !isspace(*option)) + option++; + char c = *option; + *option = 0; + if (isnumber(num)) { + cRecording *recording = recordings.Get(strtol(num, NULL, 10) - 1); + if (recording) { + if (int RecordingInUse = recording->IsInUse()) + Reply(550, RecordingInUseMessage(RecordingInUse, Option, recording)); + else { + if (c) + option = skipspace(++option); + if (*option) { + cString oldName = recording->Name(); + if (recording->ChangeName(option)) + Reply(250, "Recording \"%s\" renamed to \"%s\"", *oldName, recording->Name()); + else + Reply(554, "Error while renaming recording \"%s\" to \"%s\"!", *oldName, option); + } + else + Reply(501, "Missing new recording name"); + } + } + else + Reply(550, "Recording \"%s\" not found%s", num, recordings.Count() ? "" : " (use LSTR before renaming)"); + } + else + Reply(501, "Error in recording number \"%s\"", num); + free(opt); + } + else + Reply(501, "Missing recording number"); +} + void cSVDRP::CmdSCAN(const char *Option) { EITScanner.ForceScan(); @@ -1666,6 +1717,7 @@ void cSVDRP::Execute(char *Cmd) else if (CMD("PLUG")) CmdPLUG(s); else if (CMD("PUTE")) CmdPUTE(s); else if (CMD("REMO")) CmdREMO(s); + else if (CMD("RENR")) CmdRENR(s); else if (CMD("SCAN")) CmdSCAN(s); else if (CMD("STAT")) CmdSTAT(s); else if (CMD("UPDR")) CmdUPDR(s); diff --git a/svdrp.h b/svdrp.h index 5ec9bc76..96247e52 100644 --- a/svdrp.h +++ b/svdrp.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: svdrp.h 2.3 2012/04/26 10:30:06 kls Exp $ + * $Id: svdrp.h 3.1 2013/09/14 13:24:50 kls Exp $ */ #ifndef __SVDRP_H @@ -78,6 +78,7 @@ private: void CmdPLUG(const char *Option); void CmdPUTE(const char *Option); void CmdREMO(const char *Option); + void CmdRENR(const char *Option); void CmdSCAN(const char *Option); void CmdSTAT(const char *Option); void CmdUPDT(const char *Option); diff --git a/tools.c b/tools.c index f690fb8a..a2055ec8 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 3.1 2013/05/23 10:10:00 kls Exp $ + * $Id: tools.c 3.2 2013/09/22 13:19:19 kls Exp $ */ #include "tools.h" @@ -173,6 +173,31 @@ char *strreplace(char *s, const char *s1, const char *s2) return s; } +const char *strchrn(const char *s, char c, size_t n) +{ + if (n == 0) + return s; + if (s) { + for ( ; *s; s++) { + if (*s == c && --n == 0) + return s; + } + } + return NULL; +} + +int strcountchr(const char *s, char c) +{ + int n = 0; + if (s && c) { + for ( ; *s; s++) { + if (*s == c) + n++; + } + } + return n; +} + char *stripspace(char *s) { if (s && *s) { @@ -202,6 +227,30 @@ char *compactspace(char *s) return s; } +char *compactchars(char *s, char c) +{ + if (s && *s && c) { + char *t = s; + char *p = s; + int n = 0; + while (*p) { + if (*p != c) { + *t++ = *p; + n = 0; + } + else if (t != s && n == 0) { + *t++ = *p; + n++; + } + p++; + } + if (n) + t--; // the last character was c + *t = 0; + } + return s; +} + cString strescape(const char *s, const char *chars) { char *buffer; @@ -970,6 +1019,20 @@ cString::cString(const char *S, bool TakePointer) s = TakePointer ? (char *)S : S ? strdup(S) : NULL; } +cString::cString(const char *S, const char *To) +{ + if (!S) + s = NULL; + else if (!To) + s = strdup(S); + else { + int l = To - S; + s = MALLOC(char, l + 1); + strncpy(s, S, l); + s[l] = 0; + } +} + cString::cString(const cString &String) { s = String.s ? strdup(String.s) : NULL; @@ -1008,6 +1071,12 @@ cString &cString::Truncate(int Index) return *this; } +cString &cString::CompactChars(char c) +{ + compactchars(s, c); + return *this; +} + cString cString::sprintf(const char *fmt, ...) { va_list ap; diff --git a/tools.h b/tools.h index 9cfd152b..358f75e3 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 3.2 2013/08/23 10:32:51 kls Exp $ + * $Id: tools.h 3.3 2013/09/22 13:30:14 kls Exp $ */ #ifndef __TOOLS_H @@ -170,6 +170,7 @@ private: char *s; public: cString(const char *S = NULL, bool TakePointer = false); + cString(const char *S, const char *To); ///< Copies S up to To (exclusive). To must be a valid pointer into S. If To is NULL, everything is copied. cString(const cString &String); virtual ~cString(); operator const void * () const { return s; } // to catch cases where operator*() should be used @@ -178,6 +179,7 @@ public: cString &operator=(const cString &String); cString &operator=(const char *String); cString &Truncate(int Index); ///< Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string). + cString &CompactChars(char c); ///< Compact any sequence of characters 'c' to a single character, and strip all of them from the beginning and end of this string. static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); static cString vsprintf(const char *fmt, va_list &ap); }; @@ -193,6 +195,8 @@ char *strcpyrealloc(char *dest, const char *src); char *strn0cpy(char *dest, const char *src, size_t n); char *strreplace(char *s, char c1, char c2); char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary! +const char *strchrn(const char *s, char c, size_t n); ///< returns a pointer to the n'th occurrence (counting from 1) of c in s, or NULL if no such character was found. If n is 0, s is returned. +int strcountchr(const char *s, char c); ///< returns the number of occurrences of 'c' in 's'. inline char *skipspace(const char *s) { if ((uchar)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible @@ -203,6 +207,7 @@ inline char *skipspace(const char *s) } char *stripspace(char *s); char *compactspace(char *s); +char *compactchars(char *s, char c); ///< removes all occurrences of 'c' from the beginning an end of 's' and replaces sequences of multiple 'c's with a single 'c'. cString strescape(const char *s, const char *chars); bool startswith(const char *s, const char *p); bool endswith(const char *s, const char *p); diff --git a/vdr.c b/vdr.c index 3fc44d35..9769b0f2 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.2 2013/09/10 13:58:34 kls Exp $ + * $Id: vdr.c 3.3 2013/10/10 12:25:03 kls Exp $ */ #include @@ -1320,8 +1320,9 @@ int main(int argc, char *argv[]) if (!Menu) { if (!InhibitEpgScan) EITScanner.Process(); - if (!cCutter::Active() && cCutter::Ended()) { - if (cCutter::Error()) + bool Error = false; + if (RecordingsHandler.Finished(Error)) { + if (Error) Skins.Message(mtError, tr("Editing process failed!")); else Skins.Message(mtInfo, tr("Editing process finished")); @@ -1341,7 +1342,10 @@ int main(int argc, char *argv[]) ShutdownHandler.countdown.Cancel(); } - if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !cCutter::Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) { + // Keep the recordings handler alive: + RecordingsHandler.Active(); + + if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) { // Handle housekeeping tasks // Shutdown: @@ -1390,7 +1394,7 @@ Exit: PluginManager.StopPlugins(); cRecordControls::Shutdown(); - cCutter::Stop(); + RecordingsHandler.DelAll(); delete Menu; cControl::Shutdown(); delete Interface; diff --git a/videodir.c b/videodir.c index cd739ea1..1885a1a7 100644 --- a/videodir.c +++ b/videodir.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.c 3.2 2013/09/11 12:20:37 kls Exp $ + * $Id: videodir.c 3.3 2013/10/08 13:26:41 kls Exp $ */ #include "videodir.h" @@ -79,6 +79,7 @@ bool cVideoDirectory::Register(const char *FileName) bool cVideoDirectory::Rename(const char *OldName, const char *NewName) { + dsyslog("renaming '%s' to '%s'", OldName, NewName); if (rename(OldName, NewName) == -1) { LOG_ERROR_STR(NewName); return false; @@ -88,10 +89,15 @@ bool cVideoDirectory::Rename(const char *OldName, const char *NewName) bool cVideoDirectory::Move(const char *FromName, const char *ToName) { - if (rename(FromName, ToName) == -1) { - LOG_ERROR_STR(ToName); - return false; + dsyslog("moving '%s' to '%s'", FromName, ToName); + if (EntriesOnSameFileSystem(FromName, ToName)) { + if (rename(FromName, ToName) == -1) { + LOG_ERROR_STR(ToName); + return false; + } } + else + return RecordingsHandler.Add(ruMove, FromName, ToName); return true; }