1 # ---------------------------------------------------------------
2 # Copyright (C) 2009 David Christensen <david.a.christensen@gmail.com>
3 # Copyright (C) 2009-2011 Dan Scott <dscott@laurentian.ca>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
16 package OpenILS::WWW::AddedContent::OpenLibrary;
17 use strict; use warnings;
18 use OpenSRF::Utils::Logger qw/$logger/;
19 use OpenSRF::Utils::SettingsParser;
20 use OpenILS::WWW::AddedContent;
21 use OpenSRF::Utils::JSON;
22 use OpenSRF::EX qw/:try/;
25 # Edit the <added_content> section of /openils/conf/opensrf.xml
27 # <module>OpenILS::WWW::AddedContent::OpenLibrary</module>
29 my $AC = 'OpenILS::WWW::AddedContent';
31 # These URLs are always the same for OpenLibrary, so there's no advantage to
32 # pulling from opensrf.xml; we hardcode them here
34 # jscmd=details is unstable but includes goodies such as Table of Contents
35 my $base_url_details = 'http://openlibrary.org/api/books?format=json&jscmd=details&bibkeys=ISBN:';
37 # jscmd=data is stable and contains links to ebooks, excerpts, etc
38 my $base_url_data = 'http://openlibrary.org/api/books?format=json&jscmd=data&bibkeys=ISBN:';
40 my $cover_base_url = 'http://covers.openlibrary.org/b/isbn/';
43 my( $class, $args ) = @_;
44 $class = ref $class || $class;
45 return bless($args, $class);
48 # --------------------------------------------------------------------------
50 my( $self, $key ) = @_;
51 return $self->send_img(
52 $self->fetch_cover_response('small', $key));
56 my( $self, $key ) = @_;
57 return $self->send_img(
58 $self->fetch_cover_response('medium', $key));
62 my( $self, $key ) = @_;
63 return $self->send_img(
64 $self->fetch_cover_response('large', $key));
67 # --------------------------------------------------------------------------
70 my( $self, $key ) = @_;
71 my $book_data_json = $self->fetch_data_response($key)->content();
73 $logger->debug("$key: " . $book_data_json);
77 my $book_data = OpenSRF::Utils::JSON->JSON2perl($book_data_json);
78 my $book_key = (keys %$book_data)[0];
80 # We didn't find a matching book; short-circuit our response
82 $logger->debug("$key: no found book");
86 my $ebooks_json = $book_data->{$book_key}->{ebooks};
88 # No ebooks are available for this book; short-circuit
89 if (!$ebooks_json or !scalar(@$ebooks_json)) {
90 $logger->debug("$key: no ebooks");
94 # Check the availability of the ebooks
95 my $available = $ebooks_json->[0]->{'availability'} || '';
97 $logger->debug("$key: no available ebooks");
101 # Build a basic list of available ebook types and their URLs
102 # ebooks appears to be an array containing one element - a hash
104 # First element of the hash is 'read_url' which is a URL to
105 # Internet Archive online reader
106 my $stream_url = $ebooks_json->[0]->{'read_url'} || '';
108 $ebook_html .= "<li class='ebook_stream'><a href='$stream_url'>Read online</a></li>\n";
109 $logger->debug("$key: stream URL = $stream_url");
112 my $ebook_formats = $ebooks_json->[0]->{'formats'} || '';
113 # Next elements are various ebook formats that are available
114 foreach my $ebook (keys %{$ebook_formats}) {
115 if ($ebook_formats->{$ebook} eq 'read_url') {
118 $ebook_html .= "<li class='ebook_$ebook'><a href='" .
119 $ebook_formats->{$ebook}->{'url'} . "'>" . uc($ebook) . "</a></li>\n";
122 $logger->debug("$key: $ebook_html");
123 $self->send_html("<ul class='ebooks'>$ebook_html</ul>");
127 my( $self, $key ) = @_;
128 my $book_details_json = $self->fetch_details_response($key)->content();
130 $logger->debug("$key: $book_details_json");
134 my $book_details = OpenSRF::Utils::JSON->JSON2perl($book_details_json);
135 my $book_key = (keys %$book_details)[0];
137 # We didn't find a matching book; short-circuit our response
139 $logger->debug("$key: no found book");
143 my $first_sentence = $book_details->{$book_key}->{first_sentence};
144 if ($first_sentence) {
145 $excerpt_html .= "<div class='sentence1'>$first_sentence</div>\n";
148 my $excerpts_json = $book_details->{$book_key}->{excerpts};
149 if ($excerpts_json && scalar(@$excerpts_json)) {
150 # Load up excerpt text with comments in tooltip
151 foreach my $excerpt (@$excerpts_json) {
152 my $text = $excerpt->{text};
153 my $cmnt = $excerpt->{comment};
154 $excerpt_html .= "<div class='ac_excerpt' title='$text'>$cmnt</div>\n";
158 if (!$excerpt_html) {
162 $logger->debug("$key: $excerpt_html");
163 $self->send_html("<div class='ac_excerpts'>$excerpt_html</div>");
168 OpenLibrary returns a JSON hash of zero or more book responses matching our
169 request. Each response may contain a table of contents within the details
170 section of the response.
172 For now, we check only the first response in the hash for a table of
173 contents, and if we find a table of contents, we transform it to a simple
179 my( $self, $key ) = @_;
180 my $book_details_json = $self->fetch_details_response($key)->content();
182 $logger->debug("$key: " . $book_details_json);
186 my $book_details = OpenSRF::Utils::JSON->JSON2perl($book_details_json);
187 my $book_key = (keys %$book_details)[0];
189 # We didn't find a matching book; short-circuit our response
191 $logger->debug("$key: no found book");
195 my $toc_json = $book_details->{$book_key}->{details}->{table_of_contents};
197 # No table of contents is available for this book; short-circuit
198 if (!$toc_json or !scalar(@$toc_json)) {
199 $logger->debug("$key: no TOC");
203 # Build a basic HTML table containing the section number, section title,
204 # and page number. Some rows may not contain section numbers, we should
205 # protect against empty page numbers too.
206 foreach my $chapter (@$toc_json) {
207 my $label = $chapter->{label};
211 my $title = $chapter->{title} || '';
212 my $page_number = $chapter->{pagenum} || '';
214 $toc_html .= '<tr>' .
215 "<td class='toc_label'>$label</td>" .
216 "<td class='toc_title'>$title</td>" .
217 "<td class='toc_page'>$page_number</td>" .
221 $logger->debug("$key: $toc_html");
222 $self->send_html("<table>$toc_html</table>");
226 my( $self, $key ) = @_;
227 my $toc = $self->send_json(
228 $self->fetch_details_response($key)
233 my($self, $response) = @_;
235 content_type => $response->header('Content-type'),
236 content => $response->content,
242 my( $self, $content ) = @_;
243 return 0 unless $content;
245 return { content_type => 'text/plain', content => $content };
249 my( $self, $content ) = @_;
250 return 0 unless $content;
252 # Hide anything that might contain a link since it will be broken
253 my $HTML = <<" HTML";
255 <style type='text/css'>
256 div.ac input, div.ac a[href],div.ac img, div.ac button { display: none; visibility: hidden }
264 return { content_type => 'text/html', content => $HTML };
267 # returns the HTTP response object from the URL fetch
268 sub fetch_data_response {
269 my( $self, $key ) = @_;
270 my $url = $base_url_data . "$key";
271 my $response = $AC->get_url($url);
274 # returns the HTTP response object from the URL fetch
275 sub fetch_details_response {
276 my( $self, $key ) = @_;
277 my $url = $base_url_details . "$key";
278 my $response = $AC->get_url($url);
282 # returns the HTTP response object from the URL fetch
283 sub fetch_cover_response {
284 my( $self, $size, $key ) = @_;
286 my $response = $self->fetch_data_response($key)->content();
288 my $book_data = OpenSRF::Utils::JSON->JSON2perl($response);
289 my $book_key = (keys %$book_data)[0];
291 # We didn't find a matching book; short-circuit our response
293 $logger->debug("$key: no found book");
297 my $covers_json = $book_data->{$book_key}->{cover};
299 $logger->debug("$key: no covers for this book");
303 $logger->debug("$key: " . $covers_json->{$size});
304 return $AC->get_url($covers_json->{$size}) || 0;