// Class BackgroundLoader // Part of Swimming Icons 1 // David Tames, Version 1d, July 14, 2010 // Periodically delete ten images to the pool and add 10 new images. Searches // for images on flickr using tags as specified in the qTags global variable. // Dependent on a php script running on a web server (source code at and of // this file). // Based, in part, on the example posted by Marius Watz at: // http://processing.org/discourse/yabb2/YaBB.pl?board=Integrate;action=display;num=1204990614 // Copyright 2010 by David Tames, some rights reserved. This code is released // under the terms of a Creative Commons Share-Alike Attribution License, for // license text visit http://creativecommons.org/licenses/by-sa/2.5/ and for // attribution please link to: http://kino-eye.com/dmi/swimming-icons/ // Serious Bug: does not stop at 4000.... class BackgroundLoader implements Runnable { // query parameters int qPerPage = 10; // how many images to get each query int qPage = 0; // we'll start on page one (this gets incremented at start of loop) String qTags = "arduino,processing,diy"; // default to something interesting int total = 0; // total images processed so far with this query int qTotal= 0; // total based on Flickr's response int qMax= 4000; // limit you can fetch based on Flickr API documentation PVector here; boolean resultsAvailable = false; // the specific icon in question in loops SwimmingIcon thisIcon; XMLElement xml; void BackgroundLoader() { if (TRACE) println ("BackgroundLoader: created"); } void setQuery(String ns) { qPage = 0; qPerPage = 10; qTags = ns; total = 0; qTotal= 0; if (TRACE) println ("BackgroundLoader: query reset to tags=" + qTags); } void run () { boolean running = true; while (running) { // Delete icons that swim out of bounds for (int i = SwimmingIcons.size()-1; i >= 0; i--) { SwimmingIcon thisIcon = (SwimmingIcon) SwimmingIcons.get(i); PVector here = thisIcon.getPosition(); // did the icon swim out of bounds w/out getting tagged? then delete it! if ((here.x < -80 || here.x > width || here.y < -60 || here.y > height) && !thisIcon.tagged()){ if (TRACE) println ("BackgroundLoader: delete icon (swam out of bounds) " + thisIcon.getImageName()); SwimmingIcons.remove(i); } else if (thisIcon.deleted() == true) { if (TRACE) println ("BackgroundLoader: delete icon (tagged) " + thisIcon.getImageName()); SwimmingIcons.remove(i); } } // Delete icons // if (SwimmingIcons.size() >= iconMaxCount) // get 10 images at a time // right now we don't fetch the total image count from flickr on the // the server, WE NEED TO DO THAT SOON! qPage++; // please run query.php on your own server, source code is // at the end of this file. The functionality of query.php will get // moved into Processing in a future version. String qURL = "http://kino-eye.com/dmi/flickr/query.php"; String qParameters = "?tags=" + qTags + "&p=" + qPage + "&pp=" + qPerPage; // right now we use an array, eventually we'll use an ArrayList of // images so we can delete images and when we do, we'll be loading // more // add more images to the pool (we'll go up to 10 over given this check?) resultsAvailable = true; if (SwimmingIcons.size() < iconMaxCount) { if (TRACE) println ("BackgroundLoader: fetching: page=" + qPage + " perpage=" + qPerPage + " tags=" + qTags); try { xml = new XMLElement(getMyParent(), qURL + qParameters); } catch (Exception e) { if (TRACE) println ("BackgroundLoader: Error accessing server"); resultsAvailable = false; if (TRACE) e.printStackTrace(); } if (!resultsAvailable) { if (TRACE) println ("BackgroundLoader: Data not available from server"); // eventually we'll load some test data here so we can run without net access } else { int page = xml.getIntAttribute("page"); int perpage = xml.getIntAttribute("perpage"); int qTotal = xml.getIntAttribute("total"); // NEED TO DEAL w/ Flickr's 4000 max from one query and when we get less than // 400 back as well, as well better error handling String querytags = xml.getStringAttribute("tags"); String errorsmsg = xml.getStringAttribute("error"); if (errorsmsg.equals("none")) { if (TRACE) println ("BackgroundLoader: query: page=" + page + " perpage=" + perpage + " total=" + qTotal + " error=" + errorsmsg + " tags=" + querytags); XMLElement[] imageResults = xml.getChildren(); if (qTotal > 4000) qTotal = qMax; if (total <= qTotal) { for (int i=0; i < imageResults.length; i++) { String name = imageResults[i].getStringAttribute("name"); if (TRACE) println ("BackgroundLoader: loading image " + total + " " + name); thisIcon = new SwimmingIcon(new PVector(width/2,height/2), initMaxSpeed, initMaxForce); thisIcon.setImageIconURL(imageResults[i].getStringAttribute("icon")); thisIcon.setImageName(name); thisIcon.setImagePageURL(imageResults[i].getStringAttribute("page")); thisIcon.setUserName(imageResults[i].getStringAttribute("user")); thisIcon.setUserPageURL(imageResults[i].getStringAttribute("profile")); thisIcon.setImageTags(imageResults[i].getContent()); //println ("BackgroundLoader: loading " + total + " of " + // qTotal + ": " + imageResults[i].getStringAttribute("name")); SwimmingIcons.add(thisIcon); iconsAvailable = true; total++; // we have at least one icon, so we can start to call // the update/draw functions in the main loop } } } else { if (TRACE) println ("BackgroundLoader: images not loaded, error: " + errorsmsg + "page=" + page + " perpage=" + perpage + " total=" + qTotal + " tags=" + querytags); } } } else { try { // sleep while we're waiting so we don't overtax the CPU if (TRACE) println ("BackgroundLoader: image pool is full, sleeping (try deleting some icons)"); loadThread.sleep(10000); } catch (InterruptedException e) { if (TRACE) println ("BackgroundLoader: error"); if (TRACE) e.printStackTrace(); } } } // end while running } // end run() } // end class definition /******************** begin query.php ************************** // // TAGn1,TAGn2,TAGn3... // ... // // // error="none" if everything worked OK, otherwise, contains error message // returned by Flickr, note, even if total is over 4000, Flickr only returns // a max of 4000 images for any single query. // // // header ("Content-Type:text/xml"); // Credentials $flickr_key = "YOUR-API-KEY"; $flickr_sec = "YOUR-FLICKR-SECRET"; require_once("LOCATION-OF-phpFlickr-ON-YOUR-SERVER"); // Create new phpFlickr object $f = new phpFlickr($flickr_key, $flickr_sec); // enable caching // $f->enableCache("db", "mysql://[username]:[password]@[server]/[database]"); $i = 0; $page=1; // the page to respond with, default to page one and 20 images // api notes: per_page is omitted, it defaults to 100. // The maximum allowed value is 500. $per_page=20; // api note: If omitted, defaults to 1. $total=0; $querymode = "or"; $sort = "interestingness"; // photos_getInfo if ($_GET && $_GET['tags']) { $tags = urlencode($_GET['tags']); $tag_mode = 'any'; $querymode = "or"; if (strstr($tags, '+')) { $tags = str_replace('+', ',', $tags); $tag_mode = 'all'; $querymode = "and"; } else if (strstr($tags, "%2C")) { $tags = str_replace('%2C', ',', $tags); $tag_mode = 'any'; $querymode = "or"; } } if ($_GET && $_GET['p']) { $page = urlencode($_GET['p']); } if ($_GET && $_GET['pp']) { $per_page = urlencode($_GET['pp']); } // look into how Flickr throttles the results. // flickr.photos.search // http://www.flickr.com/services/api/flickr.photos.search.html // Flickr will return at most the first 4,000 results for any query. $photos = $f->photos_search(array('tags' => $tags, 'tag_mode' => $tag_mode, "sort"=>"interestingness-desc", 'page' => $page, 'per_page' => $per_page)); $count = 0; $total = $photos['total']; if ($total==NULL) $total="0"; echo '' . "\n"; $errormessage = "none"; if ($f->getErrorCode()) { $errormessage = $f->getErrorMsg(); echo '' . "\n"; echo "\n"; echo '' . "\n"; exit(); } echo "\n"; echo '' . "\n"; // we get an associative array of the XML results foreach ($photos['photo'] as $photo) { $count++; $owner = $f->people_getInfo($photo['owner']); echo ''; $tags = $f->tags_getListPhoto($photo['id']); $numtags = count ($tags); if (!$f->getErrorCode()) { $first = true; $some = false; foreach($tags as $thisTag) { if ($thisTag['raw']) { $n = count ($thisTag['raw']); // Print_r($thisTag['raw']); if ($first) { echo htmlspecialchars($thisTag['raw']); $first=false; $some=true; } else { echo ", ". htmlspecialchars($thisTag['raw']); } } } if ($some) { echo '' . "\n"; } else { echo 'notag' . "\n"; } } else { echo 'notag' . "\n" . 'Flickr Error: ' . $f->getErrorCode(). ' : ' .$f->getErrorMsg(); echo '' . "\n"; } } echo '' . "\n"; ?> ************************* end query.php *********************************/