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('-S.jpg', $key));
56 my( $self, $key ) = @_;
57 return $self->send_img(
58 $self->fetch_cover_response('-M.jpg', $key));
62 my( $self, $key ) = @_;
63 return $self->send_img(
64 $self->fetch_cover_response('-L.jpg', $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 OpenLibrary returns a JSON hash of zero or more book responses matching our
128 request. Each response may contain a table of contents within the details
129 section of the response.
131 For now, we check only the first response in the hash for a table of
132 contents, and if we find a table of contents, we transform it to a simple
138 my( $self, $key ) = @_;
139 my $book_details_json = $self->fetch_details_response($key)->content();
141 $logger->debug("$key: " . $book_details_json);
145 my $book_details = OpenSRF::Utils::JSON->JSON2perl($book_details_json);
146 my $book_key = (keys %$book_details)[0];
148 # We didn't find a matching book; short-circuit our response
150 $logger->debug("$key: no found book");
154 my $toc_json = $book_details->{$book_key}->{details}->{table_of_contents};
156 # No table of contents is available for this book; short-circuit
157 if (!$toc_json or !scalar(@$toc_json)) {
158 $logger->debug("$key: no TOC");
162 # Build a basic HTML table containing the section number, section title,
163 # and page number. Some rows may not contain section numbers, we should
164 # protect against empty page numbers too.
165 foreach my $chapter (@$toc_json) {
166 my $label = $chapter->{label};
170 my $title = $chapter->{title} || '';
171 my $page_number = $chapter->{pagenum} || '';
173 $toc_html .= '<tr>' .
174 "<td class='toc_label'>$label</td>" .
175 "<td class='toc_title'>$title</td>" .
176 "<td class='toc_page'>$page_number</td>" .
180 $logger->debug("$key: $toc_html");
181 $self->send_html("<table>$toc_html</table>");
185 my( $self, $key ) = @_;
186 my $toc = $self->send_json(
187 $self->fetch_details_response($key)
192 my($self, $response) = @_;
194 content_type => $response->header('Content-type'),
195 content => $response->content,
201 my( $self, $content ) = @_;
202 return 0 unless $content;
204 return { content_type => 'text/plain', content => $content };
208 my( $self, $content ) = @_;
209 return 0 unless $content;
211 # Hide anything that might contain a link since it will be broken
212 my $HTML = <<" HTML";
214 <style type='text/css'>
215 div.ac input, div.ac a[href],div.ac img, div.ac button { display: none; visibility: hidden }
223 return { content_type => 'text/html', content => $HTML };
226 # returns the HTTP response object from the URL fetch
227 sub fetch_data_response {
228 my( $self, $key ) = @_;
229 my $url = $base_url_data . "$key";
230 my $response = $AC->get_url($url);
233 # returns the HTTP response object from the URL fetch
234 sub fetch_details_response {
235 my( $self, $key ) = @_;
236 my $url = $base_url_details . "$key";
237 my $response = $AC->get_url($url);
241 # returns the HTTP response object from the URL fetch
242 sub fetch_cover_response {
243 my( $self, $size, $key ) = @_;
244 my $url = $cover_base_url . "$key$size";
245 return $AC->get_url($url);