3 class IMDbDownloadServer : Object, IMDbDownloader {
5 Cancellable cancellable;
9 unowned IMDbSqlite sqlite;
11 "ftp.fu-berlin.de/pub/misc/movies/database/",
12 "ftp.funet.fi/pub/mirrors/ftp.imdb.com/pub/",
13 "ftp.sunet.se/pub/tv+movies/imdb/"
16 delegate void ParseLineFunction (string line);
19 loop = new MainLoop (null, false);
20 cancellable = new Cancellable ();
23 // IMDbDownloader implementation
25 public void download (string mirror, int flags) throws DBus.Error {
27 message ("Download in progress. Abort.\n");
31 message ("Download started (%x).", flags);
33 download_imdb_async.begin ("ftp://anonymous@" + mirror, flags, Priority.DEFAULT_IDLE);
34 message ("Download finished.");
37 public void cancel () throws DBus.Error {
38 cancellable.cancel ();
41 public string[] get_mirrors () throws DBus.Error {
47 async void download_imdb_async (string mirror, int flags, int io_priority) {
49 File movies = File.new_for_uri (mirror + "/movies.list.gz");
50 File genres = File.new_for_uri (mirror + "/genres.list.gz");
51 File ratings = File.new_for_uri (mirror + "/ratings.list.gz");
53 description_changed ("Connecting to FTP ...");
57 m = yield movies.find_enclosing_mount_async(io_priority, cancellable);
60 bool mounted = yield movies.mount_enclosing_volume (MountMountFlags.NONE, null, cancellable);
62 m = yield movies.find_enclosing_mount_async(io_priority, cancellable);
68 critical ("Failed to mount: %s\n", e1.message);
73 stdout.printf ("Mounted: %s\n", m.get_name ());
75 description_changed ("Querying file sizes ...");
79 if (MOVIES in flags) {
80 info = yield movies.query_info_async ("", FileQueryInfoFlags.NONE, io_priority, cancellable);
81 total += info.get_size ();
83 if (GENRES in flags) {
84 info = yield genres.query_info_async ("", FileQueryInfoFlags.NONE, io_priority, cancellable);
85 total += info.get_size ();
87 if (RATINGS in flags) {
88 info = yield ratings.query_info_async ("", FileQueryInfoFlags.NONE, io_priority, cancellable);
89 total += info.get_size ();
92 warning ("Failed to get size: %s\n", e3.message);
96 var cache_dir = Path.build_filename (Environment.get_user_cache_dir (), "cinaest");
97 DirUtils.create_with_parents (cache_dir, 0770);
99 var _sqlite = new IMDbSqlite (Path.build_filename (cache_dir, "imdb.db"));
104 var movie_parser = new MovieLineParser (sqlite);
105 var genre_parser = new GenreLineParser (sqlite);
106 var rating_parser = new RatingLineParser (sqlite);
109 if (MOVIES in flags) {
110 description_changed ("Downloading movie list ...");
111 yield download_async(movies, movie_parser, io_priority);
113 if (GENRES in flags) {
114 description_changed ("Downloading genre data ...");
115 yield download_async(genres, genre_parser, io_priority);
117 if (RATINGS in flags) {
118 description_changed ("Downloading rating data ...");
119 yield download_async(ratings, rating_parser, io_priority);
122 if (e2 is IOError.CANCELLED)
123 message ("Download cancelled.\n");
125 warning ("Failed to open/read stream: %s\n", e2.message);
128 if (!cancellable.is_cancelled ()) {
129 stdout.printf ("Download complete.\n");
134 bool unmounted = yield m.unmount(MountUnmountFlags.NONE, null);
136 warning ("Failed to unmount.\n");
139 warning ("Failed to unmount: %s\n", e4.message);
145 // FIXME - use a timeout?
149 async void download_async (File f, LineParser parser, int io_priority) throws Error {
151 unowned string line = null;
153 var stream = new GzipInputStream (yield f.read_async (io_priority, cancellable));
154 var data = new DataInputStream(stream);
159 line = yield data.read_line_async (io_priority, cancellable, out l);
161 parser.parse_line (line);
165 int p = (int) (100 * (sofar + stream.total_in ()) / total);
170 } while (line != null);
172 sofar += stream.total_in ();
174 yield stream.close_async (io_priority, cancellable);
181 public static void main () {
183 var conn = DBus.Bus.get (DBus.BusType.SESSION);
184 dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
185 "/org/freedesktop/DBus",
186 "org.freedesktop.DBus");
188 // Try to register service in session bus
189 uint request_name_result = bus.request_name (DBUS_SERVICE, (uint) 0);
191 if (request_name_result == DBus.RequestNameReply.PRIMARY_OWNER) {
193 var server = new IMDbDownloadServer ();
194 conn.register_object (DBUS_OBJECT, server);
198 critical ("Service \"org.maemo.cinaest.IMDb\" already registered. Abort.\n");
201 critical ("Oops: %s\n", e.message);
206 abstract class LineParser {
207 internal unowned IMDbSqlite sqlite;
209 public LineParser (IMDbSqlite _sqlite) {
213 public abstract void parse_line (string line);
215 internal bool skip_title (string title) {
216 if (title.has_suffix ("(TV)")) {
219 if (title.has_suffix ("(V)")) {
222 if (title.has_suffix ("(VG)")) {
229 class MovieLineParser : LineParser {
232 public MovieLineParser (IMDbSqlite _sqlite) {
235 re_movie = new Regex ("^([^\t]+)\t+([0-9]+)$");
236 } catch (RegexError e) {
237 critical ("Failed to initialize regex: %s\n", e.message);
241 public override void parse_line (string line) {
244 // Skip series episodes
248 if (!re_movie.match(line, 0, out matchinfo))
252 string year = matchinfo.fetch (2);
254 title = convert(matchinfo.fetch (1), -1, "utf-8", "latin1");
255 } catch (ConvertError e) {
259 if (skip_title (title))
262 sqlite.add_movie (title, year.to_int ());
266 class GenreLineParser : LineParser {
269 public GenreLineParser (IMDbSqlite _sqlite) {
272 re_genre = new Regex ("^([^\t]+)\t+([A-Za-z-]+)$");
273 } catch (RegexError e) {
274 critical ("Failed to initialize regex: %s\n", e.message);
278 public override void parse_line (string line) {
281 // Skip series episodes
285 if (!re_genre.match(line, 0, out matchinfo))
289 string genre = matchinfo.fetch (2);
291 title = convert(matchinfo.fetch (1), -1, "utf-8", "latin1");
292 } catch (ConvertError e) {
296 sqlite.movie_add_genre (title, genre);
300 class RatingLineParser : LineParser {
303 public RatingLineParser (IMDbSqlite _sqlite) {
306 re_rating = new Regex ("^ .+ +[0-9]+ +([0-9.]+) +(.+)$");
307 } catch (RegexError e) {
308 critical ("Failed to initialize regex: %s\n", e.message);
312 public override void parse_line (string line) {
315 // Skip series episodes
319 if (!re_rating.match(line, 0, out matchinfo))
323 string rating = matchinfo.fetch (1);
325 title = convert(matchinfo.fetch (2), -1, "utf-8", "latin1");
326 } catch (ConvertError e) {
330 // Skip series episodes
334 if (skip_title (title))
337 sqlite.movie_set_rating (title, (int) (rating.to_double () * 10));