1 /* This file is part of Cinaest.
3 * Copyright (C) 2009 Philipp Zabel
5 * Cinaest is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * Cinaest 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.
15 * You should have received a copy of the GNU General Public License
16 * along with Cinaest. If not, see <http://www.gnu.org/licenses/>.
21 // A single google image search (parsing and retrieval of the poster image URI)
22 public class GoogleImageSearch : Soup.Message {
23 public string poster_uri = null;
24 private bool thumbnail;
25 private string last_chunk = null;
27 public GoogleImageSearch (string search, bool _thumbnail = false) {
28 Object (method: "GET");
29 uri = new URI ("http://images.google.com/images?imgsz=m&imgar=t&q=" + Uri.escape_string (search, "+", false));
30 thumbnail = _thumbnail;
31 got_chunk.connect (this.on_got_chunk);
34 void on_got_chunk (Buffer chunk) {
37 if (poster_uri != null)
40 // FIXME - store a copy of the last chunk and retry if the parser fails, it can't handle partial input
41 if (last_chunk == null) {
42 // FIXME - can we avoid this copy without ugly hacks?
43 found = chunk.data.ndup (chunk.length).str ("dyn.setResults([[");
45 found = (last_chunk + chunk.data).str ("dyn.setResults([[");
49 var parser = new GoogleImageSearchParser (found);
51 poster_uri = parser.run (thumbnail);
52 if (poster_uri != null)
53 got_poster_uri (this);
55 if (e is ParserError.EOF) {
56 last_chunk = chunk.data.ndup (chunk.length);
58 stdout.printf ("Parser error: %s\n", e.message);
64 public signal void got_poster_uri (GoogleImageSearch search);
67 // Encapsulation of a single poster download (google image search query and image file download)
68 public class GooglePosterDownload : Object {
69 private GooglePosterDownloader downloader;
70 private Soup.Session session;
72 private string cache_dir;
73 private string cache_filename;
74 private bool cancelled = false;
76 public GooglePosterDownload (string title, string year, bool thumbnail, int _handle, GooglePosterDownloader _downloader) {
77 var search = title + "+" + year + "+movie+poster";
80 downloader = _downloader;
81 session = downloader.session;
83 var message = new GoogleImageSearch (search, thumbnail);
84 message.got_poster_uri.connect (on_got_poster_uri);
85 session.queue_message (message, google_search_finished);
89 cache_dir = Path.build_filename (Environment.get_tmp_dir(), "cinaest-thumbnails");
91 cache_dir = Path.build_filename (Environment.get_user_cache_dir(), "media-art");
93 cache_filename = "movie-" +
94 Checksum.compute_for_string (ChecksumType.MD5, (title).down ()) + "-" +
95 Checksum.compute_for_string (ChecksumType.MD5, (year).down ()) + ".jpeg";
98 private void on_got_poster_uri (GoogleImageSearch message) {
99 session.cancel_message (message, Soup.KnownStatusCode.OK);
102 private void google_search_finished (Session session, Message message) {
106 var search_message = (GoogleImageSearch) message;
107 print ("Finished search: %s\n", message.uri.to_string (false));
109 var poster_message = new Soup.Message ("GET", search_message.poster_uri);
110 session.queue_message (poster_message, poster_downloaded);
113 private void poster_downloaded (Session session, Message message) {
117 print ("Downloaded poster: %s\n", message.uri.to_string (false));
119 // Define cache path according to the Media Art Storage Spec (http://live.gnome.org/MediaArtStorageSpec)
120 string cache_path = Path.build_filename (cache_dir, cache_filename);
122 // Make sure the directory .album_arts is available
123 DirUtils.create_with_parents (cache_dir, 0770);
126 var file = File.new_for_path (cache_path + ".part");
127 var stream = file.create (FileCreateFlags.NONE, null);
129 stream.write (message.response_body.data, (size_t) message.response_body.length, null);
131 FileUtils.rename (cache_path + ".part", cache_path);
133 print ("Stored as: %s\n", cache_path);
135 downloader.fetched (handle, cache_path);
136 downloader.timeout_quit ();
138 stdout.printf ("Failed to store poster: %s\n", e.message);
142 public void cancel () {
147 // The D-Bus service to manage poster downloads
148 public class GooglePosterDownloader : Object, PosterDownloader {
149 private MainLoop loop;
150 public SessionAsync session;
151 private int fetch_handle = 1;
152 private List<GooglePosterDownload> downloads = null;
153 private uint source_id;
155 public GooglePosterDownloader () {
156 loop = new MainLoop (null);
158 session = new SessionAsync ();
159 session.max_conns_per_host = 7;
162 public void timeout_quit () {
163 // With every change we reset the timer to 3min
164 if (source_id != 0) {
165 Source.remove (source_id);
167 source_id = Timeout.add_seconds (180, quit);
170 private bool quit () {
181 // Implement the PosterDownloader interface
182 public int Fetch (string title, string year, string kind) throws DBus.Error {
183 var download = new GooglePosterDownload (title, year, false, ++fetch_handle, this);
185 downloads.append (download);
190 public int FetchThumbnail (string title, string year, string kind) throws DBus.Error {
191 var download = new GooglePosterDownload (title, year, true, ++fetch_handle, this);
193 downloads.append (download);
198 public void Unqueue (int handle) throws DBus.Error {
199 GooglePosterDownload download = null;
200 foreach (GooglePosterDownload d in downloads) {
201 if (d.handle == handle) {
207 if (download != null) {
208 downloads.remove (download);
212 static void main () {
214 var conn = DBus.Bus.get (DBus.BusType.SESSION);
215 dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
216 "/org/freedesktop/DBus",
217 "org.freedesktop.DBus");
219 // Try to register service in session bus
220 uint res = bus.request_name ("org.maemo.movieposter.GoogleImages", (uint) 0);
221 if (res == DBus.RequestNameReply.PRIMARY_OWNER) {
223 var server = new GooglePosterDownloader ();
224 conn.register_object ("/org/maemo/movieposter/GoogleImages", server);
226 server.timeout_quit ();
230 error ("Oops: %s\n", e.message);