summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndreas Brachold <vdr07@deltab.de>2008-12-07 07:53:54 +0000
committerAndreas Brachold <vdr07@deltab.de>2008-12-07 07:53:54 +0000
commitf98ae2c03cd2ab4a47e7335e1adb5a97e4a1ccea (patch)
tree252e3d28ede025d7d6ef0844c2510fed2439b929 /lib
parent8c90ecd98b1b26d9728dea5a2ad0d66fa480ec60 (diff)
downloadxxv-f98ae2c03cd2ab4a47e7335e1adb5a97e4a1ccea.tar.gz
xxv-f98ae2c03cd2ab4a47e7335e1adb5a97e4a1ccea.tar.bz2
* RECORDS: New database 31
- support video directory per recorder (REQUEST #4306 ) (this directory should now defined with vdrlist/vdredit) * RECORDS: calc cut marks, display length of by cut recording * update-xxv: add file check, avoid missing upgrade-xxv-db.sql * update locale de
Diffstat (limited to 'lib')
-rw-r--r--lib/XXV/MODULES/RECORDS.pm523
-rw-r--r--lib/XXV/MODULES/REMOTE.pm2
-rw-r--r--lib/XXV/MODULES/STREAM.pm33
-rw-r--r--lib/XXV/MODULES/SVDRP.pm46
-rw-r--r--lib/XXV/MODULES/TIMERS.pm2
5 files changed, 410 insertions, 196 deletions
diff --git a/lib/XXV/MODULES/RECORDS.pm b/lib/XXV/MODULES/RECORDS.pm
index 325c60f..45ca2be 100644
--- a/lib/XXV/MODULES/RECORDS.pm
+++ b/lib/XXV/MODULES/RECORDS.pm
@@ -51,12 +51,6 @@ sub module {
type => 'integer',
required => gettext("This is required!"),
},
- videodir => {
- description => gettext('Directory where recordings are stored'),
- default => '/var/lib/video',
- type => 'dir',
- required => gettext("This is required!"),
- },
previewbinary => {
description => gettext('Location of used program to produce thumbnails on your system.'),
default => '/usr/bin/mplayer',
@@ -297,7 +291,7 @@ sub _init {
return 0;
}
- my $version = 29; # Must be increment if rows of table changed
+ my $version = 31; # Must be increment if rows of table changed
# this tables hasen't handmade user data,
# therefore old table could dropped if updated rows
if(!tableUpdated($self->{dbh},'RECORDS',$version,1)) {
@@ -311,10 +305,11 @@ sub _init {
RecordId int unsigned not NULL,
RecordMD5 varchar(32) NOT NULL,
Path text NOT NULL,
- Prio tinyint NOT NULL,
- Lifetime tinyint NOT NULL,
+ priority tinyint NOT NULL,
+ lifetime tinyint NOT NULL,
State tinyint NOT NULL,
FileSize int unsigned default '0',
+ cutlength int unsigned default '0',
Marks text,
Type enum('TV', 'RADIO', 'UNKNOWN') default 'TV',
preview text NOT NULL,
@@ -345,27 +340,7 @@ sub _init {
return 0;
}
- my $updatefile = sprintf("%s/.update",$self->{videodir});
- if( -r $updatefile) {
- my $inotify = new Linux::Inotify2
- or panic sprintf("Unable to create new inotify object: %s",$!);
-
- if($inotify) {
- # Bind watch to event::io
- Event->io(
- fd => $inotify->fileno,
- poll => 'r',
- cb => sub { $inotify->poll }
- );
- # watch update file
- $inotify->watch(
- $updatefile,
- IN_ALL_EVENTS,
- sub { my $e = shift; $self->_notify_readData($e); }
- );
- $self->{inotify} = 'active';
- }
- }
+ $self->_watch_updatefile();
# Interval to read recordings and put to DB
Event->timer(
@@ -374,13 +349,13 @@ sub _init {
cb => sub {
my $forceUpdate = ($self->{countReading} % ( $self->{fullreading} * 60 / $self->{reading} ) == 0);
if($forceUpdate || (time - $self->{lastupdate}) > ($self->{reading}/2) ) {
- $self->readData(undef,undef,undef,$forceUpdate);
+ $self->_readData(undef,undef,$forceUpdate);
$self->{lastupdate} = time;
}
$self->{countReading} += 1;
},
);
- $self->readData(undef,undef,undef);
+ $self->_readData(undef,undef,undef);
$self->{countReading} += 1;
$self->{lastupdate} = time;
return 1;
@@ -389,17 +364,59 @@ sub _init {
1;
}
+sub _watch_updatefile {
+ my $self = shift || return error('No object defined!');
+
+ my $hostlist = $self->{svdrp}->list_unique_recording_hosts();
+ foreach my $vid (@$hostlist) {
+
+ next if(exists $self->{inotify}
+ && exists $self->{inotify}->{$vid});
+
+ my $videodirectory = $self->{svdrp}->videodirectory($vid);
+ unless($videodirectory && -d $videodirectory) {
+ my $hostname = $self->{svdrp}->hostname($vid);
+ error(sprintf("Missing video directory on %s!",$hostname));
+ next;
+ }
+ my $updatefile = sprintf("%s/.update",$videodirectory);
+ if( -r $updatefile) {
+ $self->{inotify}->{$vid}->{handle} = new Linux::Inotify2
+ or panic sprintf("Unable to create new inotify object: %s",$!);
+
+ if($self->{inotify}->{$vid}->{handle}) {
+ # Bind watch to event::io
+ $self->{inotify}->{$vid}->{event} = Event->io(
+ fd => $self->{inotify}->{$vid}->{handle}->fileno,
+ poll => 'r',
+ cb => sub { $self->{inotify}->{$vid}->{handle}->poll }
+ );
+ # watch update file
+ $self->{inotify}->{$vid}->{handle}->watch(
+ $updatefile,
+ IN_ALL_EVENTS,
+ sub {
+ my $e = shift;
+ $self->_notify_updatefile($e, $vid);
+ }
+ );
+ }
+ }
+ }
+}
# ------------------
# Callback to reread data if /video/.update changed by VDR
# trigged by file notifcation from inotify
-sub _notify_readData {
+sub _notify_updatefile {
# ------------------
my $self = shift || return error('No object defined!');
my $e = shift;
- lg sprintf "notify events for %s:%d received: %x", $e->fullname, $e->cookie, $e->mask;
+ my $vid = shift;
+
+ lg sprintf "On recorder %d notify events for %s:%d received: %x", $vid, $e->fullname, $e->cookie, $e->mask;
if((time - $self->{lastupdate}) > 3 # Only if last update prior 3 seconds (avoid callback chill)
- && $self->readData()) {
+ && $self->_readData()) {
$self->{lastupdate} = time;
@@ -412,7 +429,7 @@ sub _notify_readData {
after => $after,
cb => sub {
if((time - $self->{lastupdate}) >= ($after - 30)) {
- if($self->readData()) {
+ if($self->_readData(undef, undef, undef, $vid)) {
$self->{lastupdate} = time;
}
$_[0]->w->cancel;
@@ -476,6 +493,7 @@ sub parseData {
sub scandirectory {
# ------------------
my $self = shift || return error('No object defined!');
+ my $directory = shift;
my $typ = shift;
#my $enc = find_encoding($self->{charset});
@@ -506,7 +524,7 @@ sub scandirectory {
# convert path to title
my $title = dirname($path);
- $title =~ s/^$self->{videodir}//g;
+ $title =~ s/^$directory//g;
$title =~ s/^\///g;
# $rec->{title} = $enc->decode($self->converttitle($title));
$rec->{title} = $self->converttitle($title);
@@ -528,61 +546,100 @@ sub scandirectory {
follow => 1,
follow_skip => 2,
},
- $self->{videodir}
+ $directory
);
return $files;
}
# ------------------
-sub readData {
+sub _readData {
# ------------------
- my $self = shift || return error('No object defined!');
- my $console = shift;
- my $config = shift;
- my $waiter = shift;
- # Read manual or Once at day, make full scan
- my $forceUpdate = shift;
-
- # Read recording over SVDRP
- my ($lstr,$error) = $self->{svdrp}->command('lstr');
+ my $self = shift || return error('No object defined!');
+ my $console = shift;
+ my $waiter = shift;
+ my $forceUpdate = shift; # Read manual or Once at day, make full scan
+ my $onlyvid = shift;
+
+ my $outdatedRecordings;
+ if($onlyvid) {
+ my $sth = $self->{dbh}->prepare('SELECT RecordMD5,CONCAT_WS("~",e.title,e.subtitle,UNIX_TIMESTAMP(e.starttime)) as hash FROM RECORDS as r,OLDEPG as e where r.eventid = e.eventid and vid = ?');
+ if(!$sth->execute($onlyvid)) {
+ con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
+ }
+ $outdatedRecordings = $sth->fetchall_hashref('hash');
+ } else {
+ my $sth = $self->{dbh}->prepare('SELECT RecordMD5,CONCAT_WS("~",e.title,e.subtitle,UNIX_TIMESTAMP(e.starttime)) as hash FROM RECORDS as r,OLDEPG as e where r.eventid = e.eventid');
+ if(!$sth->execute()) {
+ con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
+ }
+ $outdatedRecordings = $sth->fetchall_hashref('hash');
+ }
+
+ if($forceUpdate) {
+ $self->{dbh}->do('DELETE FROM RECORDS');
+ $self->{keywords}->removesource('recording');
+ }
+ my $err = [];
+ my $insertedData = 0;
+ my $updatedState = 0;
+ my $removedData = 0;
+ my @todel;
+
+ my $hostlist;
+ $hostlist = [ $onlyvid ] if($onlyvid);
+ $hostlist = $self->{svdrp}->list_unique_recording_hosts() unless($hostlist);
+ # Read recording over SVDRP
+ foreach my $vid (@$hostlist) {
+ my ($lstr,$error) = $self->{svdrp}->command('lstr',$vid);
+ my $hostname = $self->{svdrp}->hostname($vid);
my $vdata = [ grep(/^250/, @$lstr) ];
-
- unless(scalar @$vdata) {
- # Delete old Records
- $self->{dbh}->do('DELETE FROM RECORDS');
- $self->{keywords}->removesource('recording');
-
- my $msg = [gettext('No recordings available!'), $error ];
- $console->err($msg) if($console);
- return;
+ if($error) {
+ if($console) {
+ my $msg = [
+ sprintf(gettext("Can't read recordings from %s !"),$hostname),
+ $error
+ ];
+ $console->err($msg);
+ }
+ next;
}
# Get state from used harddrive (/video)
- my ($disk,$error2) = $self->{svdrp}->command('stat disk');
- my ($total, $totalUnit, $free, $freeUnit, $percent);
- my $totalDuration = 0;
- my $totalSpace = 0;
-
+ my ($disk,$error2) = $self->{svdrp}->command('stat disk',$vid);
if(!$error2 and $disk->[1] and $disk->[1] =~ /^250/s) {
#250 473807MB 98028MB 79%
- ($total, $totalUnit, $free, $freeUnit, $percent)
+ my ($total, $totalUnit, $free, $freeUnit, $percent)
= $disk->[1] =~ /^250[\-|\s](\d+)(\S+)\s+(\d+)(\S+)\s+(\S+)/s;
+ error sprintf("Unsupported unit '%s' to calc free capacity",$freeUnit) unless($freeUnit eq 'MB');
- $self->{CapacityMessage} = sprintf(gettext("Used %s, total %s%s, free %s%s"),$percent, dot1000($total), $totalUnit, dot1000($free), $freeUnit);
- $self->{CapacityPercent} = int($percent);
-
+ $self->{Capacity}->{$vid}->{Message} = sprintf(gettext("Used %s, total %s%s, free %s%s on '%s'"),$percent, dot1000($total), $totalUnit, dot1000($free), $freeUnit, $hostname);
+ $self->{Capacity}->{$vid}->{Free} = $free;
+ $self->{Capacity}->{$vid}->{Duration} = 0;
+ $self->{Capacity}->{$vid}->{FileSize} = 0;
} else {
- error("Couldn't get disc state : ".join("\n", @$disk));
- $self->{CapacityMessage} = gettext("Unknown disc capacity!");
- $self->{CapacityPercent} = 0;
+ error(sprintf("Couldn't get disc state from %s\n%s", $hostname, join("\n", @$disk)));
+ $self->{Capacity}->{$vid}->{Message} = sprintf(gettext("Unknown disc capacity on '%s'!"),$hostname);
+ $self->{Capacity}->{$vid}->{Free} = 0;
+ $self->{Capacity}->{$vid}->{Duration} = 0;
+ $self->{Capacity}->{$vid}->{FileSize} = 0;
+ }
+ # There none recordings present
+ unless(scalar @$vdata) {
+ unless($forceUpdate) {
+ # then delete old recordings
+ my $sth = $self->{dbh}->prepare('DELETE FROM RECORDS as r,OLDEPG as e where e.vid = ? and r.eventid = e.eventid');
+ if(!$sth->execute($vid)) {
+ con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
+ next;
+ }
+ }
+ next;
}
- my @merkMD5;
- my $insertedData = 0;
- my $updatedState = 0;
+
+
my $l = 0;
- my $err = [];
my $vdrData = $self->parseData($vdata);
@@ -591,10 +648,7 @@ sub readData {
if(ref $console && ref $waiter);
my $db_data;
- if($forceUpdate) {
- $self->{dbh}->do('DELETE FROM RECORDS');
- $self->{keywords}->removesource('recording');
- } else {
+ unless($forceUpdate) {
# read database for compare with vdr data
my $sql = qq|SELECT SQL_CACHE r.eventid as eventid, r.RecordId as id,
UNIX_TIMESTAMP(e.starttime) as starttime,
@@ -608,11 +662,17 @@ sub readData {
r.Marks as marks,
r.RecordMD5
from RECORDS as r,OLDEPG as e
- where r.eventid = e.eventid |;
- $db_data = $self->{dbh}->selectall_hashref($sql, 'hash');
-
+ where e.vid = ? and r.eventid = e.eventid |;
+ my $sth = $self->{dbh}->prepare($sql);
+ if(!$sth->execute($vid)) {
+ error sprintf("Couldn't execute query: %s.",$sth->errstr);
+ $console->err(sprintf(gettext("Couldn't query recordings from database!")))
+ if($console);
+ next;
+ }
+ $db_data = $sth->fetchall_hashref('hash');
lg sprintf( 'Compare recording database with data from %s : %d / %d',
- $self->{svdrp}->hostname(),
+ $hostname,
scalar keys %$db_data,scalar keys %$vdrData );
}
@@ -667,11 +727,12 @@ sub readData {
$updatedState++;
}
}
-
- $totalDuration += $db_data->{$h}->{duration};
- $totalSpace += $db_data->{$h}->{FileSize};
- push(@merkMD5,$db_data->{$h}->{RecordMD5});
+
+ $self->{Capacity}->{$vid}->{Duration} += $db_data->{$h}->{duration};
+ $self->{Capacity}->{$vid}->{FileSize} += $db_data->{$h}->{FileSize};
+
+ delete $outdatedRecordings->{$h};
# delete updated rows from hash
delete $db_data->{$h};
@@ -683,19 +744,25 @@ sub readData {
# Read VideoDir only at first call
unless($files) {
- $files = $self->scandirectory('rec');
+ my $videodirectory = $self->{svdrp}->videodirectory($vid);
+ unless($videodirectory && -d $videodirectory) {
+ $console->err(sprintf(gettext("Missing video directory on %s!"),$hostname))
+ if($console);
+ last;
+ }
+ $files = $self->scandirectory( $videodirectory, 'rec');
}
unless($files && keys %{$files}) {
last;
}
- my $info = $self->analyze($files,$event);
+ my $info = $self->analyze($vid,$files,$event);
if(ref $info eq 'HASH') {
- $totalDuration += $info->{Duration};
- $totalSpace += $info->{FileSize};
+ $self->{Capacity}->{$vid}->{Duration} += $info->{Duration};
+ $self->{Capacity}->{$vid}->{FileSize} += $info->{FileSize};
if($self->insert($info)) {
- push(@merkMD5,$info->{RecordMD5});
+ delete $outdatedRecordings->{$h};
$insertedData++;
$self->{keywords}->insert('recording',$info->{RecordMD5},$info->{keywords});
@@ -711,37 +778,63 @@ sub readData {
if($forceUpdate) {
foreach my $md5 (keys %{$files}) {
- push(@{$err},sprintf(gettext("Recording '%s' without id or unique title and date from VDR!"),$files->{$md5}->{title}));
+ push(@{$err},sprintf(gettext("Recording '%s' without id or unique title and date from '%s'!"),$files->{$md5}->{title},$hostname));
}
}
if($db_data && scalar keys %$db_data > 0) {
- my @todel;
foreach my $t (keys %{$db_data}) {
+ delete $outdatedRecordings->{$t};
push(@todel,$db_data->{$t}->{RecordMD5});
}
- my $sql = sprintf('DELETE FROM RECORDS WHERE RecordMD5 IN (%s)', join(',' => ('?') x @todel));
- my $sth = $self->{dbh}->prepare($sql);
- $sth->execute(@todel)
- or return con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
-
- $self->{keywords}->remove('recording',\@todel);
}
- my $removedData = $db_data ? scalar keys %$db_data : 0;
+ $removedData += $db_data ? scalar keys %$db_data : 0;
+ }
+
debug sprintf 'Finish .. %d recordings inserted, %d recordings updated, %d recordings removed',
$insertedData, $updatedState, $removedData;
- error sprintf("Unsupported unit '%s' to calc free capacity",$freeUnit) unless($freeUnit eq 'MB');
+ map { push(@todel,$outdatedRecordings->{$_}->{RecordMD5}); } keys %{$outdatedRecordings};
+ if(!$forceUpdate && scalar @todel) {
+ my $sql = sprintf('DELETE FROM RECORDS WHERE RecordMD5 IN (%s)', join(',' => ('?') x @todel));
+ my $sth = $self->{dbh}->prepare($sql);
+ $sth->execute(@todel)
+ or return con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
+
+ $self->{keywords}->remove('recording',\@todel);
+ }
+
# use store capacity and recordings length to calc free capacity
+ my $totalDuration = 0;
+ my $totalSpace = 0;
+ my $totalFree = 0;
+ my $Message;
+
+ foreach my $vid (keys %{$self->{Capacity}}) {
+ push(@$Message,$self->{Capacity}->{$vid}->{Message});
+ $totalDuration += $self->{Capacity}->{$vid}->{Duration};
+ $totalSpace += $self->{Capacity}->{$vid}->{FileSize};
+ $totalFree += $self->{Capacity}->{$vid}->{Free};
+ }
+
+ $self->{CapacityMessage} = join("\n",@$Message);
$self->{CapacityTotal} = $totalDuration;
if($totalSpace > 1) {
- $self->{CapacityFree} = int(($free * $totalDuration) / $totalSpace);
+ $self->{CapacityFree} = int(($totalFree * $totalDuration) / $totalSpace);
} else {
- $self->{CapacityFree} = int($free * 3600 / 2000); # use 2GB at one hour as base
+ $self->{CapacityFree} = int($totalFree * 3600 / 2000); # use 2GB at one hour as base
+ }
+ $self->{CapacityPercent} = ($totalSpace * 100 / ($totalFree + $totalSpace));
+
+
+
+ # alte PreviewDirs loeschen
+ foreach my $md5 (@todel) {
+ my $dir = sprintf('%s/%s_shot', $self->{previewimages} , $md5);
+ lg sprintf("Remove old preview files : '%s'",$dir);
+ deleteDir($dir);
}
- $self->{CapacityPercent} = ($totalSpace * 100 / ($free + $totalSpace))
- unless($self->{CapacityPercent});
# Previews im fork erzeugen
if(scalar @{$self->{JOBS}}) {
@@ -774,15 +867,6 @@ sub readData {
}
}
- # alte PreviewDirs loeschen
- foreach my $dir (glob(sprintf('%s/*_shot', $self->{previewimages}))) {
- my $basedir = basename($dir);
- unless(grep(sprintf('%s_shot',$_) eq $basedir, @merkMD5)) {
- lg sprintf("Remove old preview files : '%s'",$dir);
- deleteDir($dir);
- }
- }
-
# Delete all old EPG entrys
if($forceUpdate || $removedData) {
my $sqldeleteEvents = qq|
@@ -803,9 +887,9 @@ DELETE FROM OLDEPG
$console->start() if(ref $waiter && ref $console);
if(scalar @{$err} == 0) {
- $console->message(sprintf(gettext("Write %d recordings to the database."), scalar @merkMD5)) if(ref $console);
+ $console->message(sprintf(gettext("Write %d recordings to the database."), ($insertedData + $updatedState))) if(ref $console);
} else {
- unshift(@{$err}, sprintf(gettext("Write %d recordings to the database. Couldn't assign %d recordings."), scalar @merkMD5 , scalar @{$err}));
+ unshift(@{$err}, sprintf(gettext("Write %d recordings to the database. Couldn't assign %d recordings."), ($insertedData + $updatedState) , scalar @{$err}));
con_err($console,$err);
}
return (scalar @{$err} == 0);
@@ -847,7 +931,7 @@ sub refresh {
con_msg($console,gettext("Get information on recordings ..."));
}
- if($self->readData($console,$config, $waiter,'force')) {
+ if($self->_readData($console,$waiter,'force')) {
$console->redirect({url => '?cmd=rlist', wait => 1})
if(ref $console and $console->typ eq 'HTML');
@@ -866,8 +950,8 @@ sub insert {
my $sth = $self->{dbh}->prepare(
qq|
REPLACE INTO RECORDS
- (eventid, RecordId, RecordMD5, Path, Prio, Lifetime, State, FileSize, Marks, Type, preview, aux, addtime )
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,NOW())
+ (eventid, RecordId, RecordMD5, Path, priority, lifetime, State, FileSize, cutlength, Marks, Type, preview, aux, addtime )
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())
|);
$attr->{Marks} = ""
@@ -878,10 +962,11 @@ sub insert {
$attr->{RecordId},
$attr->{RecordMD5},
$attr->{Path},
- $attr->{Prio},
- $attr->{Lifetime},
+ $attr->{priority},
+ $attr->{lifetime},
$attr->{State},
$attr->{FileSize},
+ $attr->{cutlength},
$attr->{Marks},
$attr->{Type},
$attr->{preview},
@@ -939,6 +1024,7 @@ sub _updateFileSize {
sub analyze {
# ------------------
my $self = shift || return error('No object defined!');
+ my $vid = shift; # ID of Video disk recorder
my $files = shift; # Hash with md5 and path to recording
my $recattr = shift;
@@ -950,7 +1036,7 @@ sub analyze {
return 0;
}
- my $event = $self->SearchEpgId( $recattr->{starttime}, $info->{duration}, $recattr->{title}, $info->{channel} );
+ my $event = $self->SearchEpgId( $vid, $recattr->{starttime}, $info->{duration}, $recattr->{title}, $info->{channel} );
if($event) {
my $id = $event->{eventid};
$event->{addtime} = time;
@@ -972,7 +1058,7 @@ sub analyze {
$title = join('~',@t);
}
- $event = $self->createOldEventId($recattr->{id}, $recattr->{starttime}, $info->{duration}, $title, $subtitle, $info);
+ $event = $self->createOldEventId($vid, $recattr->{id}, $recattr->{starttime}, $info->{duration}, $title, $subtitle, $info);
unless($event) {
error sprintf("Couldn't create event!: '%s' !",$recattr->{id});
return 0;
@@ -990,12 +1076,13 @@ sub analyze {
Duration => $info->{duration},
Start => $recattr->{starttime},
Path => $info->{path},
- Prio => $info->{Prio},
- Lifetime => $info->{Lifetime},
+ priority => $info->{priority},
+ lifetime => $info->{lifetime},
eventid => $event->{eventid},
Type => $info->{type} || 'UNKNOWN',
State => $recattr->{state},
FileSize => $info->{FileSize},
+ cutlength => $info->{cutlength},
aux => $info->{aux},
keywords => $info->{keywords}
};
@@ -1034,14 +1121,15 @@ sub videoInfo {
$info->{RecordMD5} = $md5;
$info->{path} = $rec->{path};
- $info->{Prio} = $rec->{priority};
- $info->{Lifetime} = $rec->{lifetime};
+ $info->{priority} = $rec->{priority};
+ $info->{lifetime} = $rec->{lifetime};
$info->{duration} = $self->_recordinglength($rec->{path});
$info->{FileSize} = $self->_recordingCapacity($rec->{files},
($info->{duration} * 8 * $self->{framerate}));
- my $marks = $self->readmarks($rec->{path});
+ my $marks = $self->_readmarks($rec->{path});
map { $info->{$_} = $marks->{$_}; } keys %{$marks};
+ $info->{cutlength} = $self->_calcmarks($info->{marks} , $info->{duration});
delete $files->{$md5}; # remove from hash, avoid double lookup
return $info;
@@ -1054,7 +1142,7 @@ sub videoInfo {
#-------------------------------------------------------------------------------
# get cut marks from marks.vdr
-sub readmarks {
+sub _readmarks {
my $self = shift || return error('No object defined!');
my $path = shift || return error ('No recording path defined!');
@@ -1076,6 +1164,35 @@ sub readmarks {
return $status;
}
+
+sub _calcmarks {
+ my $self = shift;
+ my $marks = shift;
+ my $duration = shift;
+
+ unless ($marks) {
+ return $duration;
+ }
+
+ my $frames = 0;
+ for (my $i = 0; $i < (scalar(@$marks)); $i += 2) {
+ my ($h,$m,$s,$f) = split /[:.]/,$marks->[$i];
+ my $startframe = ($h * 3600 + $m * 60 + $s)* ($self->{framerate}) + $f;
+
+ if ($marks->[$i+1]) {
+ my ($h,$m,$s,$f) = split /[:.]/,$marks->[$i + 1];
+ my $endframe = ($h * 3600 + $m * 60 + $s)* ($self->{framerate}) + $f;
+ $frames += $endframe - $startframe;
+ } else {
+ $frames += ($duration * ($self->{framerate})) - $startframe;
+ last;
+ }
+ }
+
+ return $frames / $self->{framerate};
+
+}
+
#-------------------------------------------------------------------------------
# get information about recording from info.vdr
sub readinfo {
@@ -1397,6 +1514,7 @@ sub _mark2frames{
sub SearchEpgId {
# ------------------
my $self = shift || return error('No object defined!');
+ my $vid = shift; # ID of Video disk recorder
my $start = shift || return error('No start time defined!');
my $dur = shift || return 0;
my $title = shift || return error('No title defined!');
@@ -1407,19 +1525,21 @@ sub SearchEpgId {
if($channel && $channel ne "") {
$sth = $self->{dbh}->prepare(
qq|SELECT SQL_CACHE * FROM OLDEPG WHERE
- UNIX_TIMESTAMP(starttime) >= ?
+ vid = ?
+ AND UNIX_TIMESTAMP(starttime) >= ?
AND UNIX_TIMESTAMP(starttime)+duration <= ?
AND CONCAT_WS("~",title,subtitle) = ?
AND channel_id = ?|);
- $sth->execute($start,$bis,$title,$channel)
+ $sth->execute($vid,$start,$bis,$title,$channel)
or return error sprintf("Couldn't execute query: %s.",$sth->errstr);
} else {
$sth = $self->{dbh}->prepare(
qq|SELECT SQL_CACHE * FROM OLDEPG WHERE
- UNIX_TIMESTAMP(starttime) >= ?
+ vid = ?
+ AND UNIX_TIMESTAMP(starttime) >= ?
AND UNIX_TIMESTAMP(starttime)+duration <= ?
AND CONCAT_WS("~",title,subtitle) = ?|);
- $sth->execute($start,$bis,$title)
+ $sth->execute($vid,$start,$bis,$title)
or return error sprintf("Couldn't execute query: %s.",$sth->errstr);
}
return 0 if(!$sth);
@@ -1432,6 +1552,7 @@ qq|SELECT SQL_CACHE * FROM OLDEPG WHERE
sub createOldEventId {
# ------------------
my $self = shift || return error('No object defined!');
+ my $vid = shift; # ID of Video disk recorder
my $id = shift || return error('No eventid defined!');
my $start = shift || return error('No start time defined!');
my $duration = shift || 0;
@@ -1457,11 +1578,12 @@ sub createOldEventId {
lg sprintf('Create event "%s" into OLDEPG', $subtitle ? $title .'~'. $subtitle : $title);
my $sth = $self->{dbh}->prepare(
-q|REPLACE INTO OLDEPG(eventid, title, subtitle, description, channel_id,
+q|REPLACE INTO OLDEPG(vid, eventid, title, subtitle, description, channel_id,
duration, tableid, starttime, vpstime, video, audio, addtime)
- VALUES (?,?,?,?,?,?,?,FROM_UNIXTIME(?),FROM_UNIXTIME(?),?,?,NOW())|);
+ VALUES (?,?,?,?,?,?,?,?,FROM_UNIXTIME(?),FROM_UNIXTIME(?),?,?,NOW())|);
$sth->execute(
+ $vid,
$attr->{eventid},
$attr->{title},
$attr->{subtitle},
@@ -1497,8 +1619,8 @@ SELECT SQL_CACHE
r.eventid,
e.Duration,
r.Marks,
- r.Prio,
- r.Lifetime,
+ r.priority,
+ r.lifetime,
UNIX_TIMESTAMP(e.starttime) as StartTime,
UNIX_TIMESTAMP(e.starttime) + e.duration as StopTime,
e.title as Title,
@@ -1510,7 +1632,8 @@ SELECT SQL_CACHE
FROM CHANNELS as c
WHERE e.channel_id = c.Id
LIMIT 1) as Channel,
- preview
+ preview,
+ cutlength
from
RECORDS as r,OLDEPG as e
where
@@ -1557,8 +1680,9 @@ sub play {
my $recordid = shift || return con_err($console,gettext("No recording defined for playback! Please use rplay 'rid'."));
my $params = shift;
- my $sql = qq|SELECT SQL_CACHE r.RecordID,r.RecordMD5,e.duration as duration FROM
- RECORDS as r, OLDEPG as e WHERE e.eventid = r.eventid and r.RecordMD5 = ?|;
+ my $sql = qq|SELECT SQL_CACHE vid, RecordID, RecordMD5, duration
+ FROM RECORDS as r, OLDEPG as e
+ WHERE e.eventid = r.eventid and RecordMD5 = ?|;
my $sth = $self->{dbh}->prepare($sql);
my $rec;
if(!$sth->execute($recordid)
@@ -1580,9 +1704,13 @@ sub play {
$start = 'begin';
}
+ my $vdr = $rec->{vid};
+ if($params && exists $params->{vdr}) {
+ $vdr = $params->{vdr};
+ }
my $cmd = sprintf('PLAY %d %s', $rec->{RecordID}, $start);
- if($self->{svdrp}->scommand($console, $config, $cmd)) {
+ if($self->{svdrp}->scommand($console, $config, $cmd, $vdr)) {
$console->redirect({url => sprintf('?cmd=rdisplay&data=%s',$rec->{RecordMD5}), wait => 1})
if(ref $console and $console->typ eq 'HTML');
@@ -1599,8 +1727,11 @@ sub cut {
my $console = shift || return error('No console defined!');
my $config = shift || return error('No config defined!');
my $recordid = shift || return con_err($console,gettext("No recording defined for playback! Please use rplay 'rid'."));
+ my $params = shift;
- my $sql = qq|SELECT SQL_CACHE RecordID,RecordMD5 FROM RECORDS WHERE RecordMD5 = ?|;
+ my $sql = qq|SELECT SQL_CACHE vid, RecordID, RecordMD5
+ FROM RECORDS as r, OLDEPG as e
+ WHERE e.eventid = r.eventid and r.RecordMD5 = ?|;
my $sth = $self->{dbh}->prepare($sql);
my $rec;
if(!$sth->execute($recordid)
@@ -1608,8 +1739,13 @@ sub cut {
return con_err($console,sprintf(gettext("Recording '%s' does not exist in the database!"),$recordid));
}
+ my $vdr = $rec->{vid};
+ if($params && exists $params->{vdr}) {
+ $vdr = $params->{vdr};
+ }
+
my $cmd = sprintf('EDIT %d', $rec->{RecordID});
- if($self->{svdrp}->scommand($console, $cmd)) {
+ if($self->{svdrp}->scommand($console, $cmd, $vdr)) {
$console->redirect({url => sprintf('?cmd=rdisplay&data=%s',$rec->{RecordMD5}), wait => 1})
if(ref $console and $console->typ eq 'HTML');
@@ -1629,14 +1765,11 @@ sub list {
my $params = shift;
my $deep = 1;
- my $folder = scalar (my @a = split('/',$self->{videodir})) + 1;
my $term;
- my $where = "e.eventid = r.eventid";
+ my $where = "";
if($text) {
- $deep = scalar (my @c = split('~',$text));
- $folder += $deep;
- $deep += 1;
+ $deep += scalar (my @c = split('~',$text));
$text =~ s/\'/\\\'/sg;
$text =~ s/%/\\%/sg;
@@ -1673,15 +1806,17 @@ SELECT SQL_CACHE
SUBSTRING_INDEX(CONCAT_WS('~',e.title,e.subtitle), '~', $deep) as __fulltitle,
IF(COUNT(*)>1,0,1) as __IsRecording,
e.description as __description,
- preview as __preview
+ preview as __preview,
+ cutlength as __cutlength
FROM
RECORDS as r,
OLDEPG as e
WHERE
+ e.eventid = r.eventid
$where
GROUP BY
- SUBSTRING_INDEX(r.Path, '/', IF(Length(e.subtitle)<=0, $folder + 1, $folder))
-ORDER BY __IsRecording asc,
+ SUBSTRING_INDEX(CONCAT_WS('~',e.title,e.subtitle,RecordMD5), '~', $deep)
+ORDER BY __IsRecording asc,
|;
@@ -1810,7 +1945,8 @@ SELECT SQL_CACHE
CONCAT_WS('~',e.title,e.subtitle) as __fulltitle,
1 as __IsRecording,
e.description as __description,
- preview as __preview
+ preview as __preview,
+ cutlength as __cutlength
FROM
RECORDS as r,
OLDEPG as e
@@ -1925,7 +2061,11 @@ sub delete {
}
my @recordings = keys %rec;
- my $sql = sprintf("SELECT SQL_CACHE r.RecordId,CONCAT_WS('~',e.title,e.subtitle),r.RecordMD5 FROM RECORDS as r,OLDEPG as e WHERE e.eventid = r.eventid and r.RecordMD5 IN (%s) ORDER BY r.RecordId desc", join(',' => ('?') x @recordings));
+ my $sql = sprintf(
+qq|SELECT SQL_CACHE e.vid, r.RecordId,CONCAT_WS('~',e.title,e.subtitle),r.RecordMD5
+ FROM RECORDS as r,OLDEPG as e
+ WHERE e.eventid = r.eventid and r.RecordMD5 IN (%s)
+ ORDER BY e.vid, r.RecordId desc|, join(',' => ('?') x @recordings));
my $sth = $self->{dbh}->prepare($sql);
$sth->execute(@recordings)
or return con_err($console, sprintf("Couldn't execute query: %s.",$sth->errstr));
@@ -1934,9 +2074,10 @@ sub delete {
foreach my $recording (@$data) {
# Make hash for better reading
my $r = {
- Id => $recording->[0],
- Title => $recording->[1],
- MD5 => $recording->[2]
+ vid => $recording->[0],
+ Id => $recording->[1],
+ Title => $recording->[2],
+ MD5 => $recording->[3]
};
if(ref $console and $console->{TYP} eq 'CONSOLE') {
@@ -1956,14 +2097,14 @@ sub delete {
);
- $self->{svdrp}->queue_add(sprintf("delr %s",$r->{Id}));
+ $self->{svdrp}->queue_add(sprintf("delr %s",$r->{Id}), $r->{vid});
push(@{$todelete},$r->{Title}); # Remember title
push(@{$md5delete},$r->{MD5}); # Remember hash
# Delete recordings from request, if found in database
my $i = 0;
for my $x (@recordings) {
- if ( $x eq $recording->[2] ) { # Remove known MD5 from user request
+ if ( $x eq $r->{MD5} ) { # Remove known MD5 from user request
splice @recordings, $i, 1;
} else {
$i++;
@@ -2001,7 +2142,7 @@ sub delete {
$self->{keywords}->remove('recording',$md5delete);
}
- $self->readData($console,$waiter)
+ $self->_readData($console,$waiter)
unless($self->{inotify});
if(ref $console && $console->typ eq 'HTML') {
@@ -2046,12 +2187,13 @@ sub redit {
my $rec;
if($recordid) {
my $sql = qq|
-SELECT SQL_CACHE
+SELECT SQL_CACHE
+ e.vid,
CONCAT_WS('~',e.title,e.subtitle) as title,
e.eventid as EventId,
r.Path,
- r.Prio,
- r.Lifetime
+ r.priority,
+ r.lifetime
FROM
RECORDS as r,
OLDEPG as e
@@ -2086,7 +2228,7 @@ WHERE
'lifetime' => {
typ => 'integer',
msg => sprintf(gettext('Lifetime (%d ... %d)'),0,99),
- def => int($rec->{Lifetime}),
+ def => int($rec->{lifetime}),
check => sub{
my $value = shift || 0;
if($value >= 0 and $value < 100) {
@@ -2100,7 +2242,7 @@ WHERE
'priority' => {
typ => 'integer',
msg => sprintf(gettext('Priority (%d ... %d)'),0,99),
- def => int($rec->{Prio}),
+ def => int($rec->{priority}),
check => sub{
my $value = shift || 0;
if($value >= 0 and $value < 100) {
@@ -2167,6 +2309,13 @@ WHERE
my $dropEPGEntry = 0;
my $ChangeRecordingData = 0;
+ my $videodirectory = $self->{svdrp}->videodirectory($rec->{vid});
+ unless($videodirectory && -d $videodirectory) {
+ my $hostname = $self->{svdrp}->hostname($rec->{vid});
+ $console->err(sprintf(gettext("Missing video directory on %s!"),$hostname))
+ if($console);
+ return;
+ }
$data->{title} =~s#~+#~#g;
$data->{title} =~s#^~##g;
@@ -2206,16 +2355,16 @@ WHERE
}
- if($data->{lifetime} ne $rec->{Lifetime}
- or $data->{priority} ne $rec->{Prio}) {
+ if($data->{lifetime} ne $rec->{lifetime}
+ or $data->{priority} ne $rec->{priority}) {
my @options = split('\.', $rec->{Path});
$options[-2] = sprintf("%02d",$data->{lifetime})
- if($data->{lifetime} ne $rec->{Lifetime});
+ if($data->{lifetime} ne $rec->{lifetime});
$options[-3] = sprintf("%02d",$data->{priority})
- if($data->{priority} ne $rec->{Prio});
+ if($data->{priority} ne $rec->{priority});
my $newPath = join('.', @options);
@@ -2229,7 +2378,7 @@ WHERE
if($data->{title} ne $rec->{title}) {
# Rename auf der Platte
- my $newPath = sprintf('%s/%s/%s', $self->{videodir}, $self->translate($data->{title}),basename($rec->{Path}));
+ my $newPath = sprintf('%s/%s/%s', $videodirectory, $self->translate($data->{title}),basename($rec->{Path}));
my $parentnew = dirname($newPath);
unless( -d $parentnew) {
@@ -2241,7 +2390,7 @@ WHERE
or return con_err($console,sprintf(gettext("Recording: '%s', couldn't move to '%s' : %s"),$rec->{title},$data->{title},$!));
my $parentold = dirname($rec->{Path});
- if($self->{videodir} ne $parentold
+ if($videodirectory ne $parentold
and -d $parentold
and is_empty_dir($parentold)) {
rmdir($parentold)
@@ -2270,7 +2419,7 @@ WHERE
}
if($dropEPGEntry || $ChangeRecordingData) {
$self->{lastupdate} = 0;
- touch($self->{videodir}."/.update");
+ touch($videodirectory."/.update");
}
if($dropEPGEntry || $ChangeRecordingData) {
my $waiter;
@@ -2282,7 +2431,7 @@ WHERE
}
sleep(1);
- $self->readData($console,$waiter)
+ $self->_readData($console,$waiter)
unless($self->{inotify});
} else {
@@ -2449,10 +2598,7 @@ sub getGroupIds {
}
my $text = $data->{title};
- my $folder = scalar (my @a = split('/',$self->{videodir})) + 1;
- my $deep = scalar (my @c = split('~',$text));
- $folder += $deep;
- $deep += 1;
+ my $deep = ( scalar (my @c = split('~',$text)) ) + 1;
$text =~ s/\'/\\\'/sg;
$text =~ s/%/\\%/sg;
@@ -2471,7 +2617,7 @@ AND (
SUBSTRING_INDEX(CONCAT_WS('~',e.title,e.subtitle), '~', $deep) LIKE ?
)
GROUP BY
- SUBSTRING_INDEX(r.Path, '/', IF(Length(e.subtitle)<=0, $folder + 1, $folder))
+ SUBSTRING_INDEX(CONCAT_WS('~',e.title,e.subtitle,RecordMD5), '~', $deep)
|;
my $sth = $self->{dbh}->prepare($sql);
@@ -2652,7 +2798,24 @@ sub recover {
my $recordid = shift || 0;
my $data = shift || 0;
- my $files = $self->scandirectory('del');
+ my $files;
+ my $directories;
+
+ my $hostlist = $self->{svdrp}->list_unique_recording_hosts();
+ foreach my $vid (@$hostlist) {
+ my $videodirectory = $self->{svdrp}->videodirectory($vid);
+ unless($videodirectory && -d $videodirectory) {
+ my $hostname = $self->{svdrp}->hostname($vid);
+ $console->err(sprintf(gettext("Missing video directory on %s!"),$hostname))
+ if($console);
+ next;
+ }
+ my $f = $self->scandirectory($videodirectory, 'del');
+ if($f) {
+ map { $files->{$_} = $f->{$_}; } keys %{$f};
+ push(@$directories, $videodirectory);
+ }
+ }
return con_msg($console,gettext("There none recoverable recordings!"))
unless($files and keys %{$files});
@@ -2709,7 +2872,9 @@ sub recover {
my $waiter;
$self->{lastupdate} = 0;
- touch($self->{videodir}."/.update");
+ foreach my $d (@$directories) {
+ touch($d."/.update");
+ }
if(ref $console && $console->typ eq 'HTML' && !($self->{inotify})) {
$waiter = $console->wait(gettext('Recording recovered!'),0,1000,'no');
@@ -2718,7 +2883,7 @@ sub recover {
}
sleep(1);
- $self->readData($console,$waiter)
+ $self->_readData($console,$waiter)
unless($self->{inotify});
} else {
diff --git a/lib/XXV/MODULES/REMOTE.pm b/lib/XXV/MODULES/REMOTE.pm
index caf09f9..42ca655 100644
--- a/lib/XXV/MODULES/REMOTE.pm
+++ b/lib/XXV/MODULES/REMOTE.pm
@@ -28,7 +28,7 @@ sub module {
required => gettext('This is required!'),
},
monitor => {
- description => gettext('Grab video framebuffer, as preview on remotecontrol.'),
+ description => gettext('Show grabbed video framebuffer in addition to remote control.'),
default => 'y',
type => 'confirm',
required => gettext('This is required!'),
diff --git a/lib/XXV/MODULES/STREAM.pm b/lib/XXV/MODULES/STREAM.pm
index d6e3407..4d58234 100644
--- a/lib/XXV/MODULES/STREAM.pm
+++ b/lib/XXV/MODULES/STREAM.pm
@@ -180,6 +180,13 @@ sub init {
# ------------------
my $self = shift || return error('No object defined!');
+ main::after(sub{
+ $self->{svdrp} = main::getModule('SVDRP');
+ unless($self->{svdrp}) {
+ return 0;
+ }
+ }, "STREAM: Prepare streaming ...");
+
1;
}
@@ -277,7 +284,7 @@ sub playrecord {
unless($console->can('stream'));
my $rmod = main::getModule('RECORDS');
- my $result = $rmod->IdToData($recid)
+ my $rec = $rmod->IdToData($recid)
or return $console->err(sprintf(gettext("Couldn't find recording: '%s'"), $recid));
my $start = 0;
@@ -291,20 +298,20 @@ sub playrecord {
$data .= sprintf("&__start=%d", $start) if($start);
my $param = {
- title => $result->{title},
+ title => $rec->{title},
widget => $self->{widget},
width => $self->{width},
height => $self->{height},
};
- $param->{title} .= '~' . $result->{subtitle} if($result->{subtitle});
+ $param->{title} .= '~' . $rec->{subtitle} if($rec->{subtitle});
return $console->player($data, $param);
}
return $console->err(sprintf(gettext("Couldn't find recording: '%s'"), $recid))
- unless $result->{Path};
+ unless $rec->{Path};
- my $path = $result->{Path};
+ my $path = $rec->{Path};
my @files = bsd_glob("$path/[0-9][0-9][0-9].vdr");
return $console->err(sprintf(gettext("Couldn't find recording: '%s'"), $recid))
@@ -325,7 +332,13 @@ sub playrecord {
return $console->stream(\@files, $self->{mimetyp}, $offset);
} else {
- my $videopath = $rmod->{videodir};
+ my $videodirectory = $self->{svdrp}->videodirectory($rec->{vid});
+ unless($videodirectory && -d $videodirectory) {
+ my $hostname = $self->{svdrp}->hostname($rec->{vid});
+ $console->err(sprintf(gettext("Missing video directory on %s!"),$hostname))
+ if($console);
+ return;
+ }
my $data;
$data = "#EXTM3U\r\n";
@@ -333,7 +346,7 @@ sub playrecord {
foreach my $file (@files) {
my $fstat = stat($file);
- $file =~ s/^$videopath//si;
+ $file =~ s/^$videodirectory//si;
$file =~ s/^[\/|\\]//si;
my $URL = sprintf("%s/%s\r\n", $self->{netvideo}, $file);
$URL =~s/\//\\/g
@@ -343,10 +356,10 @@ sub playrecord {
if($fstat) {
# estimate duration of file in seconds ( filesize * totaltime / totalsize )
- my $duration = CORE::int($fstat->size * $result->{duration} / ($result->{FileSize} * 1024 * 1024));
+ my $duration = CORE::int($fstat->size * $rec->{duration} / ($rec->{FileSize} * 1024 * 1024));
# add duration and title as extended infomations
- $data .= "#EXTINF:". $duration ."," . $result->{title};
- $data .= "~" . $result->{subtitle} if($result->{subtitle});
+ $data .= "#EXTINF:". $duration ."," . $rec->{title};
+ $data .= "~" . $rec->{subtitle} if($rec->{subtitle});
$data .= "\r\n";
}
$data .= $URL;
diff --git a/lib/XXV/MODULES/SVDRP.pm b/lib/XXV/MODULES/SVDRP.pm
index 716b834..f435597 100644
--- a/lib/XXV/MODULES/SVDRP.pm
+++ b/lib/XXV/MODULES/SVDRP.pm
@@ -141,6 +141,7 @@ sub _init {
host varchar(100) NOT NULL default 'localhost',
port smallint unsigned default 2001,
cards varchar(100) default '',
+ videodirectory text default '',
PRIMARY KEY (id)
) COMMENT = '$version'
|);
@@ -153,7 +154,8 @@ sub _init {
master => 'y',
host => 'localhost',
port => 2001,
- cards => ''
+ cards => '',
+ videodirectory => '/var/lib/video'
});
}
@@ -166,14 +168,15 @@ sub _insert {
my $self = shift || return error('No object defined!');
my $data = shift || return;
- my $sth = $self->{dbh}->prepare('REPLACE INTO RECORDER VALUES (?,?,?,?,?,?)');
+ my $sth = $self->{dbh}->prepare('REPLACE INTO RECORDER VALUES (?,?,?,?,?,?,?)');
$sth->execute(
$data->{id} || 0,
$data->{active},
$data->{master},
$data->{host},
$data->{port},
- $data->{cards}
+ $data->{cards},
+ $data->{videodirectory}
) or return error sprintf("Couldn't execute query: %s.",$sth->errstr);
}
@@ -241,6 +244,12 @@ sub edit {
msg => gettext("List of present source of DVB cards. (eg. S19.2E,S19.2E,T,T )"),
def => $default->{cards} || main::getModule('CHANNELS')->buildSourceList($id || $self->primary_hosts()),
},
+ 'videodirectory' => {
+ msg => gettext("Directory where recordings are stored"),
+ def => $default->{videodirectory},
+ req => gettext('This is required!'),
+ typ => 'dir',
+ },
];
@@ -321,7 +330,8 @@ sub list {
'active' => gettext('Active'),
'master' => gettext('Primary'),
'host' => gettext('Host'),
- 'cards' => gettext('Typ of Cards')
+ 'cards' => gettext('Typ of Cards'),
+ 'videodirectory' => gettext('Video directory')
);
my $sql = qq|
@@ -330,7 +340,8 @@ SELECT SQL_CACHE
active as \'$f{active}\',
master as \'$f{master}\',
host as \'$f{host}\',
- cards as \'$f{cards}\'
+ cards as \'$f{cards}\',
+ videodirectory as \'$f{videodirectory}\'
from
RECORDER
|;
@@ -460,6 +471,31 @@ sub enum_hosts {
return $sth->fetchall_arrayref();
}
+sub list_unique_recording_hosts() {
+ my $self = shift || return error('No object defined!');
+
+ my $sth = $self->{dbh}->prepare("SELECT SQL_CACHE id from RECORDER where active = 'y' GROUP BY videodirectory");
+ $sth->execute()
+ or return error sprintf("Couldn't execute query: %s.",$sth->errstr);
+ my $result = $sth->fetchall_hashref('id');
+ return undef unless($result);
+
+ my $hosts;
+ foreach my $id (keys %{$result}) {
+ push(@$hosts,$id);
+ }
+
+ return $hosts;
+}
+
+sub videodirectory {
+ my $self = shift || return error('No object defined!');
+ my $vdrid = shift;
+
+ my $vdr = $self->_gethost($vdrid);
+ return $vdr ? $vdr->{videodirectory} : undef;
+}
+
# ------------------
sub IDfromHostname {
# ------------------
diff --git a/lib/XXV/MODULES/TIMERS.pm b/lib/XXV/MODULES/TIMERS.pm
index 8037820..c034f30 100644
--- a/lib/XXV/MODULES/TIMERS.pm
+++ b/lib/XXV/MODULES/TIMERS.pm
@@ -1008,7 +1008,7 @@ sub toggleTimer {
$sth->execute(@success)
or return error sprintf("Couldn't execute query: %s.",$sth->errstr);
my $erg = $sth->fetchall_arrayref();
- $console->table($erg);
+ $console->table($erg,{state => 'success'});
}
return 1;