/* This file is part of Cinaest. * * Copyright (C) 2009 Philipp Zabel * * Cinaest is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Cinaest is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cinaest. If not, see . */ using Soup; // A single google image search (parsing and retrieval of the poster image URI) public class GoogleImageSearch : Soup.Message { public string poster_uri = null; private bool thumbnail; private string last_chunk = null; public GoogleImageSearch (string search, bool _thumbnail = false) { Object (method: "GET"); uri = new URI ("http://images.google.com/images?imgsz=m&imgar=t&q=" + Uri.escape_string (search, "+", false)); thumbnail = _thumbnail; got_chunk.connect (this.on_got_chunk); } void on_got_chunk (Buffer chunk) { weak string found; if (poster_uri != null) return; // FIXME - store a copy of the last chunk and retry if the parser fails, it can't handle partial input if (last_chunk == null) { // FIXME - can we avoid this copy without ugly hacks? found = chunk.data.ndup (chunk.length).str ("dyn.setResults([["); } else { found = (last_chunk + chunk.data).str ("dyn.setResults([["); last_chunk = null; } if (found != null) { var parser = new GoogleImageSearchParser (found); try { poster_uri = parser.run (thumbnail); if (poster_uri != null) got_poster_uri (this); } catch (Error e) { if (e is ParserError.EOF) { last_chunk = chunk.data.ndup (chunk.length); } else { stdout.printf ("Parser error: %s\n", e.message); } } } } public signal void got_poster_uri (GoogleImageSearch search); } // Encapsulation of a single poster download (google image search query and image file download) public class GooglePosterDownload : Object { private GooglePosterDownloader downloader; private Soup.Session session; public int handle; private string cache_dir; private string cache_filename; private bool cancelled = false; public GooglePosterDownload (string title, string year, int width, int height, int _handle, GooglePosterDownloader _downloader) { var search = title + "+" + year + "+movie+poster"; handle = _handle; downloader = _downloader; session = downloader.session; var message = new GoogleImageSearch (search, (width < 128 && height < 128)); message.got_poster_uri.connect (on_got_poster_uri); session.queue_message (message, google_search_finished); if (width < 128 && height < 128) { // FIXME cache_dir = Path.build_filename (Environment.get_tmp_dir(), "cinaest-thumbnails"); } else { cache_dir = Path.build_filename (Environment.get_user_cache_dir(), "media-art"); } cache_filename = "movie-" + Checksum.compute_for_string (ChecksumType.MD5, (title).down ()) + "-" + Checksum.compute_for_string (ChecksumType.MD5, (year).down ()) + ".jpeg"; } private void on_got_poster_uri (GoogleImageSearch message) { session.cancel_message (message, Soup.KnownStatusCode.OK); } private void google_search_finished (Session session, Message message) { if (cancelled) return; var search_message = (GoogleImageSearch) message; print ("Finished search: %s\n", message.uri.to_string (false)); var poster_message = new Soup.Message ("GET", search_message.poster_uri); session.queue_message (poster_message, poster_downloaded); } private void poster_downloaded (Session session, Message message) { if (cancelled) return; print ("Downloaded poster: %s\n", message.uri.to_string (false)); // Define cache path according to the Media Art Storage Spec (http://live.gnome.org/MediaArtStorageSpec) string cache_path = Path.build_filename (cache_dir, cache_filename); // Make sure the directory .album_arts is available DirUtils.create_with_parents (cache_dir, 0770); try { var file = File.new_for_path (cache_path + ".part"); var stream = file.create (FileCreateFlags.NONE, null); stream.write (message.response_body.data, (size_t) message.response_body.length, null); FileUtils.rename (cache_path + ".part", cache_path); print ("Stored as: %s\n", cache_path); downloader.fetched (handle, cache_path); downloader.timeout_quit (); } catch (Error e) { stdout.printf ("Failed to store poster: %s\n", e.message); } } public void cancel () { cancelled = true; } } // The D-Bus service to manage poster downloads public class GooglePosterDownloader : Object, PosterDownloader { private MainLoop loop; public SessionAsync session; private int fetch_handle = 1; private List downloads = null; private uint source_id; public GooglePosterDownloader () { loop = new MainLoop (null); session = new SessionAsync (); session.max_conns_per_host = 7; } public void timeout_quit () { // With every change we reset the timer to 3min if (source_id != 0) { Source.remove (source_id); } source_id = Timeout.add_seconds (180, quit); } private bool quit () { loop.quit (); // One-shot only return false; } public void run () { loop.run (); } // Implement the PosterDownloader interface public int Fetch (string title, string year, string kind, int width, int height) throws DBus.Error { var download = new GooglePosterDownload (title, year, width, height, ++fetch_handle, this); downloads.append (download); return fetch_handle; } public void Unqueue (int handle) throws DBus.Error { GooglePosterDownload download = null; foreach (GooglePosterDownload d in downloads) { if (d.handle == handle) { download = d; d.cancel (); break; } } if (download != null) { downloads.remove (download); } } static void main () { try { var conn = DBus.Bus.get (DBus.BusType.SESSION); dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); // Try to register service in session bus uint res = bus.request_name ("org.maemo.movieposter.GoogleImages", (uint) 0); if (res == DBus.RequestNameReply.PRIMARY_OWNER) { // Start server var server = new GooglePosterDownloader (); conn.register_object ("/org/maemo/movieposter/GoogleImages", server); server.timeout_quit (); server.run (); } } catch (Error e) { error ("Oops: %s\n", e.message); } } }