1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented "Pattern Timers"

This commit is contained in:
Klaus Schmidinger 2020-12-26 15:49:01 +01:00
parent d2e0087c4e
commit 2b3556b460
46 changed files with 1161 additions and 86 deletions

View File

@ -9562,3 +9562,9 @@ Video Disk Recorder Revision History
- Fixed a compiler warning (thanks to Winfried Köhler). - Fixed a compiler warning (thanks to Winfried Köhler).
- Fixed convertCharacterTable() in case iconv_open() fails (thanks to Helmut Binder). - Fixed convertCharacterTable() in case iconv_open() fails (thanks to Helmut Binder).
- Official release. - Official release.
2020-12-26: Version 2.5.1
- Implemented "Pattern Timers" (see MANUAL, vdr.1 and vdr.5 for details).
- Events in the past are no longer marked as having a timer in the Schedules
menu.

125
MANUAL
View File

@ -480,6 +480,8 @@ Version 2.4
"forever", and a value of 0 means that this recording can be "forever", and a value of 0 means that this recording can be
deleted any time if a recording with a higher priority needs disk deleted any time if a recording with a higher priority needs disk
space. space.
Pattern: The pattern to use for recordings matching events (only available
for pattern timers). See section "Pattern timers" below.
File: The name under which a recording created through this timer will File: The name under which a recording created through this timer will
be stored on disk (the actual name will also contain the date and be stored on disk (the actual name will also contain the date and
time, so it is possible to have a "repeating timer" store all its time, so it is possible to have a "repeating timer" store all its
@ -511,6 +513,129 @@ Version 2.4
The "Red" key in the "Edit timer" menu opens a list of folders, which can be The "Red" key in the "Edit timer" menu opens a list of folders, which can be
used to define the file name in which the recording will be stored. used to define the file name in which the recording will be stored.
The "Yellow" key in the "Edit timer" menu toggles the timer between "Pattern"
and "Regular".
When editing the "File" field, the "Blue" key in can be used to insert useful
macros.
* Pattern timers
There are cases where it would make sense to have a more flexible kind of
recording timer. For instance, some channels that provide VPS don't always
use the exact same VPS time for a series, which is extremely annoying.
Or you might want to record all films that have a certain pattern in their
title, no matter when they are broadcast. In such cases, "pattern timers"
can help.
In the "Edit timer" menu press the Yellow button to turn a regular timer into
a pattern timer. Pressing this button again switches back to regular.
The following rules apply to pattern timers:
- Pattern timers can only work for channels that provide EPG data.
- When using pattern timers, there should always be at least one free device that
can be used to regularly receive the EPG of the pattern timer's channel.
- A pattern timer records every matching event on the given channel that overlaps
with the given start/stop time. Overlapping events are recorded in full,
even if they extend outside the given start/stop interval.
- In order to actually record an event, a pattern timer "spawns" a separate timer
that does the recording. At most two timers are spawned from a pattern timer at
any given time, one for the next upcoming matching event, and one for
the event immediately following that one, in case it also matches.
- Spawned timers are marked with the flag tfSpawned.
- Spawned timers take the Priority, Lifetime and VPS settings from the pattern timer.
- The special pattern "*" matches every event. So a timer with
a start/stop time of 00:00/23:59 will record every event of that day
into separate recordings. Note that when using this pattern there should
be no other timers for the same channel, because these might interfere.
- Once a timer has been spawned, it is treated like any other regular
timer. Any changes made to the corresponding pattern timer thereafter will have
no effect on spawned timers. Note that after deleting a spawned timer,
the corresponding pattern timer may respawn it.
- Recording is done according to the event's begin/end times, either
by adding the start/stop margins (for non-VPS timers) or by using the
event's running status (for VPS timers).
- The recording of a pattern timer is stored under the given file name, just like
regular timers do. In addition to the "TITLE" and "EPISODE" macros the file
name of a pattern timer can also use "{<}" and "{>}" to reference the part of the
event's title before and after the pattern, respectively. For instance,
if the event's title is "Abc def ghi" and the pattern is "def ", "{<}"
would contain "Abc " and "{>}" would contain "ghi" (note the matching of the
blanks). For completeness, "{=}" can be used to reference the matching
pattern itself.
- In the "Timers" menu pressing the Red button on a pattern timer only toggles the
timer between "on" and "off", even if this is a repeating timer.
- In the "Timers" menu pattern timers are sorted alphabetically to the end of the
list of timers.
- A regular timer that is currently recording can't be changed into a pattern timer.
- In the "Edit timer" menu the file name and pattern are displayed as
separate items. The Yellow button can be used to toggle between a regular
timer and a pattern timer. When going from regular to pattern, the Pattern item will
be initialized with the base part of the file name.
- The characters '^' and '$' can be used at the very beginning and end of
the pattern to anchor the pattern to the begin or end of the title.
Using both of these will match only titles that consist of exactly the given pattern,
with nothing before and nothing after it.
- The Pattern field in the "Edit timer" menu allows blanks at the end of the string,
which may help to separate the text after the matching pattern.
- If the first character of the pattern is '@', an event that matches the
rest of the pattern is only recorded if the resulting recording's file
name (without any folders) is not contained in the donerecs.data file.
This avoids duplicate recordings of the same programme. Timers spawned from
such a pattern timer are marked with the flag tfAvoid.
- When editing the "File" field of a timer, the Blue button can be pressed to
insert one of the macros "TITLE", "EPISODE", "{<}", "{=}" or "{>}",
respectively. Pressing the Blue button repeatedly loops through the available
macros. The "{...}" macros are only available for pattern timers.
- In the "Schedule" and "What's on...?" menus the events that will be recorded
by a pattern timer are marked in the same way as regular timers.
- The TIMERS column in the LCARS skin doesn't show the basic definitions of
pattern timers, it only shows timers actually spawned from pattern timers.
If the pattern is prepended with '@', the name of the resulting recording (everything
after the rightmost '~', or the entire file name, if there is no '~') will be stored
in the file donerecs.data, so that multiple recordings of the same programme can be
avoided. When using this feature, special care must be taken regarding the recording's
file name. For instance, with a combination of
pattern file name
@Columbo Movies~TITLE
if the event's title is just "Columbo", this pattern timer would only record once,
and ignore any future events with that title, even if the episode would be different.
So you may want to use the episode name, as in
pattern file name
@Columbo Movies~TITLE - EPISODE
to make the file name unique. If you have several pattern timers for the same show on
different channels, chances are that the broadcasters handle title and episode
differently, as for example in
TITLE EPISODE pattern file name
Columbo Blueprint for Murder @^Columbo$ TITLE - EPISODE
Columbo - Blueprint for Murder @^Columbo TITLE
Columbo: Blueprint for Murder @^Columbo:_ Columbo - {>}
(note the '_' in the pattern of the third example; this is just used to visualize
the blank at the end of the pattern)
In order to have the same episode result in the same recording file name on all
channels, the file name needs to be generated differently for each channel. First
you need to decide on a proper combination of title and episode name, preferably
one that is already used by one of your channels (let's say the second one).
In the first case, title and episode name are correctly put in their respective
places, and "TITLE - EPISODE" as file name will do. The second case is our common
version, where everything is in the title, so TITLE is just fine. The third case
poses a problem, because everything is in the title, but with a different separator.
Here the special macro "{>}" can be used in the file name, which contains everything
following the matching pattern. There are three macros that can be used here:
{<} everything before the matching pattern
{>} everything after the matching pattern
{=} the matching pattern itself (just for completeness)
* Managing folders * Managing folders
The "Select folder" menu, which can be accessed by pressing the "Red" key in The "Select folder" menu, which can be accessed by pressing the "Red" key in

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.h 4.21 2020/12/22 17:23:51 kls Exp $ * $Id: config.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __CONFIG_H #ifndef __CONFIG_H
@ -22,13 +22,13 @@
// VDR's own version number: // VDR's own version number:
#define VDRVERSION "2.4.6" #define VDRVERSION "2.5.1"
#define VDRVERSNUM 20406 // Version * 10000 + Major * 100 + Minor #define VDRVERSNUM 20501 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number: // The plugin API's version number:
#define APIVERSION "2.4.6" #define APIVERSION "2.5.1"
#define APIVERSNUM 20406 // Version * 10000 + Major * 100 + Minor #define APIVERSNUM 20501 // Version * 10000 + Major * 100 + Minor
// When loading plugins, VDR searches them by their APIVERSION, which // When loading plugins, VDR searches them by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to // may be smaller than VDRVERSION in case there have been no changes to
@ -46,6 +46,13 @@
#define TIMERMACRO_TITLE "TITLE" #define TIMERMACRO_TITLE "TITLE"
#define TIMERMACRO_EPISODE "EPISODE" #define TIMERMACRO_EPISODE "EPISODE"
#define TIMERMACRO_BEFORE "{<}"
#define TIMERMACRO_MATCH "{=}"
#define TIMERMACRO_AFTER "{>}"
#define TIMERPATTERN_AVOID "@"
#define TIMERPATTERN_BEGIN "^"
#define TIMERPATTERN_END "$"
#define MINOSDWIDTH 480 #define MINOSDWIDTH 480
#define MAXOSDWIDTH 1920 #define MAXOSDWIDTH 1920

94
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 4.88 2020/12/12 22:01:01 kls Exp $ * $Id: menu.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -993,6 +993,23 @@ eOSState cMenuFolder::ProcessKey(eKeys Key)
// --- cMenuEditTimer -------------------------------------------------------- // --- cMenuEditTimer --------------------------------------------------------
static const char *TimerFileMacrosForPattern[] = {
TIMERMACRO_TITLE,
TIMERMACRO_EPISODE,
TIMERMACRO_BEFORE,
TIMERMACRO_MATCH,
TIMERMACRO_AFTER,
"",
NULL
};
static const char *TimerFileMacros[] = {
TIMERMACRO_TITLE,
TIMERMACRO_EPISODE,
"",
NULL
};
const cTimer *cMenuEditTimer::addedTimer = NULL; const cTimer *cMenuEditTimer::addedTimer = NULL;
cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New) cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
@ -1000,6 +1017,7 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
{ {
SetMenuCategory(mcTimerEdit); SetMenuCategory(mcTimerEdit);
addedTimer = NULL; addedTimer = NULL;
pattern = NULL;
file = NULL; file = NULL;
day = firstday = NULL; day = firstday = NULL;
timer = Timer; timer = Timer;
@ -1019,6 +1037,7 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME)); Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file))); Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
SetFirstDayItem(); SetFirstDayItem();
SetPatternItem(true);
if (data.remote) if (data.remote)
strn0cpy(remote, data.remote, sizeof(remote)); strn0cpy(remote, data.remote, sizeof(remote));
else else
@ -1047,7 +1066,7 @@ const cTimer *cMenuEditTimer::AddedTimer(void)
void cMenuEditTimer::SetHelpKeys(void) void cMenuEditTimer::SetHelpKeys(void)
{ {
SetHelp(tr("Button$Folder"), data.weekdays ? tr("Button$Single") : tr("Button$Repeating")); SetHelp(tr("Button$Folder"), data.weekdays ? tr("Button$Single") : tr("Button$Repeating"), *data.pattern ? tr("Button$Regular") : tr("Button$Pattern"));
} }
void cMenuEditTimer::SetFirstDayItem(void) void cMenuEditTimer::SetFirstDayItem(void)
@ -1063,6 +1082,40 @@ void cMenuEditTimer::SetFirstDayItem(void)
} }
} }
void cMenuEditTimer::SetPatternItem(bool Initial)
{
if (Initial && !*data.pattern) {
file->SetMacros(TimerFileMacros);
return;
}
if (!pattern) {
if (data.HasFlags(tfRecording)) {
Skins.Message(mtWarning, tr("Timer is recording!"));
return;
}
if (!*data.pattern) {
char *p = strrchr(data.file, FOLDERDELIMCHAR);
if (p)
p++;
else
p = data.file;
strn0cpy(data.pattern, p, sizeof(data.pattern));
}
Ins(pattern = new cMenuEditStrItem( tr("Pattern"), data.pattern, sizeof(data.pattern)), true, file);
pattern->SetKeepSpace();
file->SetMacros(TimerFileMacrosForPattern);
Display();
}
else {
Del(pattern->Index());
pattern = NULL;
*data.pattern = 0;
file->SetMacros(TimerFileMacros);
Display();
}
SetHelpKeys();
}
eOSState cMenuEditTimer::SetFolder(void) eOSState cMenuEditTimer::SetFolder(void)
{ {
if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) { if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
@ -1142,6 +1195,8 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
cRecordControls::Stop(timer); cRecordControls::Stop(timer);
if (timer->Remote() && data.Remote()) if (timer->Remote() && data.Remote())
Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll); Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
if (data.Local() && !timer->IsPatternTimer() && data.IsPatternTimer())
data.SetEvent(NULL);
*timer = data; *timer = data;
} }
LOCK_SCHEDULES_READ; LOCK_SCHEDULES_READ;
@ -1159,7 +1214,8 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
Display(); Display();
} }
return osContinue; return osContinue;
case kYellow: case kYellow: SetPatternItem();
return osContinue;
case kBlue: return osContinue; case kBlue: return osContinue;
default: break; default: break;
} }
@ -1212,12 +1268,19 @@ void cMenuTimerItem::Set(void)
strftime(buffer, sizeof(buffer), "%Y%m%d", &tm_r); strftime(buffer, sizeof(buffer), "%Y%m%d", &tm_r);
day = buffer; day = buffer;
} }
const char *File = Setup.FoldersInTimerMenu ? NULL : strrchr(timer->File(), FOLDERDELIMCHAR); const char *File = timer->Pattern();
if (File && strcmp(File + 1, TIMERMACRO_TITLE) && strcmp(File + 1, TIMERMACRO_EPISODE)) if (!*File) {
File++; if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title())
else File = timer->Event()->Title();
File = timer->File(); else {
SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s%s", File = Setup.FoldersInTimerMenu ? NULL : strrchr(timer->File(), FOLDERDELIMCHAR);
if (File && strcmp(File + 1, TIMERMACRO_TITLE) && strcmp(File + 1, TIMERMACRO_EPISODE))
File++;
else
File = timer->File();
}
}
SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s%s%s%s",
!(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>', !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
timer->Channel()->Number(), timer->Channel()->Number(),
*name, *name,
@ -1228,7 +1291,9 @@ void cMenuTimerItem::Set(void)
timer->Stop() / 100, timer->Stop() / 100,
timer->Stop() % 100, timer->Stop() % 100,
timer->Remote() ? *cString::sprintf("@%s: ", timer->Remote()) : "", timer->Remote() ? *cString::sprintf("@%s: ", timer->Remote()) : "",
File)); timer->IsPatternTimer() ? "{" : "",
File,
timer->IsPatternTimer() ? "}" : ""));
} }
void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
@ -1544,6 +1609,8 @@ bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
eTimerMatch OldTimerMatch = timerMatch; eTimerMatch OldTimerMatch = timerMatch;
bool OldTimerActive = timerActive; bool OldTimerActive = timerActive;
const cTimer *Timer = Timers->GetMatch(event, &timerMatch); const cTimer *Timer = Timers->GetMatch(event, &timerMatch);
if (event->EndTime() < time(NULL) && !event->IsRunning())
timerMatch = tmNone;
timerActive = Timer && Timer->HasFlags(tfActive); timerActive = Timer && Timer->HasFlags(tfActive);
if (Force || timerMatch != OldTimerMatch || timerActive != OldTimerActive) { if (Force || timerMatch != OldTimerMatch || timerActive != OldTimerActive) {
cString buffer; cString buffer;
@ -5354,8 +5421,13 @@ void cRecordControl::Stop(bool ExecuteUserCommand)
bool cRecordControl::Process(time_t t) bool cRecordControl::Process(time_t t)
{ {
if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t)) { if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t)) {
if (timer) if (timer) {
timer->SetPending(false); timer->SetPending(false);
if (timer->HasFlags(tfAvoid)) {
const char *p = strgetlast(timer->File(), FOLDERDELIMCHAR);
DoneRecordingsPattern.Append(p);
}
}
return false; return false;
} }
return true; return true;

4
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.h 4.8 2018/04/14 10:24:41 kls Exp $ * $Id: menu.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __MENU_H #ifndef __MENU_H
@ -79,11 +79,13 @@ private:
bool addIfConfirmed; bool addIfConfirmed;
cStringList svdrpServerNames; cStringList svdrpServerNames;
char remote[HOST_NAME_MAX]; char remote[HOST_NAME_MAX];
cMenuEditStrItem *pattern;
cMenuEditStrItem *file; cMenuEditStrItem *file;
cMenuEditDateItem *day; cMenuEditDateItem *day;
cMenuEditDateItem *firstday; cMenuEditDateItem *firstday;
eOSState SetFolder(void); eOSState SetFolder(void);
void SetFirstDayItem(void); void SetFirstDayItem(void);
void SetPatternItem(bool Initial = false);
void SetHelpKeys(void); void SetHelpKeys(void);
public: public:
cMenuEditTimer(cTimer *Timer, bool New = false); cMenuEditTimer(cTimer *Timer, bool New = false);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menuitems.c 4.3 2018/03/23 15:37:02 kls Exp $ * $Id: menuitems.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "menuitems.h" #include "menuitems.h"
@ -390,6 +390,10 @@ cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, co
allowed = Allowed ? Allowed : tr(FileNameChars); allowed = Allowed ? Allowed : tr(FileNameChars);
pos = -1; pos = -1;
offset = 0; offset = 0;
keepSpace = false;
macro = -1;
lastMacro = -1;
macros = NULL;
insert = uppercase = false; insert = uppercase = false;
newchar = true; newchar = true;
lengthUtf8 = 0; lengthUtf8 = 0;
@ -408,6 +412,13 @@ cMenuEditStrItem::~cMenuEditStrItem()
delete[] charMapUtf8; delete[] charMapUtf8;
} }
void cMenuEditStrItem::SetMacros(const char **Macros)
{
macros = Macros;
macro = 0;
lastMacro = -1;
}
void cMenuEditStrItem::EnterEditMode(void) void cMenuEditStrItem::EnterEditMode(void)
{ {
if (!valueUtf8) { if (!valueUtf8) {
@ -430,7 +441,8 @@ void cMenuEditStrItem::LeaveEditMode(bool SaveValue)
if (valueUtf8) { if (valueUtf8) {
if (SaveValue) { if (SaveValue) {
Utf8FromArray(valueUtf8, value, length); Utf8FromArray(valueUtf8, value, length);
stripspace(value); if (!keepSpace)
stripspace(value);
} }
lengthUtf8 = 0; lengthUtf8 = 0;
delete[] valueUtf8; delete[] valueUtf8;
@ -448,7 +460,7 @@ void cMenuEditStrItem::LeaveEditMode(bool SaveValue)
void cMenuEditStrItem::SetHelpKeys(void) void cMenuEditStrItem::SetHelpKeys(void)
{ {
if (InEditMode()) if (InEditMode())
SetHelp(tr("Button$ABC/abc"), insert ? tr("Button$Overwrite") : tr("Button$Insert"), tr("Button$Delete")); SetHelp(tr("Button$ABC/abc"), insert ? tr("Button$Overwrite") : tr("Button$Insert"), tr("Button$Delete"), macros ? tr("Button$Macro") : NULL);
else else
SetHelp(NULL); SetHelp(NULL);
} }
@ -581,11 +593,39 @@ void cMenuEditStrItem::Delete(void)
lengthUtf8--; lengthUtf8--;
} }
void cMenuEditStrItem::InsertMacro(void)
{
if (!macros)
return;
if (lastMacro >= 0) {
int l = strlen(macros[lastMacro]);
while (l-- > 0)
Delete();
}
const char *p = macros[macro];
int oldPos = pos;
bool oldInsert = insert;
insert = true;
newchar = true;
while (*p) {
Type(*p);
p++;
}
insert = oldInsert;
pos = oldPos;
lastMacro = macro;
if (!macros[++macro])
macro = 0;
}
eOSState cMenuEditStrItem::ProcessKey(eKeys Key) eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
{ {
bool SameKey = NORMALKEY(Key) == lastKey; bool SameKey = NORMALKEY(Key) == lastKey;
if (Key != kNone) if (Key != kNone) {
lastKey = NORMALKEY(Key); lastKey = NORMALKEY(Key);
if (Key != kBlue)
lastMacro = -1;
}
else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) { else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
AdvancePos(); AdvancePos();
newchar = true; newchar = true;
@ -635,8 +675,9 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
return osUnknown; return osUnknown;
break; break;
case kBlue|k_Repeat: case kBlue|k_Repeat:
case kBlue: // consume the key only if in edit-mode case kBlue: if (InEditMode())
if (!InEditMode()) InsertMacro();
else
return osUnknown; return osUnknown;
break; break;
case kLeft|k_Repeat: case kLeft|k_Repeat:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menuitems.h 4.1 2015/09/06 10:38:37 kls Exp $ * $Id: menuitems.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __MENUITEMS_H #ifndef __MENUITEMS_H
@ -111,6 +111,9 @@ private:
int length; int length;
const char *allowed; const char *allowed;
int pos, offset; int pos, offset;
bool keepSpace;
const char **macros;
int macro, lastMacro;
bool insert, newchar, uppercase; bool insert, newchar, uppercase;
int lengthUtf8; int lengthUtf8;
uint *valueUtf8; uint *valueUtf8;
@ -127,6 +130,7 @@ private:
void Type(uint c); void Type(uint c);
void Insert(void); void Insert(void);
void Delete(void); void Delete(void);
void InsertMacro(void);
protected: protected:
void EnterEditMode(void); void EnterEditMode(void);
void LeaveEditMode(bool SaveValue = false); void LeaveEditMode(bool SaveValue = false);
@ -134,6 +138,8 @@ protected:
public: public:
cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed = NULL); cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed = NULL);
~cMenuEditStrItem(); ~cMenuEditStrItem();
void SetKeepSpace(void) { keepSpace = true; }
void SetMacros(const char **Macros);
virtual eOSState ProcessKey(eKeys Key); virtual eOSState ProcessKey(eKeys Key);
}; };

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2008-10-16 11:16-0400\n" "PO-Revision-Date: 2008-10-16 11:16-0400\n"
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n" "Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
"Language-Team: Arabic <ar@li.org>\n" "Language-Team: Arabic <ar@li.org>\n"
@ -691,9 +691,21 @@ msgstr "Single"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Repeating" msgstr "Repeating"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "اليوم الاول" msgstr "اليوم الاول"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1495,6 +1507,9 @@ msgstr "اعادة الكتابة"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "ادراج" msgstr "ادراج"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "الملحق" msgstr "الملحق"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2008-03-02 19:02+0100\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n"
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n" "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
"Language-Team: Catalan <vdr@linuxtv.org>\n" "Language-Team: Catalan <vdr@linuxtv.org>\n"
@ -690,9 +690,21 @@ msgstr "Individual"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Repetitiu" msgstr "Repetitiu"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Primer dia" msgstr "Primer dia"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1494,6 +1506,9 @@ msgstr "Sobrescriure"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Inserir" msgstr "Inserir"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2010-05-06 11:00+0200\n" "PO-Revision-Date: 2010-05-06 11:00+0200\n"
"Last-Translator: Aleš Juřík <ajurik@quick.cz>\n" "Last-Translator: Aleš Juřík <ajurik@quick.cz>\n"
"Language-Team: Czech <vdr@linuxtv.org>\n" "Language-Team: Czech <vdr@linuxtv.org>\n"
@ -690,9 +690,21 @@ msgstr "Bez opakování"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "S opakováním" msgstr "S opakováním"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "První den" msgstr "První den"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1494,6 +1506,9 @@ msgstr "Přepsat"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Vložit" msgstr "Vložit"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Modul" msgstr "Modul"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n" "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
"Language-Team: Danish <vdr@linuxtv.org>\n" "Language-Team: Danish <vdr@linuxtv.org>\n"
@ -687,9 +687,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Første dag" msgstr "Første dag"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1491,6 +1503,9 @@ msgstr "Overskriv"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Indsæt" msgstr "Indsæt"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-10 13:45+0100\n" "PO-Revision-Date: 2015-02-10 13:45+0100\n"
"Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n" "Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
"Language-Team: German <vdr@linuxtv.org>\n" "Language-Team: German <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Einmalig"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Wiederholend" msgstr "Wiederholend"
msgid "Button$Regular"
msgstr "Normal"
msgid "Button$Pattern"
msgstr "Muster"
msgid "First day" msgid "First day"
msgstr "Erster Tag" msgstr "Erster Tag"
msgid "Timer is recording!"
msgstr "Timer nimmt auf!"
msgid "Pattern"
msgstr "Muster"
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Fehler beim Ansprechen des fernen Timers" msgstr "Fehler beim Ansprechen des fernen Timers"
@ -1492,6 +1504,9 @@ msgstr "
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Einfügen" msgstr "Einfügen"
msgid "Button$Macro"
msgstr "Makro"
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n" "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
"Language-Team: Greek <vdr@linuxtv.org>\n" "Language-Team: Greek <vdr@linuxtv.org>\n"
@ -687,9 +687,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Ðñþôç ìÝñá" msgstr "Ðñþôç ìÝñá"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1491,6 +1503,9 @@ msgstr "
msgid "Button$Insert" msgid "Button$Insert"
msgstr "ÅéóáãùãÞ" msgstr "ÅéóáãùãÞ"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "ÅðÝêôáóç" msgstr "ÅðÝêôáóç"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-19 23:00+0100\n" "PO-Revision-Date: 2015-02-19 23:00+0100\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n" "Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n" "Language-Team: Spanish <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Individual"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Periódico" msgstr "Periódico"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Primer día" msgstr "Primer día"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr "Sobreescribir"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Insertar" msgstr "Insertar"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n" "Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
"Language-Team: Estonian <vdr@linuxtv.org>\n" "Language-Team: Estonian <vdr@linuxtv.org>\n"
@ -687,9 +687,21 @@ msgstr "Üksik"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Korduv" msgstr "Korduv"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "1. päev" msgstr "1. päev"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Kaugtaimeri viga" msgstr "Kaugtaimeri viga"
@ -1491,6 +1503,9 @@ msgstr "Asenda (OVR)"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Lisa (INS)" msgstr "Lisa (INS)"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2007-08-15 15:52+0200\n" "PO-Revision-Date: 2007-08-15 15:52+0200\n"
"Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n" "Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n" "Language-Team: Finnish <vdr@linuxtv.org>\n"
@ -691,9 +691,21 @@ msgstr "Yksittäinen"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Toistuva" msgstr "Toistuva"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "1. päivä" msgstr "1. päivä"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Etäajastimen hakeminen epäonnistui" msgstr "Etäajastimen hakeminen epäonnistui"
@ -1495,6 +1507,9 @@ msgstr "Korvaa"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Lisää" msgstr "Lisää"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Laajennos" msgstr "Laajennos"

View File

@ -18,7 +18,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-04-14 10:16+0100\n" "PO-Revision-Date: 2018-04-14 10:16+0100\n"
"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n" "Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
"Language-Team: French <vdr@linuxtv.org>\n" "Language-Team: French <vdr@linuxtv.org>\n"
@ -698,9 +698,21 @@ msgstr "Simple"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Périodique" msgstr "Périodique"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Premier jour" msgstr "Premier jour"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Erreur pendant l'accès à la programmation" msgstr "Erreur pendant l'accès à la programmation"
@ -1502,6 +1514,9 @@ msgstr "Écraser"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Insérer" msgstr "Insérer"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Module" msgstr "Module"

View File

@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2008-03-17 19:00+0100\n" "PO-Revision-Date: 2008-03-17 19:00+0100\n"
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n" "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
"Language-Team: Croatian <vdr@linuxtv.org>\n" "Language-Team: Croatian <vdr@linuxtv.org>\n"
@ -689,9 +689,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Prvi dan" msgstr "Prvi dan"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1493,6 +1505,9 @@ msgstr "Prepi
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Umetni" msgstr "Umetni"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Dodatak" msgstr "Dodatak"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-04-09 21:42+0300\n" "PO-Revision-Date: 2018-04-09 21:42+0300\n"
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n" "Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
"Language-Team: Hungarian <vdr@linuxtv.org>\n" "Language-Team: Hungarian <vdr@linuxtv.org>\n"
@ -692,9 +692,21 @@ msgstr "Egyszeri"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Ismétlődő" msgstr "Ismétlődő"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Első nap" msgstr "Első nap"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Távoli időzítő nem elérhető" msgstr "Távoli időzítő nem elérhető"
@ -1496,6 +1508,9 @@ msgstr "Felülírás"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Beillesztés" msgstr "Beillesztés"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-04-06 19:13+0100\n" "PO-Revision-Date: 2018-04-06 19:13+0100\n"
"Last-Translator: Gringo <vdr-italian@tiscali.it>\n" "Last-Translator: Gringo <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n" "Language-Team: Italian <vdr@linuxtv.org>\n"
@ -693,9 +693,21 @@ msgstr "Una volta"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Repliche" msgstr "Repliche"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "1° giorno" msgstr "1° giorno"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Errore durante l'accesso al timer remoto" msgstr "Errore durante l'accesso al timer remoto"
@ -1497,6 +1509,9 @@ msgstr "Sovrascrivi"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Inserisci" msgstr "Inserisci"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-11 14:02+0200\n" "PO-Revision-Date: 2015-02-11 14:02+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n" "Language-Team: Lithuanian <vdr@linuxtv.org>\n"
@ -687,9 +687,21 @@ msgstr "Vienas"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Kartotinas" msgstr "Kartotinas"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Pirma diena" msgstr "Pirma diena"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1491,6 +1503,9 @@ msgstr "Perrąšyti"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Įterpti" msgstr "Įterpti"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Įskiepas" msgstr "Įskiepas"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-03-31 21:47+0100\n" "PO-Revision-Date: 2018-03-31 21:47+0100\n"
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n" "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
"Language-Team: Macedonian <kde-i18n-doc@kde.org>\n" "Language-Team: Macedonian <kde-i18n-doc@kde.org>\n"
@ -689,9 +689,21 @@ msgstr "Единчен"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Периодичен" msgstr "Периодичен"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Прв ден" msgstr "Прв ден"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Грешка при пристап на далечен тајмер" msgstr "Грешка при пристап на далечен тајмер"
@ -1493,6 +1505,9 @@ msgstr "Препиши"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Вметни" msgstr "Вметни"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Додаток" msgstr "Додаток"

View File

@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-10 19:43+0100\n" "PO-Revision-Date: 2015-02-10 19:43+0100\n"
"Last-Translator: Erik Oomen <oomen.e@gmail.com>\n" "Last-Translator: Erik Oomen <oomen.e@gmail.com>\n"
"Language-Team: Dutch <vdr@linuxtv.org>\n" "Language-Team: Dutch <vdr@linuxtv.org>\n"
@ -693,9 +693,21 @@ msgstr "Eenmalig"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Herhalen" msgstr "Herhalen"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Eerste dag" msgstr "Eerste dag"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1497,6 +1509,9 @@ msgstr "Overschrijven"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Invoegen" msgstr "Invoegen"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n" "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n" "Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Første dag" msgstr "Første dag"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr ""
msgid "Button$Insert" msgid "Button$Insert"
msgstr "" msgstr ""
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-02-19 00:42+0100\n" "PO-Revision-Date: 2018-02-19 00:42+0100\n"
"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n" "Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n" "Language-Team: Polish <vdr@linuxtv.org>\n"
@ -692,9 +692,21 @@ msgstr "Pojedynczy"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Powtarzanie" msgstr "Powtarzanie"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Pierwszy dzień" msgstr "Pierwszy dzień"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Błąd podczas dostępu do zdalnego timera" msgstr "Błąd podczas dostępu do zdalnego timera"
@ -1496,6 +1508,9 @@ msgstr "Nadpisz"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Wstaw" msgstr "Wstaw"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Wtyczka" msgstr "Wtyczka"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2010-03-28 22:49+0100\n" "PO-Revision-Date: 2010-03-28 22:49+0100\n"
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n" "Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
"Language-Team: Portuguese <vdr@linuxtv.org>\n" "Language-Team: Portuguese <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "1° dia" msgstr "1° dia"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr "Sobrescrever"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Inserir" msgstr "Inserir"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-11 22:26+0100\n" "PO-Revision-Date: 2015-02-11 22:26+0100\n"
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n" "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
"Language-Team: Romanian <vdr@linuxtv.org>\n" "Language-Team: Romanian <vdr@linuxtv.org>\n"
@ -689,9 +689,21 @@ msgstr "Odată"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Repetitiv" msgstr "Repetitiv"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Prima zi" msgstr "Prima zi"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1493,6 +1505,9 @@ msgstr "Suprascrie"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Inserează" msgstr "Inserează"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Plugin" msgstr "Plugin"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2016-12-27 17:13+0100\n" "PO-Revision-Date: 2016-12-27 17:13+0100\n"
"Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n" "Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n" "Language-Team: Russian <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Один раз"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Повтор" msgstr "Повтор"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Первый день" msgstr "Первый день"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Ошибка доступа к таймеру" msgstr "Ошибка доступа к таймеру"
@ -1492,6 +1504,9 @@ msgstr "Замена"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Вставка" msgstr "Вставка"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Модуль" msgstr "Модуль"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-17 18:59+0100\n" "PO-Revision-Date: 2015-02-17 18:59+0100\n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <vdr@linuxtv.org>\n" "Language-Team: Slovak <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "bez opakovania"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "s opakovaním" msgstr "s opakovaním"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Odo dòa" msgstr "Odo dòa"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr "Prep
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Vlo¾i»" msgstr "Vlo¾i»"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Modul" msgstr "Modul"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2013-03-04 12:46+0100\n" "PO-Revision-Date: 2013-03-04 12:46+0100\n"
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n" "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
"Language-Team: Slovenian <vdr@linuxtv.org>\n" "Language-Team: Slovenian <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Enkraten"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Ponavljajoèe" msgstr "Ponavljajoèe"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Prvi dan" msgstr "Prvi dan"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr "Prepi
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Vstavi" msgstr "Vstavi"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Vstavek" msgstr "Vstavek"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2013-03-16 15:05+0100\n" "PO-Revision-Date: 2013-03-16 15:05+0100\n"
"Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n" "Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n"
"Language-Team: Serbian <vdr@linuxtv.org>\n" "Language-Team: Serbian <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Jedinstven"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Ponavljajuæi" msgstr "Ponavljajuæi"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Prvi dan" msgstr "Prvi dan"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1492,6 +1504,9 @@ msgstr "Zameni"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Ubaci" msgstr "Ubaci"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Dodatak" msgstr "Dodatak"

View File

@ -12,7 +12,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2015-02-12 21:58+0100\n" "PO-Revision-Date: 2015-02-12 21:58+0100\n"
"Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n" "Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n"
"Language-Team: Swedish <vdr@linuxtv.org>\n" "Language-Team: Swedish <vdr@linuxtv.org>\n"
@ -692,9 +692,21 @@ msgstr "Enskilld"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Repeterande" msgstr "Repeterande"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Första dag" msgstr "Första dag"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1496,6 +1508,9 @@ msgstr "Skriv
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Infoga" msgstr "Infoga"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Modul" msgstr "Modul"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2008-02-28 00:33+0100\n" "PO-Revision-Date: 2008-02-28 00:33+0100\n"
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n" "Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
"Language-Team: Turkish <vdr@linuxtv.org>\n" "Language-Team: Turkish <vdr@linuxtv.org>\n"
@ -687,9 +687,21 @@ msgstr ""
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "" msgstr ""
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Ýlk gün" msgstr "Ýlk gün"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1491,6 +1503,9 @@ msgstr "
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Ekle" msgstr "Ekle"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Eklenti" msgstr "Eklenti"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2018-03-18 20:00+0100\n" "PO-Revision-Date: 2018-03-18 20:00+0100\n"
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n" "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
"Language-Team: Ukrainian <vdr@linuxtv.org>\n" "Language-Team: Ukrainian <vdr@linuxtv.org>\n"
@ -688,9 +688,21 @@ msgstr "Одинарне"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "Повтор" msgstr "Повтор"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "Перший день" msgstr "Перший день"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "Помилка доступу до віддаленого таймера" msgstr "Помилка доступу до віддаленого таймера"
@ -1492,6 +1504,9 @@ msgstr "Заміна"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "Вставка" msgstr "Вставка"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "Модуль" msgstr "Модуль"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.4.0\n" "Project-Id-Version: VDR 2.4.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2020-06-15 17:50+0200\n" "POT-Creation-Date: 2020-12-24 17:42+0100\n"
"PO-Revision-Date: 2013-03-04 14:52+0800\n" "PO-Revision-Date: 2013-03-04 14:52+0800\n"
"Last-Translator: NFVDR <nfvdr@live.com>\n" "Last-Translator: NFVDR <nfvdr@live.com>\n"
"Language-Team: Chinese (simplified) <nfvdr@live.com>\n" "Language-Team: Chinese (simplified) <nfvdr@live.com>\n"
@ -689,9 +689,21 @@ msgstr "单个"
msgid "Button$Repeating" msgid "Button$Repeating"
msgstr "重复" msgstr "重复"
msgid "Button$Regular"
msgstr ""
msgid "Button$Pattern"
msgstr ""
msgid "First day" msgid "First day"
msgstr "第一天" msgstr "第一天"
msgid "Timer is recording!"
msgstr ""
msgid "Pattern"
msgstr ""
msgid "Error while accessing remote timer" msgid "Error while accessing remote timer"
msgstr "" msgstr ""
@ -1493,6 +1505,9 @@ msgstr "覆盖"
msgid "Button$Insert" msgid "Button$Insert"
msgstr "插入" msgstr "插入"
msgid "Button$Macro"
msgstr ""
msgid "Plugin" msgid "Plugin"
msgstr "插件" msgstr "插件"

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.c 4.29 2020/10/30 16:08:29 kls Exp $ * $Id: recording.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "recording.h" #include "recording.h"
@ -3050,6 +3050,74 @@ cUnbufferedFile *cFileName::NextFile(void)
return SetOffset(fileNumber + 1); return SetOffset(fileNumber + 1);
} }
// --- cDoneRecordings -------------------------------------------------------
cDoneRecordings DoneRecordingsPattern;
bool cDoneRecordings::Load(const char *FileName)
{
fileName = FileName;
if (*fileName && access(fileName, F_OK) == 0) {
isyslog("loading %s", *fileName);
FILE *f = fopen(fileName, "r");
if (f) {
char *s;
cReadLine ReadLine;
while ((s = ReadLine.Read(f)) != NULL)
Add(s);
fclose(f);
}
else {
LOG_ERROR_STR(*fileName);
return false;
}
}
return true;
}
bool cDoneRecordings::Save(void) const
{
bool result = true;
cSafeFile f(fileName);
if (f.Open()) {
for (int i = 0; i < doneRecordings.Size(); i++) {
if (fputs(doneRecordings[i], f) == EOF || fputc('\n', f) == EOF) {
result = false;
break;
}
}
if (!f.Close())
result = false;
}
else
result = false;
return result;
}
void cDoneRecordings::Add(const char *Title)
{
doneRecordings.Append(strdup(Title));
}
void cDoneRecordings::Append(const char *Title)
{
if (!Contains(Title)) {
Add(Title);
if (FILE *f = fopen(fileName, "a")) {
fputs(Title, f);
fputc('\n', f);
fclose(f);
}
else
esyslog("ERROR: can't open '%s' for appending '%s'", *fileName, Title);
}
}
bool cDoneRecordings::Contains(const char *Title) const
{
return doneRecordings.Find(Title) >= 0;
}
// --- Index stuff ----------------------------------------------------------- // --- Index stuff -----------------------------------------------------------
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond) cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.h 4.10 2020/09/16 13:48:33 kls Exp $ * $Id: recording.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __RECORDING_H #ifndef __RECORDING_H
@ -504,6 +504,20 @@ public:
cUnbufferedFile *NextFile(void); cUnbufferedFile *NextFile(void);
}; };
class cDoneRecordings {
private:
cString fileName;
cStringList doneRecordings;
void Add(const char *Title);
public:
bool Load(const char *FileName);
bool Save(void) const;
void Append(const char *Title);
bool Contains(const char *Title) const;
};
extern cDoneRecordings DoneRecordingsPattern;
cString IndexToHMSF(int Index, bool WithFrame = false, double FramesPerSecond = DEFAULTFRAMESPERSECOND); cString IndexToHMSF(int Index, bool WithFrame = false, double FramesPerSecond = DEFAULTFRAMESPERSECOND);
// Converts the given index to a string, optionally containing the frame number. // Converts the given index to a string, optionally containing the frame number.
int HMSFToIndex(const char *HMSF, double FramesPerSecond = DEFAULTFRAMESPERSECOND); int HMSFToIndex(const char *HMSF, double FramesPerSecond = DEFAULTFRAMESPERSECOND);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skinlcars.c 4.7 2020/05/18 16:47:29 kls Exp $ * $Id: skinlcars.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures, // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures,
@ -1264,7 +1264,9 @@ void cSkinLCARSDisplayMenu::DrawTimers(void)
if (y + lineHeight > ys05) if (y + lineHeight > ys05)
break; break;
if (const cTimer *Timer = SortedTimers[i]) { if (const cTimer *Timer = SortedTimers[i]) {
if (Timer->Recording()) { if (Timer->IsPatternTimer())
SortedTimers[i] = NULL;
else if (Timer->Recording()) {
if (Timer->Remote()) { if (Timer->Remote()) {
if (!Device && Timer->HasFlags(tfActive)) { if (!Device && Timer->HasFlags(tfActive)) {
DrawTimer(Timer, y, false); DrawTimer(Timer, y, false);

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 4.43 2020/06/22 20:59:49 kls Exp $ * $Id: svdrp.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "svdrp.h" #include "svdrp.h"
@ -2048,6 +2048,10 @@ void cSVDRPServer::CmdMODT(const char *Option)
Reply(501, "Error in timer settings"); Reply(501, "Error in timer settings");
return; return;
} }
if (IsRecording && t.IsPatternTimer()) {
Reply(550, "Timer is recording");
return;
}
*Timer = t; *Timer = t;
if (IsRecording) if (IsRecording)
Timer->SetFlags(tfRecording); Timer->SetFlags(tfRecording);
@ -2055,6 +2059,8 @@ void cSVDRPServer::CmdMODT(const char *Option)
Timer->ClrFlags(tfRecording); Timer->ClrFlags(tfRecording);
Timers->SetModified(); Timers->SetModified();
isyslog("SVDRP %s < %s modified timer %s (%s)", Setup.SVDRPHostName, *clientName, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive"); isyslog("SVDRP %s < %s modified timer %s (%s)", Setup.SVDRPHostName, *clientName, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive");
if (Timer->IsPatternTimer())
Timer->SetEvent(NULL);
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
} }
else else

250
timers.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: timers.c 4.20 2020/09/16 13:48:33 kls Exp $ * $Id: timers.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "timers.h" #include "timers.h"
@ -31,6 +31,7 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
deferred = 0; deferred = 0;
pending = inVpsMargin = false; pending = inVpsMargin = false;
flags = tfNone; flags = tfNone;
*pattern = 0;
*file = 0; *file = 0;
aux = NULL; aux = NULL;
remote = NULL; remote = NULL;
@ -81,7 +82,94 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name()); snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
} }
cTimer::cTimer(const cEvent *Event) static bool MatchPattern(const char *Pattern, const char *Title, cString *Before = NULL, cString *Match = NULL, cString *After = NULL)
{
if (Title) {
bool AvoidDuplicates = startswith(Pattern, TIMERPATTERN_AVOID);
if (AvoidDuplicates)
Pattern++;
if (strcmp(Pattern, "*") == 0) {
if (Before)
*Before = "";
if (Match)
*Match = Title;
if (After)
*After = "";
return true;
}
bool AnchorBegin = startswith(Pattern, TIMERPATTERN_BEGIN);
if (AnchorBegin)
Pattern++;
bool AnchorEnd = endswith(Pattern, TIMERPATTERN_END);
cNullTerminate nt;
if (AnchorEnd)
nt.Set(const_cast<char *>(Pattern + strlen(Pattern) - 1));
if (AnchorBegin && AnchorEnd) {
if (strcmp(Title, Pattern) == 0) {
if (Before)
*Before = "";
if (Match)
*Match = Title;
if (After)
*After = "";
return true;
}
}
else if (AnchorBegin) {
if (strstr(Title, Pattern) == Title) {
if (Before)
*Before = "";
if (Match)
*Match = Pattern;
if (After)
*After = cString(Title + strlen(Pattern));
return true;
}
}
else if (AnchorEnd) {
if (endswith(Title, Pattern)) {
if (Before)
*Before = cString(Title, Title + strlen(Title) - strlen(Pattern));
if (Match)
*Match = Pattern;
if (After)
*After = "";
return true;
}
}
else if (const char *p = strstr(Title, Pattern)) {
if (Before)
*Before = cString(Title, p);
if (Match)
*Match = Pattern;
if (After)
*After = cString(p + strlen(Pattern));
return true;
}
}
return false;
}
static cString MakePatternFileName(const char *Pattern, const char *Title, const char *Episode, const char *File)
{
if (!Pattern || !Title || !File)
return NULL;
cString Before = "";
cString Match = "";
cString After = "";
if (MatchPattern(Pattern, Title, &Before, &Match, &After)) {
char *Result = strdup(File);
Result = strreplace(Result, TIMERMACRO_TITLE, Title);
Result = strreplace(Result, TIMERMACRO_EPISODE, Episode);
Result = strreplace(Result, TIMERMACRO_BEFORE, Before);
Result = strreplace(Result, TIMERMACRO_MATCH, Match);
Result = strreplace(Result, TIMERMACRO_AFTER, After);
return cString(Result, true);;
}
return NULL;
}
cTimer::cTimer(const cEvent *Event, const char *FileName, const cTimer *PatternTimer)
{ {
id = 0; id = 0;
startTime = stopTime = 0; startTime = stopTime = 0;
@ -89,12 +177,15 @@ cTimer::cTimer(const cEvent *Event)
deferred = 0; deferred = 0;
pending = inVpsMargin = false; pending = inVpsMargin = false;
flags = tfActive; flags = tfActive;
*pattern = 0;
*file = 0; *file = 0;
aux = NULL; aux = NULL;
remote = NULL; remote = NULL;
event = NULL; event = NULL;
if (Event->Vps() && Setup.UseVps) if (!PatternTimer || PatternTimer->HasFlags(tfVps)) {
SetFlags(tfVps); if (Event->Vps() && Setup.UseVps)
SetFlags(tfVps);
}
LOCK_CHANNELS_READ; LOCK_CHANNELS_READ;
channel = Channels->GetByChannelID(Event->ChannelID(), true); channel = Channels->GetByChannelID(Event->ChannelID(), true);
time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime(); time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime();
@ -112,11 +203,12 @@ cTimer::cTimer(const cEvent *Event)
stop = time->tm_hour * 100 + time->tm_min; stop = time->tm_hour * 100 + time->tm_min;
if (stop >= 2400) if (stop >= 2400)
stop -= 2400; stop -= 2400;
priority = Setup.DefaultPriority; priority = PatternTimer ? PatternTimer->Priority() : Setup.DefaultPriority;
lifetime = Setup.DefaultLifetime; lifetime = PatternTimer ? PatternTimer->Lifetime() : Setup.DefaultLifetime;
const char *Title = Event->Title(); if (!FileName)
if (!isempty(Title)) FileName = Event->Title();
Utf8Strn0Cpy(file, Event->Title(), sizeof(file)); if (!isempty(FileName))
Utf8Strn0Cpy(file, FileName, sizeof(file));
SetEvent(Event); SetEvent(Event);
} }
@ -156,6 +248,7 @@ cTimer& cTimer::operator= (const cTimer &Timer)
stop = Timer.stop; stop = Timer.stop;
priority = Timer.priority; priority = Timer.priority;
lifetime = Timer.lifetime; lifetime = Timer.lifetime;
strncpy(pattern, Timer.pattern, sizeof(pattern));
strncpy(file, Timer.file, sizeof(file)); strncpy(file, Timer.file, sizeof(file));
free(aux); free(aux);
aux = Timer.aux ? strdup(Timer.aux) : NULL; aux = Timer.aux ? strdup(Timer.aux) : NULL;
@ -178,20 +271,37 @@ int cTimer::Compare(const cListObject &ListObject) const
int r = t1 - t2; int r = t1 - t2;
if (r == 0) if (r == 0)
r = ti->priority - priority; r = ti->priority - priority;
if (IsPatternTimer() ^ ti->IsPatternTimer()) {
if (IsPatternTimer())
r = 1;
else
r = -1;
}
else if (IsPatternTimer() && ti->IsPatternTimer())
r = strcoll(Pattern(), ti->Pattern());
return r; return r;
} }
cString cTimer::PatternAndFile(void) const
{
if (IsPatternTimer())
return cString::sprintf("{%s}%s", pattern, file);
return file;
}
cString cTimer::ToText(bool UseChannelID) const cString cTimer::ToText(bool UseChannelID) const
{ {
strreplace(pattern, ':', '|');
strreplace(file, ':', '|'); strreplace(file, ':', '|');
cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux ? aux : ""); cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, *PatternAndFile(), aux ? aux : "");
strreplace(pattern, '|', ':');
strreplace(file, '|', ':'); strreplace(file, '|', ':');
return buffer; return buffer;
} }
cString cTimer::ToDescr(void) const cString cTimer::ToDescr(void) const
{ {
return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Id(), remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file); return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Id(), remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", *PatternAndFile());
} }
int cTimer::TimeToInt(int t) int cTimer::TimeToInt(int t)
@ -332,7 +442,18 @@ bool cTimer::Parse(const char *s)
} }
//TODO add more plausibility checks //TODO add more plausibility checks
result = ParseDay(daybuffer, day, weekdays); result = ParseDay(daybuffer, day, weekdays);
Utf8Strn0Cpy(file, filebuffer, sizeof(file)); char *fb = filebuffer;
if (*fb == '{') {
if (char *p = strchr(fb, '}')) {
*p = 0;
Utf8Strn0Cpy(pattern, fb + 1, sizeof(pattern));
strreplace(pattern, '|', ':');
fb = p + 1;
}
}
else
*pattern = 0;
Utf8Strn0Cpy(file, fb, sizeof(file));
strreplace(file, '|', ':'); strreplace(file, '|', ':');
LOCK_CHANNELS_READ; LOCK_CHANNELS_READ;
if (isnumber(channelbuffer)) if (isnumber(channelbuffer))
@ -404,6 +525,11 @@ time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
return mktime(&tm); return mktime(&tm);
} }
void cTimer::SetPattern(const char *Pattern)
{
Utf8Strn0Cpy(pattern, Pattern, sizeof(pattern));
}
void cTimer::SetFile(const char *File) void cTimer::SetFile(const char *File)
{ {
if (!isempty(File)) if (!isempty(File))
@ -451,6 +577,9 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
day = 0; day = 0;
} }
if (IsPatternTimer())
return false; // we only need to have start/stopTime initialized
if (t < deferred) if (t < deferred)
return false; return false;
deferred = 0; deferred = 0;
@ -483,6 +612,21 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
// gets 200 added to the FULLMATCH. // gets 200 added to the FULLMATCH.
if (channel->GetChannelID() == Event->ChannelID()) { if (channel->GetChannelID() == Event->ChannelID()) {
bool UseVps = HasFlags(tfVps) && Event->Vps(); bool UseVps = HasFlags(tfVps) && Event->Vps();
if (IsPatternTimer()) {
if (startswith(Pattern(), TIMERPATTERN_AVOID)) {
cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File());
if (*FileName) {
const char *p = strgetlast(*FileName, FOLDERDELIMCHAR);
if (DoneRecordingsPattern.Contains(p))
return tmNone;
}
else
return tmNone;
}
else if (!MatchPattern(Pattern(), Event->Title()))
return tmNone;
UseVps = false;
}
Matches(UseVps ? Event->Vps() : Event->StartTime(), true); Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
int overlap = 0; int overlap = 0;
if (UseVps) { if (UseVps) {
@ -499,8 +643,11 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
overlap = FULLMATCH; overlap = FULLMATCH;
else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime) else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime)
overlap = 0; overlap = 0;
else else {
overlap = (min(stopTime, Event->EndTime()) - max(startTime, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1); overlap = (min(stopTime, Event->EndTime()) - max(startTime, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
if (IsPatternTimer() && overlap > 0)
overlap = FULLMATCH;
}
} }
startTime = stopTime = 0; startTime = stopTime = 0;
if (Overlap) if (Overlap)
@ -542,8 +689,60 @@ void cTimer::SetId(int Id)
id = Id; id = Id;
} }
void cTimer::SpawnPatternTimer(const cEvent *Event, cTimers *Timers)
{
cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File());
isyslog("spawning timer %s for event %s", *ToDescr(), *Event->ToDescr());
cTimer *t = new cTimer(Event, FileName, this);
t->SetFlags(tfSpawned);
if (startswith(Pattern(), TIMERPATTERN_AVOID))
t->SetFlags(tfAvoid);
Timers->Add(t);
HandleRemoteTimerModifications(t);
}
bool cTimer::SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers)
{
bool TimersSpawned = false;
const cSchedule *Schedule = Schedules->GetSchedule(Channel());
if (Schedule && Schedule->Events()->First()) {
if (Schedule->Modified(scheduleState)) {
time_t Now = time(NULL);
for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
if (Matches(e) != tmNone) {
bool CheckThis = false;
bool CheckNext = false;
if (e->HasTimer()) // a matching event that already has a timer
CheckNext = true;
else if (e->EndTime() > Now) { // only look at events that have not yet ended
CheckThis = true;
CheckNext = true;
}
if (CheckThis) {
SpawnPatternTimer(e, Timers);
TimersSpawned = true;
}
if (CheckNext) {
// We also check the event immediately following this one:
e = Schedule->Events()->Next(e);
if (e && !e->HasTimer() && Matches(e) != tmNone) {
SpawnPatternTimer(e, Timers);
TimersSpawned = true;
}
}
if (CheckThis || CheckNext)
break;
}
}
}
}
return TimersSpawned;
}
bool cTimer::SetEventFromSchedule(const cSchedules *Schedules) bool cTimer::SetEventFromSchedule(const cSchedules *Schedules)
{ {
if (IsPatternTimer())
return SetEvent(NULL);
const cSchedule *Schedule = Schedules->GetSchedule(Channel()); const cSchedule *Schedule = Schedules->GetSchedule(Channel());
if (Schedule && Schedule->Events()->First()) { if (Schedule && Schedule->Events()->First()) {
if (Schedule->Modified(scheduleState)) { if (Schedule->Modified(scheduleState)) {
@ -707,7 +906,7 @@ void cTimer::Skip(void)
void cTimer::OnOff(void) void cTimer::OnOff(void)
{ {
if (IsSingleEvent()) if (IsSingleEvent() || IsPatternTimer())
InvFlags(tfActive); InvFlags(tfActive);
else if (day) { else if (day) {
day = 0; day = 0;
@ -718,6 +917,8 @@ void cTimer::OnOff(void)
else else
SetFlags(tfActive); SetFlags(tfActive);
SetEvent(NULL); SetEvent(NULL);
if (HasFlags(tfActive))
scheduleState = -1; // have pattern timers spawn if necessary
Matches(); // refresh start and end time Matches(); // refresh start and end time
} }
@ -831,7 +1032,7 @@ const cTimer *cTimers::GetNextActiveTimer(void) const
{ {
const cTimer *t0 = NULL; const cTimer *t0 = NULL;
for (const cTimer *ti = First(); ti; ti = Next(ti)) { for (const cTimer *ti = First(); ti; ti = Next(ti)) {
if (!ti->Remote()) { if (!ti->Remote() && !ti->IsPatternTimer()) {
ti->Matches(); ti->Matches();
if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0)) if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0))
t0 = ti; t0 = ti;
@ -882,8 +1083,22 @@ const cTimer *cTimers::UsesChannel(const cChannel *Channel) const
bool cTimers::SetEvents(const cSchedules *Schedules) bool cTimers::SetEvents(const cSchedules *Schedules)
{ {
bool TimersModified = false; bool TimersModified = false;
for (cTimer *ti = First(); ti; ti = Next(ti)) for (cTimer *ti = First(); ti; ti = Next(ti)) {
TimersModified |= ti->SetEventFromSchedule(Schedules); if (!ti->IsPatternTimer())
TimersModified |= ti->SetEventFromSchedule(Schedules);
}
return TimersModified;
}
bool cTimers::SpawnPatternTimers(const cSchedules *Schedules)
{
bool TimersModified = false;
for (cTimer *ti = First(); ti; ti = Next(ti)) {
if (ti->IsPatternTimer() && ti->Local()) {
if (ti->HasFlags(tfActive))
TimersModified |= ti->SpawnPatternTimers(Schedules, this);
}
}
return TimersModified; return TimersModified;
} }
@ -896,6 +1111,7 @@ bool cTimers::DeleteExpired(void)
while (ti) { while (ti) {
cTimer *next = Next(ti); cTimer *next = Next(ti);
if (!ti->Remote() && ti->Expired()) { if (!ti->Remote() && ti->Expired()) {
ti->SetEvent(NULL); // Del() doesn't call ~cTimer() right away, so this is necessary here
isyslog("deleting timer %s", *ti->ToDescr()); isyslog("deleting timer %s", *ti->ToDescr());
Del(ti); Del(ti);
TimersModified = true; TimersModified = true;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: timers.h 4.12 2019/05/23 09:47:19 kls Exp $ * $Id: timers.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __TIMERS_H #ifndef __TIMERS_H
@ -20,10 +20,14 @@ enum eTimerFlags { tfNone = 0x0000,
tfInstant = 0x0002, tfInstant = 0x0002,
tfVps = 0x0004, tfVps = 0x0004,
tfRecording = 0x0008, tfRecording = 0x0008,
tfSpawned = 0x0010,
tfAvoid = 0x0020,
tfAll = 0xFFFF, tfAll = 0xFFFF,
}; };
enum eTimerMatch { tmNone, tmPartial, tmFull }; enum eTimerMatch { tmNone, tmPartial, tmFull };
class cTimers;
class cTimer : public cListObject { class cTimer : public cListObject {
friend class cMenuEditTimer; friend class cMenuEditTimer;
private: private:
@ -40,13 +44,14 @@ private:
int stop; int stop;
int priority; int priority;
int lifetime; int lifetime;
mutable char pattern[NAME_MAX * 2 + 1]; // same size as 'file', to be able to initially fill 'pattern' with 'file' in the 'Edit timer' menu
mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long
char *aux; char *aux;
char *remote; char *remote;
const cEvent *event; const cEvent *event;
public: public:
cTimer(bool Instant = false, bool Pause = false, const cChannel *Channel = NULL); cTimer(bool Instant = false, bool Pause = false, const cChannel *Channel = NULL);
cTimer(const cEvent *Event); cTimer(const cEvent *Event, const char *FileName = NULL, const cTimer *PatternTimer = NULL);
cTimer(const cTimer &Timer); cTimer(const cTimer &Timer);
virtual ~cTimer(); virtual ~cTimer();
cTimer& operator= (const cTimer &Timer); cTimer& operator= (const cTimer &Timer);
@ -63,12 +68,14 @@ public:
int Stop(void) const { return stop; } int Stop(void) const { return stop; }
int Priority(void) const { return priority; } int Priority(void) const { return priority; }
int Lifetime(void) const { return lifetime; } int Lifetime(void) const { return lifetime; }
const char *Pattern(void) const { return pattern; }
const char *File(void) const { return file; } const char *File(void) const { return file; }
time_t FirstDay(void) const { return weekdays ? day : 0; } time_t FirstDay(void) const { return weekdays ? day : 0; }
const char *Aux(void) const { return aux; } const char *Aux(void) const { return aux; }
const char *Remote(void) const { return remote; } const char *Remote(void) const { return remote; }
bool Local(void) const { return !remote; } // convenience bool Local(void) const { return !remote; } // convenience
time_t Deferred(void) const { return deferred; } time_t Deferred(void) const { return deferred; }
cString PatternAndFile(void) const;
cString ToText(bool UseChannelID = false) const; cString ToText(bool UseChannelID = false) const;
cString ToDescr(void) const; cString ToDescr(void) const;
const cEvent *Event(void) const { return event; } const cEvent *Event(void) const { return event; }
@ -80,13 +87,17 @@ public:
bool DayMatches(time_t t) const; bool DayMatches(time_t t) const;
static time_t IncDay(time_t t, int Days); static time_t IncDay(time_t t, int Days);
static time_t SetTime(time_t t, int SecondsFromMidnight); static time_t SetTime(time_t t, int SecondsFromMidnight);
void SetPattern(const char *Pattern);
void SetFile(const char *File); void SetFile(const char *File);
bool IsPatternTimer(void) const { return *pattern; }
bool Matches(time_t t = 0, bool Directly = false, int Margin = 0) const; bool Matches(time_t t = 0, bool Directly = false, int Margin = 0) const;
eTimerMatch Matches(const cEvent *Event, int *Overlap = NULL) const; eTimerMatch Matches(const cEvent *Event, int *Overlap = NULL) const;
bool Expired(void) const; bool Expired(void) const;
time_t StartTime(void) const; time_t StartTime(void) const;
time_t StopTime(void) const; time_t StopTime(void) const;
void SetId(int Id); void SetId(int Id);
void SpawnPatternTimer(const cEvent *Event, cTimers *Timers);
bool SpawnPatternTimers(const cSchedules *Schedules, cTimers *Timers);
bool SetEventFromSchedule(const cSchedules *Schedules); bool SetEventFromSchedule(const cSchedules *Schedules);
bool SetEvent(const cEvent *Event); bool SetEvent(const cEvent *Event);
void SetRecording(bool Recording); void SetRecording(bool Recording);
@ -182,6 +193,7 @@ public:
const cTimer *GetNextActiveTimer(void) const; const cTimer *GetNextActiveTimer(void) const;
const cTimer *UsesChannel(const cChannel *Channel) const; const cTimer *UsesChannel(const cChannel *Channel) const;
bool SetEvents(const cSchedules *Schedules); bool SetEvents(const cSchedules *Schedules);
bool SpawnPatternTimers(const cSchedules *Schedules);
bool DeleteExpired(void); bool DeleteExpired(void);
void Add(cTimer *Timer, cTimer *After = NULL); void Add(cTimer *Timer, cTimer *After = NULL);
void Ins(cTimer *Timer, cTimer *Before = NULL); void Ins(cTimer *Timer, cTimer *Before = NULL);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.c 4.13 2020/11/22 13:32:05 kls Exp $ * $Id: tools.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include "tools.h" #include "tools.h"
@ -198,6 +198,12 @@ int strcountchr(const char *s, char c)
return n; return n;
} }
const char *strgetlast(const char *s, char c)
{
const char *p = strrchr(s, c);
return p ? p + 1 : s;
}
char *stripspace(char *s) char *stripspace(char *s)
{ {
if (s && *s) { if (s && *s) {

30
tools.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.h 4.18 2020/09/16 13:48:33 kls Exp $ * $Id: tools.h 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#ifndef __TOOLS_H #ifndef __TOOLS_H
@ -193,6 +193,33 @@ public:
static cString vsprintf(const char *fmt, va_list &ap); static cString vsprintf(const char *fmt, va_list &ap);
}; };
class cNullTerminate {
private:
char *p;
char c;
public:
cNullTerminate(void) {
p = NULL;
c = 0;
}
cNullTerminate(char *s) {
Set(s);
}
~cNullTerminate() {
if (p)
*p = c;
}
void Set(char *s) {
if (s) {
p = s;
c = *s;
*s = 0;
}
else
p = NULL;
}
};
ssize_t safe_read(int filedes, void *buffer, size_t size); ssize_t safe_read(int filedes, void *buffer, size_t size);
ssize_t safe_write(int filedes, const void *buffer, size_t size); ssize_t safe_write(int filedes, const void *buffer, size_t size);
void writechar(int filedes, char c); void writechar(int filedes, char c);
@ -206,6 +233,7 @@ 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! 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. 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'. int strcountchr(const char *s, char c); ///< returns the number of occurrences of 'c' in 's'.
const char *strgetlast(const char *s, char c); // returns the part of 's' after the last occurrence of 'c', or 's' if there is no 'c'.
inline char *skipspace(const char *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 if ((uchar)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible

7
vdr.1
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.1 4.4 2018/04/10 13:58:06 kls Exp $ .\" $Id: vdr.1 5.1 2020/12/26 15:49:01 kls Exp $
.\" .\"
.TH vdr 1 "15 Apr 2018" "2.4" "Video Disk Recorder" .TH vdr 1 "15 Apr 2018" "2.4" "Video Disk Recorder"
.SH NAME .SH NAME
@ -305,6 +305,11 @@ The actual data files of a recording.
Contains all current EPG data. Can be used for external processing and will Contains all current EPG data. Can be used for external processing and will
also be read at program startup to have the full EPG data available immediately. also be read at program startup to have the full EPG data available immediately.
.TP .TP
.I donerecs.data
Contains the names of recordings that have been done by pattern timers with '@'
as the first character of the pattern. File names are appended to this file after
a recording has finished, and the entire file is read upon startup of VDR.
.TP
.I .update .I .update
If this file is present in the video directory, its last modification time will If this file is present in the video directory, its last modification time will
be used to trigger an update of the list of recordings in the "Recordings" menu. be used to trigger an update of the list of recordings in the "Recordings" menu.

43
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.5 4.8 2018/04/10 13:58:16 kls Exp $ .\" $Id: vdr.5 5.1 2020/12/26 15:49:01 kls Exp $
.\" .\"
.TH vdr 5 "15 Apr 2018" "2.4" "Video Disk Recorder Files" .TH vdr 5 "15 Apr 2018" "2.4" "Video Disk Recorder Files"
.SH NAME .SH NAME
@ -317,10 +317,12 @@ The individual bits in this field have the following meaning:
.TS .TS
tab (@); tab (@);
l l. l l.
\fB1\fR@the timer is active (and will record if it hits) \fB0x0001\fR@the timer is active (and will record if it hits)
\fB2\fR@this is an instant recording timer \fB0x0002\fR@this is an instant recording timer
\fB4\fR@this timer uses VPS \fB0x0004\fR@this timer uses VPS
\fB8\fR@this timer is currently recording (may only be up-to-date with SVDRP) \fB0x0008\fR@this timer is currently recording (may only be up-to-date with SVDRP)
\fB0x0010\fR@this timer was spawned from a pattern timer
\fB0x0020\fR@this timer will store the recording's name in donerecs.data
.TE .TE
All other bits are reserved for future use. All other bits are reserved for future use.
@ -425,6 +427,37 @@ by the title and episode information from the EPG data at the time of
recording (if that data is available). If at the time of recording either recording (if that data is available). If at the time of recording either
of these cannot be determined, \fBTITLE\fR will default to the channel name, and of these cannot be determined, \fBTITLE\fR will default to the channel name, and
\fBEPISODE\fR will default to a blank. \fBEPISODE\fR will default to a blank.
The file name can be prepended with a pattern, enclosed in curly braces, as in
{Columbo}Movies~TITLE
which makes this a "pattern timer". A pattern timer records every event on the
given channel where the title contains the pattern (case sensitive).
The following special characters can be used in a pattern:
.TS
tab (;);
l l.
\fB^\fR;anchor to the beginning of the event's title
\fB$\fR;anchor to the end of the event's title
\fB*\fR;match every event
\fB@\fR;avoid duplicate recordings
.TE
If \fB@\fR is used, it must be the very first character of the pattern.
If both \fB@\fR and \fB^\fR are used, \fB@\fR must come first.
If \fB*\fR is used, it must be the only character in the pattern and may only be
prepended with \fB@\fR.
In addition to TITLE and EPISODE you can use the following macros to compose the file
name (the curly braces are part of the macros):
.TS
tab (@);
l l.
{<}@everything before the matching pattern
{>}@everything after the matching pattern
{=}@the matching pattern itself (just for completeness)
.TE
.TP .TP
.B Auxiliary data .B Auxiliary data
An arbitrary string that can be used by external applications to store any An arbitrary string that can be used by external applications to store any

10
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.tvdr.de * The project's page is at http://www.tvdr.de
* *
* $Id: vdr.c 4.34 2020/11/20 13:49:58 kls Exp $ * $Id: vdr.c 5.1 2020/12/26 15:49:01 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -784,6 +784,7 @@ int main(int argc, char *argv[])
KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true); KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
Folders.Load(AddDirectory(ConfigDirectory, "folders.conf")); Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
CamResponsesLoad(AddDirectory(ConfigDirectory, "camresponses.conf"), true); CamResponsesLoad(AddDirectory(ConfigDirectory, "camresponses.conf"), true);
DoneRecordingsPattern.Load(AddDirectory(CacheDirectory, "donerecs.data"));
if (!*cFont::GetFontFileName(Setup.FontOsd)) { if (!*cFont::GetFontFileName(Setup.FontOsd)) {
const char *msg = "no fonts available - OSD will not show any text!"; const char *msg = "no fonts available - OSD will not show any text!";
@ -1098,15 +1099,20 @@ int main(int argc, char *argv[])
static cStateKey TimersStateKey; static cStateKey TimersStateKey;
cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey); cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey);
{ {
LOCK_CHANNELS_READ; // Channels are needed for spawning pattern timers!
// Assign events to timers: // Assign events to timers:
static cStateKey SchedulesStateKey; static cStateKey SchedulesStateKey;
if (TimersStateKey.StateChanged()) if (TimersStateKey.StateChanged())
SchedulesStateKey.Reset(); // we assign events if either the Timers or the Schedules have changed SchedulesStateKey.Reset(); // we assign events if either the Timers or the Schedules have changed
bool TimersModified = false; bool TimersModified = false;
if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) { if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) {
Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll); Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll); // setting events shall not trigger a remote timer poll...
if (Timers->SetEvents(Schedules)) if (Timers->SetEvents(Schedules))
TimersModified = true; TimersModified = true;
if (Timers->SpawnPatternTimers(Schedules)) {
StateKeySVDRPRemoteTimersPoll.Reset(); // ...but spawning new timers must!
TimersModified = true;
}
SchedulesStateKey.Remove(); SchedulesStateKey.Remove();
} }
TimersStateKey.Remove(TimersModified); // we need to remove the key here, so that syncing StateKeySVDRPRemoteTimersPoll takes effect! TimersStateKey.Remove(TimersModified); // we need to remove the key here, so that syncing StateKeySVDRPRemoteTimersPoll takes effect!