Create a widget for Goodreads with the latest books read | Flavio's blog
 

Create a widget for Goodreads with the latest books read

goodreads widget

Goodreads is an excellent (and more flexible) alternative to the famous Anobii, that allows you to catalog your books, review them, get advice on books related to your own interests.

Unlike Anobii, Goodreads offers more elaborate APIs, and that allows us to create a widget to put on our website, to show the latest books read, and the book you are reading now (if you are reading a book, and it is in the shelf “currently-reading” di Goodreads).

As shown in the beginning of the post, the widget displays the covers of our books and, when hovering with the mouse little arrow on one of these, a pop-up that shows the title, the author, and the description of the book.

Also, I added a little’ CSS code (which does NOT work, Unfortunately, with Internet Explorer 9) to rotate slightly the book cover, when the little mouse arrow hovers over it.

Clicking on a book, will open the corresponding page on Goodreads.

After checking into the site and entering our books, go on http://www.goodreads.com/api/keys to recover your API Key.

Let's see the code of the widget:

<?php
/* Goodreads.com PHP widget for websites. It shows the last $number_books read books or,if there's currently a book you're reading in "currently-reading" bookshelf, display it together with $number_books-1 read books.
Insert your API key, your Goodreads userid, the number of books to show, the expiration of the cachefile and the cachefile name.
Book covers images are shown. title, author and description (120 characters) are shown in the tooltip that appears when you hover on one of the books. Also, the date you started reading the book in the "currently-reading" shelf is displayed.
Descriptions are not shown in Internet Explorer because it's a crappy browser and doesn't support break lines in the tooltips. 
Please respect Goodreads API terms!
(C) flapane.com - Oct-2013*/

$api_key = "yourapikey";        //goodreads api key
$number_books = "5";    //how many books from "read" shelf
$user_id = "youruserid";    //goodreads user id
$expiretime=24;          // cache expire time in hours. Goodreads does not allow more than 1 day
"goodreads.cache"; //name of the cachefile

function limitString($string, $limit = 120) {    //shorten book descriptions but do not cut words. From Stackoverflow
    // Return early if the string is already shorter than the limit
    if(strlen($string) < $limit) {return $string;}

    $regex = "/(.{1,$limit})\b/";
    preg_match($regex, $string, $matches);
    return $matches[1];
}

//do not mess with the next lines!!!
if (!file_exists($cacheName)) {    //create the cachefile if it doesn't exist
     $create = fopen($cacheName, 'W');
      chmod ("$cacheName", 0644); //set chmod 644
     fclose($create);
}

// Is the file older than $expiretime, or the file is new/empty?
$FileAge = time() - filectime($cacheName);    // Calculate file age in seconds
if ($FileAge > ($expiretime * 3600) || 0 == filesize($cacheName))
{
$handle = fopen($cacheName, 'wb');

$read_books_path = "http://www.goodreads.com/review/list/".$user_id.".xml?key=".$api_key."&v = 2&shelf=read&sort=date_read&order=d&page=1&per_page =".$number_books;
$read_books_xml_query = file_get_contents($read_books_path); 
$read_books_json_array = json_decode(json_encode((array)simplexml_load_string($read_books_xml_query)),1);    //from http://gilbert.pellegrom.me/php-quick-convert-xml-to-array/
sleep(1);    //Goodreads allows 1 API call per second
$reading_books_path = "http://www.goodreads.com/review/list/".$user_id.".xml?key=".$api_key."&v = 2&shelf=currently-reading&sort=date_read&order=d&page=1&per_page = 1";    //get last currently reading book
$reading_books_xml_query = file_get_contents($reading_books_path); 
$reading_books_json_array = json_decode(json_encode((array)simplexml_load_string($reading_books_xml_query)),1);    //from http://gilbert.pellegrom.me/php-quick-convert-xml-to-array/

for($i=0; $I<$number_books; $i  ){
$read_titles[$I] = $read_books_json_array['reviews']['review'][$I]['book']['title'];
$read_links[$I] = $read_books_json_array['reviews']['review'][$I]['book']['link'];
$read_authors[$I] = $read_books_json_array['reviews']['review'][$I]['book']['authors']['author']['name'];
$read_cover_images[$I] = $read_books_json_array['reviews']['review'][$I]['book']['image_url'];

    if (!empty($read_books_json_array['reviews']['review'][$I]['book']['description']))    //avoid empty descriptions
    {
    $read_descriptions[$I] = limitString(strip_tags($read_books_json_array['reviews']['review'][$I]['book']['description'])) . "[...]";    //strip html tags and cut description
    $read_descriptions[$I] = str_replace("\"", "'", $read_descriptions[$I]);    //avoid double quotes
    }
    else $read_descriptions[$I] = "No description / No description";
}

if (!empty($reading_books_json_array['reviews']['review']['book']))    //are you currently reading any book and is it in "currently-reading" shelf?
{
$reading_titles = $reading_books_json_array['reviews']['review']['book']['title'];
$reading_links = $reading_books_json_array['reviews']['review']['book']['link'];
$reading_authors = $reading_books_json_array['reviews']['review']['book']['authors']['author']['name'];
$reading_cover_images = $reading_books_json_array['reviews']['review']['book']['image_url'];
$reading_start_date = $reading_books_json_array['reviews']['review']['started_at'];
$reading_descriptions = $reading_books_json_array['reviews']['review']['book']['description'];

    if (!empty($reading_start_date))    //avoid empty  "date I started this book" fields
    {
    $tz = new DateTimeZone('GMT');
    $date = new DateTime($reading_start_date);
    $date->setTimeZone($tz);
    $reading_start_date = '- Started on '.$date->format('L, j M Y g:I:s A T');    //date you started reading the current book in UTC time
    }

    if (!empty($reading_descriptions))    //avoid empty descriptions
    {
    $reading_descriptions = limitString(strip_tags($reading_books_json_array['reviews']['review']['book']['description'])) . "[...]";    //strip html tags and cut description
            if (isset($read_descriptions[$I])){
            $read_descriptions[$I] = str_replace("\"", "'", $read_descriptions[$I]);    //avoid double quotes
        }
    }
    else $reading_descriptions = "No description / No description";

//show the "currently-reading" book and the $number_books-1 most recently read books
$data[0] = "<div class='goodreads-widget'>";
$data[1] = "<a href='$reading_links' target='_blank' title=\"$reading_authors - $reading_titles $reading_start_date&#13;Desc: $reading_descriptions\"><img src='$reading_cover_images' alt=\"$reading_authors - $reading_titles\" /></A>";
    for($i=0; $I<$number_books-1; $i  ){
        $data[2+$I] = "<a href='$read_links[$I]' target='_blank' title=\"$read_authors[$I] - $read_titles[$I]&#13;Desc: $read_descriptions[$I]\"><img src='$read_cover_images[$I]'Alt = "$read_authors[$I] - $read_titles[$I]\" /></A>";
    }
$data[$number_books 1] = "</div>";    
}
else    {    //else show only $number_books "read" books
    $data[0] = "<div class='goodreads-widget'>";
    for($i=0; $I<$number_books; $i  ){
        $data[1+$I] = "<a href='$read_links[$I]' target='_blank' title=\"$read_authors[$I] - $read_titles[$I]&#13;Desc: $read_descriptions[$I]\"><img src='$read_cover_images[$I]'Alt = "$read_authors[$I] - $read_titles[$I]\" /></A>";
    }
    $data[$number_books 1] = "</div>";
}

foreach($data as $value){    //write the output to the cachefile
                fwrite($handle, $value);
        }
fclose($handle);
}

//cachefile is fresh enough... outputting data
$data_cache=file_get_contents($cacheName);
echo $data_cache;
?>

And its CSS code:

<style type="text/css">
.goodreads-widget img {
	width: 98px;
	height: 160px;
	border:0px;
	-webkit-transition: all 0.5s ease;
	-moz-transition: all 0.5s ease;
	-o-transition: all 0.5s ease;
	-ms-transition: all 0.5s ease;
	transition: all 0.5s ease;
	}
.goodreads-widget img:hover {
	-webkit-transform: rotate(-10you);
	-moz-transform: rotate(-10you);
	-o-transform: rotate(-10you);
	-ms-transform: rotate(-10you);
	transform: rotate(-10you);
	-sand-transform: rotate(-10you);
	}
</style>

As I wrote in the comments, just enter your API Key ($api_key), the number of books to show ($number_books), your user id Goodreads ($user_id), the maximum time duration of the cache file ($expiretime)(Goodreads does not allow to store data for more than 24 hours), and the name of the cache file ($cacheName).

I suggest to carefully read the terms of use of Goodreads (click here), where it says what to do to not violate the terms of use.

The script retrieves the desired number of books read ($number_books), and goes to check if, at the moment, we are reading a book (that, Then, will be on the currently-reading shelf of Goodreads).

If we are reading a book, the script will show this book, and the last $number_books-1 books read.

If at this time we are not reading any book, the script will show the latest $number_books Read.

The API returns responses in XML, but, for my preference and convenience, I decided to convert it to JSON.

In the cache file will be saved only the HTML code of the widget, so as to minimize the size of the file.

Any suggestions? Problems? Did you put the widget on your site? Please leave a comment! 🙂

 

0 Comments »

Please accept third-party cookies to be able to comment on the post! The CHANGE COOKIE CHOICES button is located in the footer of the site. / In order to comment this post, please accept the third party cookies! The button CAMBIA LE SCELTE DEI COOKIE is in the footer of the website.