Bug 8377 [ENH] Show HTML5 video/ audio for media files in OPAC and staff client
authorMirko Tietgen <mirko@abunchofthings.net>
Thu, 22 Nov 2012 12:22:22 +0000 (13:22 +0100)
committerJared Camins-Esakov <jcamins@cpbibliography.com>
Thu, 27 Dec 2012 14:28:00 +0000 (09:28 -0500)
This enhancement uses information from MARC field 856 to generate the appropriate HTML5 code to embed am media player for the file(s) in a tab in the OPAC and staff client detail view. This patch supports the HTML5 <audio> and <video> element. Additionally it gives basic support for the <track> element. This element is not supported very well by recent browsers. Please consider the patch working when you get working video or audio.

Rebased to Master 22.11.2012

Signed-off-by: Chris Cormack <chrisc@catalyst.net.nz>
Signed-off-by: Jonathan Druart <jonathan.druart@biblibre.com>
Signed-off-by: Jared Camins-Esakov <jcamins@cpbibliography.com>

C4/HTML5Media.pm [new file with mode: 0644]
catalogue/detail.pl
installer/data/mysql/sysprefs.sql
installer/data/mysql/updatedatabase.pl
koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
opac/opac-detail.pl

diff --git a/C4/HTML5Media.pm b/C4/HTML5Media.pm
new file mode 100644 (file)
index 0000000..aff7192
--- /dev/null
@@ -0,0 +1,226 @@
+package C4::HTML5Media;
+
+# Copyright 2012 Mirko Tietgen
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use strict;
+use warnings;
+
+use C4::Context;
+use MARC::Field;
+
+
+=head1 HTML5Media
+
+C4::HTML5Media
+
+=head1 Description
+
+This module gets the relevant data from field 856 (MARC21/UNIMARC) to create a HTML5 audio or video element containing the file(s) catalogued in 856.
+
+=cut
+
+=head2 gethtml5media
+
+Get all relevant data from field 856. Takes $template and $record in the subroutine call, sets appropriate params.
+
+=cut
+
+sub gethtml5media {
+    my $self = shift;
+    my $template = shift;
+    my $record = shift;
+    my @HTML5Media_sets = ();
+    my @HTML5Media_fields = $record->field(856);
+    my $HTML5MediaParent;
+    my $HTML5MediaWidth;
+    my @HTML5MediaExtensions = split( /\|/, C4::Context->preference("HTML5MediaExtensions") );
+    my $marcflavour          = C4::Context->preference("marcflavour");
+    foreach my $HTML5Media_field (@HTML5Media_fields) {
+        my %HTML5Media;
+        # protocol
+        if ( $HTML5Media_field->indicator(1) eq '1' ) {
+            $HTML5Media{protocol} = 'ftp';
+        }
+        elsif ( $HTML5Media_field->indicator(1) eq '4' ) {
+            $HTML5Media{protocol} = 'http';
+        }
+        elsif ( $HTML5Media_field->indicator(1) eq '7' ) {
+            if ($marcflavour eq 'MARC21' || $marcflavour eq 'NORMARC') {
+                $HTML5Media{protocol} = $HTML5Media_field->subfield('2');
+            }
+            elsif ($marcflavour eq 'UNIMARC') {
+                $HTML5Media{protocol} = $HTML5Media_field->subfield('y');
+            }
+        }
+        else {
+            $HTML5Media{protocol} = 'http';
+        }
+        # user
+        if ( $HTML5Media_field->subfield('l') ) {
+            $HTML5Media{username} = $HTML5Media_field->subfield('l'); # yes, that is arbitrary if h and l are not the same. originally i flipped a coin in that case.
+        }
+        elsif ( $HTML5Media_field->subfield('h') ) {
+            $HTML5Media{username} = $HTML5Media_field->subfield('h');
+        }
+        # user/pass
+        if ( $HTML5Media{username} && $HTML5Media_field->subfield('k') ) {
+            $HTML5Media{loginblock} = $HTML5Media{username} . ':' . $HTML5Media_field->subfield('k') . '@';
+        }
+        elsif ( $HTML5Media{username} ) {
+            $HTML5Media{loginblock} = $HTML5Media{username} . '@';
+        }
+        else {
+            $HTML5Media{loginblock} = '';
+        }
+        # port
+        if ( $HTML5Media_field->subfield('p') ) {
+            $HTML5Media{portblock} = ':' . $HTML5Media_field->subfield('k');
+        }
+        else {
+            $HTML5Media{portblock} = '';
+        }
+        # src
+        if ( $HTML5Media_field->subfield('u') ) {
+            $HTML5Media{srcblock} = $HTML5Media_field->subfield('u');
+        }
+        elsif ( $HTML5Media_field->subfield('a') && $HTML5Media_field->subfield('d') && $HTML5Media_field->subfield('f') ) {
+            $HTML5Media{host}        = $HTML5Media_field->subfield('a');
+            $HTML5Media{host}        =~ s/(^\/|\/$)//g;
+            $HTML5Media{path}        = $HTML5Media_field->subfield('d');
+            $HTML5Media{path}        =~ s/(^\/|\/$)//g;
+            $HTML5Media{file}        = $HTML5Media_field->subfield('f');
+            $HTML5Media{srcblock}    = $HTML5Media{protocol} . '://' . $HTML5Media{loginblock} . $HTML5Media{host} . $HTML5Media{portblock} . '/' . $HTML5Media{path} . '/' . $HTML5Media{file};
+        }
+        else {
+            next; # no file to play
+        }
+        # extension
+        $HTML5Media{extension} = ($HTML5Media{srcblock} =~ m/([^.]+)$/)[0];
+        if ( !grep /$HTML5Media{extension}/, @HTML5MediaExtensions ) {
+            next; # not a specified media file
+        }
+        # mime
+        if ( $HTML5Media_field->subfield('c') ) {
+            $HTML5Media{codecs} = $HTML5Media_field->subfield('c');
+        }
+        ### from subfield q…
+        if ( $HTML5Media_field->subfield('q') ) {
+            $HTML5Media{mime} = $HTML5Media_field->subfield('q');
+        }
+        ### …or from file extension and codecs…
+        elsif ( $HTML5Media{codecs} ) {
+            if ( $HTML5Media{codecs} =~ /theora.*vorbis/ ) {
+                $HTML5Media{mime} = 'video/ogg';
+            }
+            elsif ( $HTML5Media{codecs} =~ /vp8.*vorbis/ ) {
+                $HTML5Media{mime} = 'video/webm';
+            }
+            elsif ( ($HTML5Media{codecs} =~ /^vorbis$/) && ($HTML5Media{extension} eq 'ogg') ) {
+                $HTML5Media{mime} = 'audio/ogg';
+            }
+            elsif ( ($HTML5Media{codecs} =~ /^vorbis$/) && ($HTML5Media{extension} eq 'webm') ) {
+                $HTML5Media{mime} = 'audio/webm';
+            }
+        }
+        ### …or just from file extension
+        else {
+            if ( $HTML5Media{extension} eq 'ogv' ) {
+                $HTML5Media{mime} = 'video/ogg';
+                $HTML5Media{codecs} = 'theora,vorbis';
+            }
+            if ( $HTML5Media{extension} eq 'oga' ) {
+                $HTML5Media{mime} = 'audio/ogg';
+              $HTML5Media{codecs} = 'vorbis';
+            }
+            elsif ( $HTML5Media{extension} eq 'spx' ) {
+                $HTML5Media{mime} = 'audio/ogg';
+                $HTML5Media{codecs} = 'speex';
+            }
+            elsif ( $HTML5Media{extension} eq 'opus' ) {
+                $HTML5Media{mime} = 'audio/ogg';
+                $HTML5Media{codecs} = 'opus';
+            }
+            elsif ( $HTML5Media{extension} eq 'vtt' ) {
+                $HTML5Media{mime} = 'text/vtt';
+            }
+        }
+        # codecs
+        if ( $HTML5Media{codecs} ) {
+            $HTML5Media{codecblock} = '; codecs="' . $HTML5Media{codecs} . '"';
+        }
+        else {
+            $HTML5Media{codecblock} = '';
+        }
+        # type
+        if ( $HTML5Media{mime} ) {
+            $HTML5Media{typeblock} = ' type=\'' . $HTML5Media{mime} . $HTML5Media{codecblock} . '\'';
+        }
+        else {
+          $HTML5Media{typeblock} = '';
+        }
+        # element
+        if ( $HTML5Media{mime} =~ /audio/ ) {
+            $HTML5Media{type} = 'audio';
+        }
+        elsif ( $HTML5Media{mime} =~ /video/ ) {
+            $HTML5Media{type} = 'video';
+        }
+        elsif ( $HTML5Media{mime} =~ /text/ ) {
+            $HTML5Media{type} = 'track';
+        }
+        # push
+        if ( $HTML5Media{srcblock} && $HTML5Media{type} ) {
+            push (@HTML5Media_sets, \%HTML5Media);
+        }
+    }
+    # parent element
+    for my $i ( 0 .. $#HTML5Media_sets ) {
+        if ( ($HTML5Media_sets[$i]{mime}) && ($HTML5Media_sets[$i]{mime} =~ /audio/) ) {
+            if ( $HTML5MediaParent ne 'video' ) {
+                $HTML5MediaParent = 'audio';
+                $HTML5MediaWidth = '';
+            }
+        }
+        elsif ( ($HTML5Media_sets[$i]{mime}) && ($HTML5Media_sets[$i]{mime} =~ /video/) ) {
+            $HTML5MediaParent = 'video';
+            $HTML5MediaWidth = ' width="480"';
+        }
+    }
+    # child element
+    for my $j ( 0 .. $#HTML5Media_sets ) {
+        if ( ($HTML5Media_sets[$j]{type}) && ( ($HTML5Media_sets[$j]{type} eq 'video') || ($HTML5Media_sets[$j]{type} eq 'audio') ) ) {
+            if ( $HTML5Media_sets[$j]{type} eq $HTML5MediaParent ) {
+                $HTML5Media_sets[$j]{child} = 'source';
+            }
+        }
+        else {
+            $HTML5Media_sets[$j]{child} = $HTML5Media_sets[$j]{type};
+        }
+    }
+    # template parameters
+    if ( (scalar(@HTML5Media_sets) > 0) && ($HTML5MediaParent) ) {
+        $template->param(
+            HTML5MediaEnabled  => 1,
+            HTML5MediaSets     => \@HTML5Media_sets,
+            HTML5MediaParent   => $HTML5MediaParent,
+            HTML5MediaWidth    => $HTML5MediaWidth);
+    }
+    return $template;
+}
+
+1;
index 50d966f..65866b8 100755 (executable)
@@ -40,6 +40,7 @@ use C4::VirtualShelves;
 use C4::XSLT;
 use C4::Images;
 use Koha::DateUtils;
+use C4::HTML5Media;
 
 # use Smart::Comments;
 
@@ -379,6 +380,12 @@ if ( C4::Context->preference("LocalCoverImages") == 1 ) {
     $template->{VARS}->{localimages} = \@images;
 }
 
+# HTML5 Media
+if ( (C4::Context->preference("HTML5MediaEnabled") eq 'staff') || (C4::Context->preference("HTML5MediaEnabled") eq 'both') ) {
+    $template = C4::HTML5Media->gethtml5media($template,$record);
+}
+
+
 # Get OPAC URL
 if (C4::Context->preference('OPACBaseURL')){
      $template->param( OpacUrl => C4::Context->preference('OPACBaseURL') );
index ccb3fcf..4605d96 100644 (file)
@@ -400,3 +400,5 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacSeparateHoldings', '0', 'Separate current branch holdings from other holdings (OPAC)', NULL, 'YesNo');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacSeparateHoldingsBranch', 'homebranch', 'Branch used to separate holdings (OPAC)', 'homebranch|holdingbranch', 'Choice');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('RenewalSendNotice','0', NULL, '', 'YesNo');
+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('HTML5MediaEnabled','not','Show a tab with a HTML5 media player for files catalogued in field 856','not|opac|staff|both','Choice');
+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('HTML5MediaExtensions','webm|ogg|ogv|oga|vtt','Media file extensions','','free');
index d93de4e..1f1485e 100755 (executable)
@@ -6309,6 +6309,14 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
     SetVersion($DBversion);
 }
 
+$DBversion = 'XXX';
+if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
+   $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('HTML5MediaEnabled','not','Show a HTML5 media player in a tab on opac-detail.pl for media files catalogued in field 856.','not|opac|staff|both','Choice');");
+   $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('HTML5MediaExtensions','webm|ogg|ogv|oga|vtt','Media file extensions','','free');");
+   print "Upgrade to $DBversion done (Add HTML5MediaEnabled and HTML5MediaExtensions sysprefs)\n";
+   SetVersion ($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
index d1f2814..655ae34 100644 (file)
@@ -286,3 +286,17 @@ Enhanced Content:
                   yes: Allow
                   no: "Don't allow"
             - multiple images to be attached to each bibliographic record.
+    HTML5 Media:
+        -
+            - Show a tab with a HTML5 media player for files catalogued in field 856
+            - pref: HTML5MediaEnabled
+              choices:
+                  not: "not at all."
+                  opac: "in the OPAC."
+                  staff: "in the staff client."
+                  both: "in OPAC and staff client."
+        -
+            - Media file extensions
+            - pref: HTML5MediaExtensions
+              class: multi
+            - (separated with |).
index 6722a05..fc9d6e1 100644 (file)
@@ -272,7 +272,8 @@ function verify_images() {
 [% IF ( subscriptionsnumber ) %]<li><a href="#subscriptions">Subscriptions</a></li>[% END %]
 [% IF ( FRBRizeEditions ) %][% IF ( XISBNS ) %]<li><a href="#editions">Editions</a></li>[% END %][% END %]
 [% IF ( LocalCoverImages ) %][% IF ( localimages || CAN_user_tools_upload_local_cover_images ) %]<li><a href="#images">Images</a></li>[% END %][% END %]
- </ul>
+[% IF ( HTML5MediaEnabled ) %][% IF ( HTML5MediaSets ) %]<li><a href="#html5media">Play media</a></li>[% END %][% END %]
+</ul>
 
 [% BLOCK items_table %]
     <table>
@@ -587,6 +588,20 @@ function verify_images() {
 </div>
 [% END %]
 
+[% IF ( HTML5MediaEnabled ) %]
+<div id="html5media">
+        <p>
+        <[% HTML5MediaParent %][% HTML5MediaWidth %] controls preload=none>
+          [% FOREACH HTML5MediaSet IN HTML5MediaSets %]
+            <[% HTML5MediaSet.child  %] src="[% HTML5MediaSet.srcblock %]"[% HTML5MediaSet.typeblock %] />
+          [% END %]
+            [[% HTML5MediaParent %] tag not supported by your browser.]
+        </[% HTML5MediaParent %]>
+        </p>
+</div>
+[% END %]
+
+
 </div><!-- /bibliodetails -->
 
 <div class="yui-g" id="export" style="margin-top: 1em;">
index 763354d..3ef2470 100644 (file)
@@ -16,6 +16,7 @@
 <link rel="stylesheet" type="text/css" href="/opac-tmpl/prog/en/css/jquery.rating.css" />[% END %]
 
 [% IF ( OpacHighlightedWords ) %]<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.highlight-3.js"></script>[% END %]
+
 <script type="text/JavaScript" language="JavaScript">
 //<![CDATA[
 
@@ -718,6 +719,11 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
 [% IF ( OPACLocalCoverImages && localimages.size ) %]
     <li id="tab_images"><a href="#images">Images</a></li>
 [% END %]
+
+[% IF ( HTML5MediaEnabled ) %][% IF ( HTML5MediaSets ) %]
+    <li id="tab_html5media"><a href="#html5media">Play media</a></li>
+[% END %][% END %]
+
 </ul>
 
 [% IF ( serialcollection ) %]
@@ -1053,6 +1059,19 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
 </table>
 </div>[% END %][% END %]
 
+[% IF ( HTML5MediaEnabled ) %]
+<div id="html5media">
+        <p>
+        <[% HTML5MediaParent %][% HTML5MediaWidth %] controls preload=none>
+          [% FOREACH HTML5MediaSet IN HTML5MediaSets %]
+            <[% HTML5MediaSet.child  %] src="[% HTML5MediaSet.srcblock %]"[% HTML5MediaSet.typeblock %] />
+          [% END %]
+            [[% HTML5MediaParent %] tag not supported by your browser.]
+        </[% HTML5MediaParent %]>
+        </p>
+</div>
+[% END %]
+
 [% IF ( OPACLocalCoverImages && localimages.size ) %]
 <div id="images">
 <p>Click on an image to view it in the image viewer</p>
index c4760c0..0fe44f5 100755 (executable)
@@ -49,6 +49,7 @@ use MARC::Field;
 use List::MoreUtils qw/any none/;
 use C4::Images;
 use Koha::DateUtils;
+use C4::HTML5Media;
 
 BEGIN {
        if (C4::Context->preference('BakerTaylorEnabled')) {
@@ -776,6 +777,11 @@ if (C4::Context->preference("OPACLocalCoverImages")){
                $template->param(OPACLocalCoverImages => 1);
 }
 
+# HTML5 Media
+if ( (C4::Context->preference("HTML5MediaEnabled") eq 'opac') || (C4::Context->preference("HTML5MediaEnabled") eq 'both') ) {
+    $template = C4::HTML5Media->gethtml5media($template,$record);
+}
+
 my $syndetics_elements;
 
 if ( C4::Context->preference("SyndeticsEnabled") ) {