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 # This URL is always the same for OpenLibrary, so there's no advantage to
32 # pulling from opensrf.xml
34 my $read_api = 'http://openlibrary.org/api/volumes/brief/json/';
37 my( $class, $args ) = @_;
38 $class = ref $class || $class;
39 return bless($args, $class);
42 # --------------------------------------------------------------------------
44 my( $self, $key ) = @_;
45 return $self->send_img(
46 $self->fetch_cover_response('small', $key));
50 my( $self, $key ) = @_;
51 return $self->send_img(
52 $self->fetch_cover_response('medium', $key));
56 my( $self, $key ) = @_;
57 return $self->send_img(
58 $self->fetch_cover_response('large', $key));
61 # --------------------------------------------------------------------------
64 my( $self, $key ) = @_;
65 my $book_data_json = $self->fetch_response($key);
67 $logger->debug("$key: " . $book_data_json);
71 my $book_data = OpenSRF::Utils::JSON->JSON2perl($book_data_json);
72 my $book_key = (keys %$book_data)[0];
74 # We didn't find a matching book; short-circuit our response
76 $logger->debug("$key: no found book");
80 my $ebooks_json = $book_data->{$book_key}->{ebooks};
82 # No ebooks are available for this book; short-circuit
83 if (!$ebooks_json or !scalar(@$ebooks_json)) {
84 $logger->debug("$key: no ebooks");
88 # Check the availability of the ebooks
89 my $available = $ebooks_json->[0]->{'availability'} || '';
91 $logger->debug("$key: no available ebooks");
95 # Build a basic list of available ebook types and their URLs
96 # ebooks appears to be an array containing one element - a hash
98 # First element of the hash is 'read_url' which is a URL to
99 # Internet Archive online reader
100 my $stream_url = $ebooks_json->[0]->{'read_url'} || '';
102 $ebook_html .= "<li class='ebook_stream'><a href='$stream_url'>Read online</a></li>\n";
103 $logger->debug("$key: stream URL = $stream_url");
106 my $ebook_formats = $ebooks_json->[0]->{'formats'} || '';
107 # Next elements are various ebook formats that are available
108 foreach my $ebook (keys %{$ebook_formats}) {
109 if ($ebook_formats->{$ebook} eq 'read_url') {
112 $ebook_html .= "<li class='ebook_$ebook'><a href='" .
113 $ebook_formats->{$ebook}->{'url'} . "'>" . uc($ebook) . "</a></li>\n";
116 $logger->debug("$key: $ebook_html");
117 $self->send_html("<ul class='ebooks'>$ebook_html</ul>");
121 my( $self, $key ) = @_;
125 my $content = $self->fetch_details_response($key)->content();
127 my $first_sentence = $content->{first_sentence};
128 if ($first_sentence) {
129 $excerpt_html .= "<div class='sentence1'>$first_sentence</div>\n";
132 my $excerpts_json = $content->{excerpts};
133 if ($excerpts_json && scalar(@$excerpts_json)) {
134 # Load up excerpt text with comments in tooltip
135 foreach my $excerpt (@$excerpts_json) {
136 my $text = $excerpt->{text};
137 my $cmnt = $excerpt->{comment};
138 $excerpt_html .= "<div class='ac_excerpt' title='$text'>$cmnt</div>\n";
142 if (!$excerpt_html) {
146 $logger->debug("$key: $excerpt_html");
147 $self->send_html("<div class='ac_excerpts'>$excerpt_html</div>");
152 OpenLibrary returns a JSON hash of zero or more book responses matching our
153 request. Each response may contain a table of contents within the details
154 section of the response.
156 For now, we check only the first response in the hash for a table of
157 contents, and if we find a table of contents, we transform it to a simple
163 my( $self, $key ) = @_;
167 my $book_data = $self->fetch_data_response($key) || return 0;
169 my $toc_json = $book_data->{table_of_contents};
171 # No table of contents is available for this book; short-circuit
172 if (!$toc_json or !scalar(@$toc_json)) {
173 $logger->debug("$key: no TOC");
177 # Build a basic HTML table containing the section number, section title,
178 # and page number. Some rows may not contain section numbers, we should
179 # protect against empty page numbers too.
180 foreach my $chapter (@$toc_json) {
181 my $label = $chapter->{label};
185 my $title = $chapter->{title} || '';
186 my $page_number = $chapter->{pagenum} || '';
188 $toc_html .= '<tr>' .
189 "<td class='toc_label'>$label</td>" .
190 "<td class='toc_title'>$title</td>" .
191 "<td class='toc_page'>$page_number</td>" .
195 $logger->debug("$key: $toc_html");
196 $self->send_html("<table>$toc_html</table>");
200 my( $self, $key ) = @_;
201 my $toc = $self->send_json(
202 $self->fetch_response($key)
207 my($self, $response) = @_;
209 content_type => $response->header('Content-type'),
210 content => $response->content,
216 my( $self, $content ) = @_;
217 return 0 unless $content;
219 return { content_type => 'text/plain', content => $content };
223 my( $self, $content ) = @_;
224 return 0 unless $content;
226 # Hide anything that might contain a link since it will be broken
227 my $HTML = <<" HTML";
229 <style type='text/css'>
230 div.ac input, div.ac a[href],div.ac img, div.ac button { display: none; visibility: hidden }
238 return { content_type => 'text/html', content => $HTML };
241 # returns the HTTP response object from the URL fetch
243 my( $self, $key ) = @_;
245 # TODO: OpenLibrary can also accept lccn, oclc, olid...
246 # Hardcoded to only accept ISBNs for now.
249 my $url = $read_api . $key;
250 my $response = $AC->get_url($url)->content();
252 $logger->debug("$key: response was $response");
254 my $book_results = OpenSRF::Utils::JSON->JSON2perl($response);
255 my $record = $book_results->{$key};
257 # We didn't find a matching book; short-circuit our response
259 $logger->debug("$key: no found record");
266 sub fetch_data_response {
267 my ($self, $key) = @_;
269 my $book_results = $self->fetch_response($key);
271 my $book_key = (keys %{$book_results->{records}})[0];
273 $logger->debug("$key: using record key $book_key");
275 # We didn't find a matching book; short-circuit our response
276 if (!$book_key || !$book_results->{records}->{$book_key}->{data}) {
277 $logger->debug("$key: no found book");
281 return $book_results->{records}->{$book_key}->{data};
285 sub fetch_details_response {
286 my ($self, $key) = @_;
288 my $book_results = $self->fetch_response($key);
290 my $book_key = (keys %{$book_results->{records}})[0];
292 $logger->debug("$key: using record key $book_key");
294 # We didn't find a matching book; short-circuit our response
296 $logger->debug("$key: no found book");
300 return $book_results->{$book_key}->{details};
303 sub fetch_items_response {
304 my ($self, $key) = @_;
306 my $book_results = $self->fetch_response($key);
308 my $items = $book_results->{items};
310 # We didn't find a matching book; short-circuit our response
311 if (!$items || scalar(@$items) == 0) {
312 $logger->debug("$key: no found items");
316 return $book_results->{items};
320 # returns a cover image from the list of associated items
321 # TODO: Look for the best match in the array of items
322 sub fetch_cover_response {
323 my( $self, $size, $key ) = @_;
325 my $items = $self->fetch_items_response($key);
327 $logger->debug("$key: items request got " . scalar(@$items) . " items back");
329 foreach my $item (@$items) {
330 if ($item->{cover}) {
331 return $AC->get_url($item->{cover}->{$size}) || 0;
335 $logger->debug("$key: no covers for this book");