]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/AddedContent/OpenLibrary.pm
Merge branch 'master' of git+ssh://yeti.esilibrary.com/home/evergreen/evergreen-equin...
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / AddedContent / OpenLibrary.pm
1 # ---------------------------------------------------------------
2 # Copyright (C) 2009 David Christensen <david.a.christensen@gmail.com>
3 # Copyright (C) 2009-2011 Dan Scott <dscott@laurentian.ca>
4 #
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.
9 #
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 # ---------------------------------------------------------------
15
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/;
23 use Data::Dumper;
24
25 # Edit the <added_content> section of /openils/conf/opensrf.xml
26 # Change <module> to:
27 #   <module>OpenILS::WWW::AddedContent::OpenLibrary</module>
28
29 my $AC = 'OpenILS::WWW::AddedContent';
30
31 # These URLs are always the same for OpenLibrary, so there's no advantage to
32 # pulling from opensrf.xml; we hardcode them here
33
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:';
36
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:';
39
40 my $cover_base_url = 'http://covers.openlibrary.org/b/isbn/';
41
42 sub new {
43     my( $class, $args ) = @_;
44     $class = ref $class || $class;
45     return bless($args, $class);
46 }
47
48 # --------------------------------------------------------------------------
49 sub jacket_small {
50     my( $self, $key ) = @_;
51     return $self->send_img(
52         $self->fetch_cover_response('small', $key));
53 }
54
55 sub jacket_medium {
56     my( $self, $key ) = @_;
57     return $self->send_img(
58         $self->fetch_cover_response('medium', $key));
59
60 }
61 sub jacket_large {
62     my( $self, $key ) = @_;
63     return $self->send_img(
64         $self->fetch_cover_response('large', $key));
65 }
66
67 # --------------------------------------------------------------------------
68
69 sub ebooks_html {
70     my( $self, $key ) = @_;
71     my $book_data_json = $self->fetch_data_response($key)->content();
72
73     $logger->debug("$key: " . $book_data_json);
74
75     my $ebook_html;
76     
77     my $book_data = OpenSRF::Utils::JSON->JSON2perl($book_data_json);
78     my $book_key = (keys %$book_data)[0];
79
80     # We didn't find a matching book; short-circuit our response
81     if (!$book_key) {
82         $logger->debug("$key: no found book");
83         return 0;
84     }
85
86     my $ebooks_json = $book_data->{$book_key}->{ebooks};
87
88     # No ebooks are available for this book; short-circuit
89     if (!$ebooks_json or !scalar(@$ebooks_json)) {
90         $logger->debug("$key: no ebooks");
91         return 0;
92     }
93
94     # Check the availability of the ebooks
95     my $available = $ebooks_json->[0]->{'availability'} || '';
96     if (!$available) {
97         $logger->debug("$key: no available ebooks");
98         return 0;
99     }
100
101     # Build a basic list of available ebook types and their URLs
102     # ebooks appears to be an array containing one element - a hash
103
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'} || '';
107     if ($stream_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");
110     }
111
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') {
116             next;
117         }
118         $ebook_html .= "<li class='ebook_$ebook'><a href='" . 
119             $ebook_formats->{$ebook}->{'url'} . "'>" . uc($ebook) . "</a></li>\n";
120     }
121
122     $logger->debug("$key: $ebook_html");
123     $self->send_html("<ul class='ebooks'>$ebook_html</ul>");
124 }
125
126 sub excerpt_html {
127     my( $self, $key ) = @_;
128     my $book_details_json = $self->fetch_details_response($key)->content();
129
130     $logger->debug("$key: $book_details_json");
131
132     my $excerpt_html;
133     
134     my $book_details = OpenSRF::Utils::JSON->JSON2perl($book_details_json);
135     my $book_key = (keys %$book_details)[0];
136
137     # We didn't find a matching book; short-circuit our response
138     if (!$book_key) {
139         $logger->debug("$key: no found book");
140         return 0;
141     }
142
143     my $first_sentence = $book_details->{$book_key}->{first_sentence};
144     if ($first_sentence) {
145         $excerpt_html .= "<div class='sentence1'>$first_sentence</div>\n";
146     }
147
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";
155         }
156     }
157
158     if (!$excerpt_html) {
159         return 0;
160     }
161
162     $logger->debug("$key: $excerpt_html");
163     $self->send_html("<div class='ac_excerpts'>$excerpt_html</div>");
164 }
165
166 =head1
167
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.
171
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
174 HTML table.
175
176 =cut
177
178 sub toc_html {
179     my( $self, $key ) = @_;
180     my $book_details_json = $self->fetch_details_response($key)->content();
181
182     $logger->debug("$key: " . $book_details_json);
183
184     my $toc_html;
185     
186     my $book_details = OpenSRF::Utils::JSON->JSON2perl($book_details_json);
187     my $book_key = (keys %$book_details)[0];
188
189     # We didn't find a matching book; short-circuit our response
190     if (!$book_key) {
191         $logger->debug("$key: no found book");
192         return 0;
193     }
194
195     my $toc_json = $book_details->{$book_key}->{details}->{table_of_contents};
196
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");
200         return 0;
201     }
202
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};
208         if ($label) {
209             $label .= '. ';
210         }
211         my $title = $chapter->{title} || '';
212         my $page_number = $chapter->{pagenum} || '';
213  
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>" .
218             "</tr>\n";
219     }
220
221     $logger->debug("$key: $toc_html");
222     $self->send_html("<table>$toc_html</table>");
223 }
224
225 sub toc_json {
226     my( $self, $key ) = @_;
227     my $toc = $self->send_json(
228         $self->fetch_details_response($key)
229     );
230 }
231
232 sub send_img {
233     my($self, $response) = @_;
234     return { 
235         content_type => $response->header('Content-type'),
236         content => $response->content, 
237         binary => 1 
238     };
239 }
240
241 sub send_json {
242     my( $self, $content ) = @_;
243     return 0 unless $content;
244
245     return { content_type => 'text/plain', content => $content };
246 }
247
248 sub send_html {
249     my( $self, $content ) = @_;
250     return 0 unless $content;
251
252     # Hide anything that might contain a link since it will be broken
253     my $HTML = <<"    HTML";
254         <div>
255             <style type='text/css'>
256                 div.ac input, div.ac a[href],div.ac img, div.ac button { display: none; visibility: hidden }
257             </style>
258             <div class='ac'>
259                 $content
260             </div>
261         </div>
262     HTML
263
264     return { content_type => 'text/html', content => $HTML };
265 }
266
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);
272     return $response;
273 }
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);
279     return $response;
280 }
281
282 # returns the HTTP response object from the URL fetch
283 sub fetch_cover_response {
284     my( $self, $size, $key ) = @_;
285
286     my $response = $self->fetch_data_response($key)->content();
287
288     my $book_data = OpenSRF::Utils::JSON->JSON2perl($response);
289     my $book_key = (keys %$book_data)[0];
290
291     # We didn't find a matching book; short-circuit our response
292     if (!$book_key) {
293         $logger->debug("$key: no found book");
294         return 0;
295     }
296
297     my $covers_json = $book_data->{$book_key}->{cover};
298     if (!$covers_json) {
299         $logger->debug("$key: no covers for this book");
300         return 0;
301     }
302
303     $logger->debug("$key: " . $covers_json->{$size});
304     return $AC->get_url($covers_json->{$size}) || 0;
305 }
306
307
308 1;