File Coverage

blib/lib/Elive/Entity/Recording.pm
Criterion Covered Total %
statement 12 61 19.6
branch 0 26 0.0
condition 0 32 0.0
subroutine 4 9 44.4
pod 5 5 100.0
total 21 133 15.7


line stmt bran cond sub pod time code
1             package Elive::Entity::Recording;
2 1     1   760 use warnings; use strict;
  1     1   2  
  1         30  
  1         6  
  1         2  
  1         26  
3 1     1   5 use Mouse;
  1         1  
  1         6  
4              
5             extends 'Elive::Entity';
6              
7 1     1   417 use Elive::Util;
  1         2  
  1         1144  
8              
9             __PACKAGE__->entity_name('Recording');
10             __PACKAGE__->collection_name('Recordings');
11             __PACKAGE__->derivable(url => 'web_url');
12              
13             has 'recordingId' => (is => 'rw', isa => 'Str', required => 1);
14             __PACKAGE__->primary_key('recordingId');
15              
16             __PACKAGE__->params(
17             userId => 'Str',
18             userIP => 'Str',
19             length => 'Int',
20             );
21              
22             has 'creationDate' => (is => 'rw', isa => 'HiResDate', required => 1,
23             documentation => 'creation date and time of the recording');
24              
25             has 'data' => (is => 'rw', isa => 'Str',
26             documentation => 'recording byte-stream');
27              
28             has 'facilitator' => (is => 'rw', isa => 'Str',
29             documentation => 'the creator of the meeting');
30              
31             has 'keywords' => (is => 'rw', isa => 'Str',
32             documentation => 'keywords for this recording');
33              
34             has 'meetingId' => (is => 'rw', isa => 'Int',
35             documentation => 'id of the meeting that created this recording');
36             __PACKAGE__->_alias(meetingRoomId => 'meetingId', freeze => 1);
37              
38             has 'sessionInstanceId' => (is => 'rw', isa => 'Int',
39             documentation => 'id of the session instance that created this recording');
40              
41             has 'open' => (is => 'rw', isa => 'Bool',
42             documentation => 'whether to display this recording on the public page');
43             has 'roomName' => (is => 'rw', isa => 'Str',
44             documentation => 'name of the meeting that created this recording');
45             has 'size' => (is => 'rw', isa => 'Int',
46             documentation => 'recording file size (bytes');
47             has 'version' => (is => 'rw', isa => 'Str',
48             documentation => 'version of Elluminate Live! that created this recording');
49              
50             has 'sasId' => (is => 'rw', isa => 'Int');
51              
52             has 'startDate' => (is => 'rw', isa => 'HiResDate',
53             documentation => 'start date/time of the recording');
54              
55             has 'endDate' => (is => 'rw', isa => 'HiResDate',
56             documentation => 'end date/time of the recording');
57              
58             =head1 NAME
59              
60             Elive::Entity::Recording - Elluminate Recording Entity class
61              
62             =cut
63              
64             sub BUILDARGS {
65 0     0 1   my $class = shift;
66 0           my $spec = shift;
67              
68 0 0         die "usage $class->new({ ... }, ...)"
69             unless Elive::Util::_reftype($spec) eq 'HASH';
70              
71 0           my %args = %{ $spec };
  0            
72              
73 0 0         if (defined $args{data}) {
74 0   0       $args{size} ||= length( $args{data} );
75             }
76              
77 0           return \%args;
78             }
79              
80             =head1 METHODS
81              
82             =cut
83              
84             =head2 web_url
85              
86             Utility method to return various website links for the recording. This is
87             available as both object and class level methods.
88              
89             #
90             # Object level.
91             #
92             my $recording = Elive::Entity::Recording->retrieve($recording_id);
93             my $url = recording->web_url(action => 'play');
94              
95             #
96             # Class level access.
97             #
98             my $url = $recording->web_url(
99             action => 'play',
100             recording_id => $recording_id,
101             connection => $my_connection); # optional
102              
103              
104             =cut
105              
106             sub web_url {
107 0     0 1   my ($self, %opt) = @_;
108              
109 0   0       my $recording_id = $opt{recording_id} || $self->recordingId;
110 0           $recording_id = Elive::Util::_freeze($recording_id, 'Str');
111              
112 0 0         die "no recording_id given"
113             unless $recording_id;
114              
115 0 0 0       my $connection = $self->connection || $opt{connection}
116             or die "not connected";
117              
118 0           my $url = $connection->url;
119              
120 0           my %Actions = (
121             'play' => '%s/play_recording.html?recordingId=%s',
122             );
123              
124 0   0       my $action = $opt{action} || 'play';
125              
126 0 0         die "unrecognised action: $action"
127             unless exists $Actions{$action};
128              
129 0           return sprintf($Actions{$action},
130             $url, $recording_id);
131             }
132              
133             =head2 buildJNLP
134              
135             my $jnlp = $recording_entity->buildJNLP(version => version,
136             userId => $user->userId,
137             userIP => $ENV{REMOTE_ADDR});
138              
139             Builds a JNLP for the recording.
140              
141             JNLP is the 'Java Network Launch Protocol', also commonly known as Java
142             WebStart. You can, for example, render this as a web page with mime type
143             C.
144              
145             The C is required for elm 9.0+ when C has
146             been set to C in C (or set interactively via:
147             Preferences EE Session Access EE Lock Recording Playback to Client IP)
148              
149             It represents a fixed client IP address for launching the recording playback.
150              
151             See also L.
152              
153             =cut
154              
155             sub buildJNLP {
156 0     0 1   my ($self, %opt) = @_;
157              
158 0 0 0       my $connection = $self->connection || $opt{connection}
159             or die "not connected";
160              
161 0   0       my $recording_id = $opt{recording_id} || $self->recordingId;
162              
163 0 0         die "unable to determine recording_id"
164             unless $recording_id;
165              
166 0           my %soap_params = (recordingId => $recording_id);
167              
168 0 0         $soap_params{'userIP'} = $opt{userIP} if $opt{userIP}; # elm 9.1+ compat
169 0   0       $soap_params{'userId'} = $opt{userId} || $connection->login->userId;
170              
171 0           my $som = $connection->call('buildRecordingJNLP', %{$self->_freeze(\%soap_params)});
  0            
172              
173 0           my $results = $self->_get_results($som, $connection);
174              
175 0   0       return @$results && $results->[0];
176             }
177              
178             =head2 download
179              
180             my $recording = Elive::Entity::Recording->retrieve($recording_id);
181             my $binary_data = $recording->download;
182              
183             Download data for a recording.
184              
185             =cut
186              
187             sub download {
188 0     0 1   my ($self, %opt) = @_;
189              
190 0   0       my $recording_id = $opt{recording_id} || $self->recordingId;
191              
192 0 0         die "unable to get a recording_id"
193             unless $recording_id;
194              
195 0 0 0       my $connection = $self->connection || $opt{connection}
196             or die "not connected";
197              
198 0           my $som = $connection->call('getRecordingStream',
199 0           %{ $self->_freeze({
200             recordingId => $recording_id,
201             })},
202             );
203              
204 0           my $results = $self->_get_results($som, $connection);
205              
206 0 0         return Elive::Util::_hex_decode($results->[0])
207             if $results->[0];
208              
209 0           return;
210             }
211              
212             =head2 upload
213              
214             This method lets you import recordings to an Elluminate Server.
215              
216             You'll need supply binary data, a generated recording-Id and, optionally,
217             an associated meeting:
218              
219             use Elive;
220             use Elive::Entity::Recording;
221              
222             sub example_upload {
223             #
224             # demo subroutine to upload a recording file to an Elluminate server
225             # - assumes that we are already connected to the server
226             #
227             my $recording_file = shift; # path to recording file
228             my %opt = @_;
229              
230             # get the binary data from somewhere
231              
232             open (my $fh, '<', $recording_file)
233             or die "unable to open $recording_file: $!";
234             $fh->binmode;
235              
236             my $binary_data = do {local $/ = undef; <$fh>};
237             die "no recording data: $recording_file"
238             unless ($binary_data && length($binary_data));
239              
240             # somehow generate a unique key for the recordingId.
241              
242             use Time::HiRes();
243             my ($seconds, $microseconds) = Time::HiRes::gettimeofday();
244             my $recordingId = sprintf("%d%04d_upload", $seconds, $microseconds/1000);
245              
246             my %recording_data = (
247             data => $binary_data,
248             recordingId => $recordingId,
249             version => $opt{version},
250             );
251              
252             if (my $meeting = $opt{meeting}) {
253             #
254             # associate the recording with this meeting
255             #
256             $recording_data{meetingId} = $meeting->meetingId;
257             $recording_data{roomName} = $meeting->name;
258             $recording_data{facilitator} = $meeting->facilitatorId;
259             }
260              
261             $recording_data{version} ||= Elive->server_details->version;
262             $recording_data{facilitator} ||= Elive->login;
263              
264             my $recording = Elive::Entity::Recording->upload( \%recording_data );
265              
266             return $recording;
267             }
268              
269             Note: the C, when supplied must match the facilitator for the given C.
270              
271             =cut
272              
273             sub upload {
274 0     0 1   my ($class, $spec, %opt) = @_;
275              
276 0           my $insert_data = $class->BUILDARGS( $spec );
277              
278 0           my $binary_data = delete $insert_data->{data};
279              
280 0           my $self = $class->insert($insert_data, %opt);
281              
282 0           my $size = $self->size;
283              
284 0 0 0       if ($size && $binary_data) {
285              
286 0 0         my $connection = $self->connection
287             or die "not connected";
288              
289 0           my $som = $connection->call('streamRecording',
290 0           %{ $class->_freeze({
291             recordingId => $self->recordingId,
292             length => $size}) },
293             stream => (SOAP::Data
294             ->type('hexBinary')
295             ->value($binary_data)),
296             );
297              
298 0           $connection->_check_for_errors($som);
299             }
300              
301 0           return $self;
302             }
303              
304             =head2 insert
305              
306             The C method is typically used to describe files that are
307             present in the site's recording directory.
308              
309             You'll may need to insert recordings yourself if you are importing large
310             volumes of recording files or to recover recordings that have
311             not been closed cleanly.
312              
313             sub demo_recording_insert {
314             my $import_filename = shift;
315             my $meeting = shift;
316              
317             my $recordingId = $meeting->meetingId.'_import';
318             my $import_filename = sprintf("%s_recordingData.bin", $recordingId);
319              
320             #
321             # Somehow upload the file to the server and work-out the byte-size.
322             # This needs to be uploaded to:
323             # ${instancesRoot}/${instanceName}/WEB-INF/resources/recordings
324             # where $instanceRoot is typically /opt/ElluminateLive/manager/tomcat
325             #
326             my $bytes = import_recording($import_filename); # implementation dependant
327              
328             my $recording = Elive::Entity::Recording->insert({
329             recordingId => $recordingId,
330             creationDate => time().'000',
331             meetingId => $meeting->meetingId,
332             facilitator => $meeting->facilitator,
333             roomName => $meeting->name,
334             version => Elive->server_details->version,
335             size => $bytes,
336             });
337             }
338              
339             The Recording C method, unlike other entities, requires that you supply
340             a primary key. This is then used to determine the name of the file to look for
341             in the recording directory, as in the above example.
342              
343             The C is optional. Recordings do not have to be associated with a
344             particular meetings. They will still be searchable and are available for
345             playback.
346              
347             =cut
348              
349             1;