[Koha-patches] [PATCH] Bug 8377 [ENH] Show HTML5 video/ audio for media files in OPAC and staff client

Mirko Tietgen 5p4m at gmx.de
Sat Jul 21 05:11:20 CEST 2012


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.

Changes since last version:
- failed QA: removed default view for media tab, changed tab name to "Play audio" resp. "Play video".
- put most of the code in separate module and access it from OPAC and staff client now, so the feature works in both
- added mp3 to default media types
- had to change sysprefs: HTML5MediaEnabled can be 'none', 'opac', 'staff' or 'both'; HTML5MediaExtensions has additional 'mp3' as default

If you tested this before, you need to delete the old syspref entries from your database. There is an attachment to do this, use exactly like upgradedatabase.pl.

Test plan

- Import attached test data. [I chose media typ 'bk' because it's probably present on most systems. It's not appropriate for the data but that doesn't matter.]
- Rebuild zebra -b -z
- Go to Administration->System preferences->enhanced content->HTML5 Media and set HTML5MediaEnabled to an appropriate setting, save preferences.
- Search the OPAC or staff client for 'bug 8377'
- Get 4 results
- Open detail pages for results. Observe working video (1-3) or audio (4) player on detail page.
---
 Koha/HTML5Media.pm                                 |  230 ++++++++++++++++++++
 catalogue/detail.pl                                |    7 +
 installer/data/mysql/sysprefs.sql                  |    2 +
 installer/data/mysql/updatedatabase.pl             |    8 +
 .../admin/preferences/enhanced_content.pref        |   14 ++
 .../prog/en/modules/catalogue/detail.tt            |   17 ++-
 koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt |   21 ++
 opac/opac-detail.pl                                |    8 +
 8 files changed, 306 insertions(+), 1 deletions(-)
 create mode 100644 Koha/HTML5Media.pm

diff --git a/Koha/HTML5Media.pm b/Koha/HTML5Media.pm
new file mode 100644
index 0000000..7ac8113
--- /dev/null
+++ b/Koha/HTML5Media.pm
@@ -0,0 +1,230 @@
+package Koha::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
+
+Koha::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 'mp3' ) {
+                $HTML5Media{mime} = 'audio/mp3';
+            }
+            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;
+
diff --git a/catalogue/detail.pl b/catalogue/detail.pl
index d110740..e54bea9 100755
--- a/catalogue/detail.pl
+++ b/catalogue/detail.pl
@@ -40,6 +40,7 @@ use C4::VirtualShelves;
 use C4::XSLT;
 use C4::Images;
 use Koha::DateUtils;
+use Koha::HTML5Media;
 
 # use Smart::Comments;
 
@@ -397,6 +398,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 = Koha::HTML5Media->gethtml5media($template,$record);
+}
+
+
 # Get OPAC URL
 if (C4::Context->preference('OPACBaseURL')){
      $template->param( OpacUrl => C4::Context->preference('OPACBaseURL') );
diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql
index 7009155..3c6324d 100644
--- a/installer/data/mysql/sysprefs.sql
+++ b/installer/data/mysql/sysprefs.sql
@@ -375,3 +375,5 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES (
 INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free');
+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|mp3|vtt','Media file extensions','','free');
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index bb96644..130e40b 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -5536,6 +5536,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|mp3|vtt','Media file extensions','','free');");
+   print "Upgrade to $DBversion done (Add HTML5MediaEnabled and HTML5MediaExtensions sysprefs)\n";
+   SetVersion ($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
index bb3bc2d..8eaba32 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
@@ -336,3 +336,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 |).
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
index 4f00524..7b092cd 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
@@ -262,7 +262,8 @@ function verify_images() {
 [% IF ( FRBRizeEditions ) %][% IF ( XISBNS ) %]<li><a href="#editions">Editions</a></li>[% END %][% END %]
 [% IF ( AmazonSimilarItems ) %]<li><a href="#related">Related titles</a></li>[% 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 [% HTML5MediaParent %]</a></li>[% END %][% END %]
+</ul>
 
 <div id="holdings">
 [% IF ( count ) %]
@@ -566,6 +567,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;">
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
index 5e2b55e..fcda163b 100644
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
@@ -12,6 +12,7 @@
 [% IF ( OpacStarRatings != 'disable' ) %]<script type="text/javascript" src="/opac-tmpl/prog/en/lib/jquery/plugins/jquery.rating.js"></script>
 <link rel="stylesheet" type="text/css" href="/opac-tmpl/prog/en/css/jquery.rating.css" />[% END %]
 
+
 <script type="text/JavaScript" language="JavaScript">
 //<![CDATA[
 
@@ -678,6 +679,12 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
 [% IF ( OPACLocalCoverImages ) %][% IF ( localimages ) %]
     <li id="tab_images"><a href="#images">Images</a></li>
 [% END %][% END %]
+
+
+[% IF ( HTML5MediaEnabled ) %][% IF ( HTML5MediaSets ) %]
+    <li id="tab_html5media"><a href="#html5media">Play [% HTML5MediaParent %]</a></li>
+[% END %][% END %]
+
 </ul>
 
 [% IF ( serialcollection ) %]
@@ -1165,6 +1172,20 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
 [% 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 ) %]
 <div id="images">
 <p>Click on an image to view it in the image viewer</p>
diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl
index 0bafabd..c5089a6 100755
--- a/opac/opac-detail.pl
+++ b/opac/opac-detail.pl
@@ -49,6 +49,7 @@ use MARC::Field;
 use List::MoreUtils qw/any none/;
 use C4::Images;
 use Koha::DateUtils;
+use Koha::HTML5Media;
 
 BEGIN {
 	if (C4::Context->preference('BakerTaylorEnabled')) {
@@ -747,6 +748,13 @@ 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 = Koha::HTML5Media->gethtml5media($template,$record);
+}
+
+
 # Amazon.com Stuff
 if ( C4::Context->preference("OPACAmazonEnabled") ) {
     $template->param( AmazonTld => get_amazon_tld() );
-- 
1.7.2.5



More information about the Koha-patches mailing list