X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fposter%2Fgoogle-poster-downloader.vala;h=423d017b0b4719df5f9eba1ce8a0434632b1c472;hb=6468272248974610829f2301ddc87e24c4d9f87f;hp=5dee8c2b75496019547a09af4eff47a175a88bb5;hpb=52b7b48e0102fc22f46c8b67f6df6a6d7b38a2f9;p=cinaest
diff --git a/src/poster/google-poster-downloader.vala b/src/poster/google-poster-downloader.vala
index 5dee8c2..423d017 100644
--- a/src/poster/google-poster-downloader.vala
+++ b/src/poster/google-poster-downloader.vala
@@ -1,223 +1,234 @@
-using GLib;
-
-[DBus (name = "org.maemo.movieposter.Provider", signals = "fetched")]
-public interface Provider {
- public abstract int Fetch (string title, string year, string kind) throws DBus.Error;
- public abstract int FetchThumbnail (string title, string year, string kind) throws DBus.Error;
- public abstract void Unqueue (int handle) throws DBus.Error;
- public signal void fetched (int handle, string path);
-}
-
-// http://live.gnome.org/MediaArtStorageSpec
-
-// Sample implementation of org.maemo.movieposter.Provider that uses Google
-// images's first result as movie poster. There is of course no certainty
-// that the first result on Google images is indeed the movie's poster - there
-// are quite a few false positives, especially for non-mainstream movies.
+/* 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);
+ }
-public class GoogleImages : Object, Provider {
- int fetch_handle;
- Cancellable cancellable;
+ void on_got_chunk (Buffer chunk) {
+ weak string found;
- public int Fetch (string title, string year, string kind) throws DBus.Error {
- int handle = fetch_handle++;
+ if (poster_uri != null)
+ return;
- _fetch_async.begin (title, year, kind, false, handle);
- return handle;
+ // 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 int FetchThumbnail (string title, string year, string kind) throws DBus.Error {
- int handle = fetch_handle++;
+ signal void got_poster_uri (GoogleImageSearch search);
+}
- _fetch_async.begin (title, year, kind, true, handle);
- return handle;
+// 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, bool thumbnail, int _handle, GooglePosterDownloader _downloader) {
+ var search = title + "+" + year + "+movie+poster";
+
+ handle = _handle;
+ downloader = _downloader;
+ session = downloader.session;
+
+ var message = new GoogleImageSearch (search, thumbnail);
+ message.got_poster_uri.connect (on_got_poster_uri);
+ session.queue_message (message, google_search_finished);
+
+ if (thumbnail) {
+ // 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";
}
- public void Unqueue (int handle) {
- // FIXME - cancel everything for now
- cancellable.cancel ();
+ private void on_got_poster_uri (GoogleImageSearch message) {
+ session.cancel_message (message, Soup.KnownStatusCode.OK);
}
- private async void _fetch_async (string title, string year, string kind, bool thumbnail, int handle) {
- uint u = 0;
- size_t hread = 0;
- string [] pieces = title.split (" ", -1);
- string stitched = "";
- bool first = true;
+ private void google_search_finished (Session session, Message message) {
+ if (cancelled)
+ return;
- // FIXME - cancel everything for now
- cancellable = new Cancellable ();
+ var search_message = (GoogleImageSearch) message;
+ print ("Finished search: %s\n", message.uri.to_string (false));
- stdout.printf ("Fetching %s \"%s (%s)\" ...\n", kind, title, year);
+ var poster_message = new Soup.Message ("GET", search_message.poster_uri);
+ session.queue_message (poster_message, poster_downloaded);
+ }
- if (title == null || title == "")
- title = " ";
+ private void poster_downloaded (Session session, Message message) {
+ if (cancelled)
+ return;
- if (year == null || year == "")
- year = " ";
+ print ("Downloaded poster: %s\n", message.uri.to_string (false));
- // Convert the title and year into something that will work for Google images
+ // 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);
- while (pieces[u] != null) {
- if (!first)
- stitched += "+";
- stitched += pieces[u];
- u++;
- first = false;
- }
+ // Make sure the directory .album_arts is available
+ DirUtils.create_with_parents (cache_dir, 0770);
- stitched += "+";
+ try {
+ var file = File.new_for_path (cache_path + ".part");
+ var stream = file.create (FileCreateFlags.NONE, null);
- u = 0;
- first = true;
- pieces = year.split (" ", -1);
+ stream.write (message.response_body.data, (size_t) message.response_body.length, null);
- while (pieces[u] != null) {
- if (!first)
- stitched += "+";
- stitched += pieces[u];
- u++;
- first = false;
- }
+ FileUtils.rename (cache_path + ".part", cache_path);
- stitched += "+movie+poster";
+ print ("Stored as: %s\n", cache_path);
- // Start the query on Google images
+ downloader.fetched (handle, cache_path);
+ downloader.timeout_quit ();
+ } catch (Error e) {
+ stdout.printf ("Failed to store poster: %s\n", e.message);
+ }
+ }
- stdout.printf("GET http://images.google.com/images?q=" + Uri.escape_string (stitched, "+", false) + "\n");
+ public void cancel () {
+ cancelled = true;
+ }
+}
- File google_search = File.new_for_uri ("http://images.google.com/images?q=" + Uri.escape_string (stitched, "+", false));
+// 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;
- try {
- char [] buffer = new char [40000];
- string asstring;
- size_t total = 0;
+ public GooglePosterDownloader () {
+ loop = new MainLoop (null);
- // Fetch the first page
+ session = new SessionAsync ();
+ session.max_conns_per_host = 7;
+ }
- InputStream stream = google_search.read (null);
+ 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);
+ }
- while (total < 40000) {
- hread = yield stream.read_async ((char *)buffer + total, 40000 - total, Priority.DEFAULT_IDLE, cancellable);
- total += hread;
- if (cancellable.is_cancelled ()) {
- stdout.printf ("CANCELED\n");
- return;
- }
- if (hread == 0)
- break;
- }
- buffer[total] = 0;
-
- asstring = (string) buffer;
-
- // Find the first result
-
- string found = null;
- int i;
- char end;
- if (thumbnail) {
- do {
- found = asstring.str ("http://t");
- } while (found != null && !found.offset (9).has_prefix (".gstatic.com/images?q=tbn"));
- i = 0;
- end = ' ';
- } else {
- found = asstring.str ("href=/imgres?imgurl=");
- i = 20;
- end = '&';
- }
+ private bool quit () {
+ loop.quit ();
- if (found != null) {
+ // One-shot only
+ return false;
+ }
- StringBuilder url = new StringBuilder ();
- long y = found.len();
+ public void run () {
+ loop.run ();
+ }
- while (found[i] != end && i < y) {
- url.append_unichar (found[i]);
- i++;
- }
+ // Implement the PosterDownloader interface
+ public int Fetch (string title, string year, string kind) throws DBus.Error {
+ var download = new GooglePosterDownload (title, year, false, ++fetch_handle, this);
- string cache_path;
+ downloads.append (download);
- string cache_dir;
- if (thumbnail) {
- // 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");
- }
+ return fetch_handle;
+ }
- // Define cache path = ~/.album_art/MD5 (down (albumartist)).jpeg
-
- cache_path = Path.build_filename (cache_dir, kind + "-" +
- Checksum.compute_for_string (
- ChecksumType.MD5,
- (title).down (),
- -1) + "-" +
- Checksum.compute_for_string (
- ChecksumType.MD5,
- (year).down (),
- -1) +
- ".jpeg", null);
-
- // Make sure the directory .album_arts is available
- DirUtils.create_with_parents (cache_dir, 0770);
-
- stdout.printf ("GET %s --> %s\n", url.str, cache_path);
-
- File online_image = File.new_for_uri (url.str);
- File cache_image = File.new_for_path (cache_path + ".part");
-
- // Copy from Google images to local cache
-
- yield online_image.copy_async (cache_image,
- FileCopyFlags.NONE,
- Priority.DEFAULT_IDLE,
- cancellable,
- null);
- if (cancellable.is_cancelled ()) {
- stdout.printf ("CANCELED\n");
- return;
- }
+ public int FetchThumbnail (string title, string year, string kind) throws DBus.Error {
+ var download = new GooglePosterDownload (title, year, true, ++fetch_handle, this);
- FileUtils.rename (cache_path + ".part", cache_path);
+ downloads.append (download);
- fetched (handle, cache_path);
+ return fetch_handle;
+ }
- stdout.printf ("DONE\n");
- } else {
- stdout.printf ("NOT FOUND\n");
- // stdout.printf ("%s\n", asstring);
+ 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;
}
-
- } catch (GLib.Error error) {
- stderr.printf ("Error: %s\n", error.message);
+ }
+ if (download != null) {
+ downloads.remove (download);
}
}
-}
-
-void main () {
- var loop = new MainLoop (null, false);
-
- 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 request_name_result = bus.request_name ("org.maemo.movieposter.GoogleImages", (uint) 0);
-
- if (request_name_result == DBus.RequestNameReply.PRIMARY_OWNER) {
- // Start server
- var server = new GoogleImages ();
- conn.register_object ("/org/maemo/movieposter/GoogleImages", server);
-
- loop.run ();
+ 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);
}
- } catch (Error e) {
- error ("Oops: %s\n", e.message);
}
}
+