IMDb plaintext downloader: optionally download actors and actresses
[cinaest] / src / imdb / imdb-plaintext-downloader.vala
1 using GLib;
2
3 class IMDbDownloadServer : Object, IMDbDownloader {
4         MainLoop loop;
5         Cancellable cancellable;
6         bool running;
7         uint source_id;
8         unowned IMDbSqlite sqlite;
9         string[] mirrors = {
10                 "ftp.fu-berlin.de/pub/misc/movies/database/",
11                 "ftp.funet.fi/pub/mirrors/ftp.imdb.com/pub/",
12                 "ftp.sunet.se/pub/tv+movies/imdb/"
13         };
14         string url;
15         int flags;
16         int percent_finished;
17
18         delegate void ParseLineFunction (string line);
19
20         construct {
21                 loop = new MainLoop (null, false);
22                 cancellable = new Cancellable ();
23         }
24
25         // IMDbDownloader implementation
26
27         public void download (string mirror, int _flags) throws DBus.Error {
28                 if (running) {
29                         stdout.printf ("Download in progress. Abort.\n");
30                         return;
31                 }
32                 running = true;
33                 if (source_id != 0) {
34                         Source.remove (source_id);
35                 }
36
37                 stdout.printf ("Download started (%x).", flags);
38                 progress (0);
39                 url = "ftp://anonymous@" + mirror;
40                 flags = _flags;
41                 try {
42                         Thread.create(download_thread, false);
43                 } catch (ThreadError e) {
44                         critical ("Failed to create download thread\n");
45                         return;
46                 }
47         }
48
49         public void cancel () throws DBus.Error {
50                 cancellable.cancel ();
51         }
52
53         public string[] get_mirrors () throws DBus.Error {
54                 return mirrors;
55         }
56
57         // Private methods
58
59         private void* download_thread () {
60                 description_changed ("Connecting to FTP ...");
61                 progress (0);
62                 percent_finished = 0;
63
64                 var cache_dir = Path.build_filename (Environment.get_user_cache_dir (), "cinaest");
65                 DirUtils.create_with_parents (cache_dir, 0770);
66
67                 var _sqlite = new IMDbSqlite (Path.build_filename (cache_dir, "imdb.db"));
68                 sqlite = _sqlite;
69                 _sqlite.clear ();
70
71                 try {
72                         var movie_parser = new MovieLineParser (sqlite);
73                         var genre_parser = new GenreLineParser (sqlite);
74                         var rating_parser = new RatingLineParser (sqlite);
75                         var aka_parser = new AkaLineParser (sqlite);
76                         var plot_parser = new PlotLineParser (sqlite);
77                         var person_parser = new PersonParser (sqlite);
78
79                         var downloader = new FtpDownloader (cancellable);
80                         downloader.progress.connect (on_progress);
81
82                         var parser = new IMDbGzipParser (cancellable);
83                         parser.progress.connect (on_progress);
84
85                         if (MOVIES in flags) {
86                                 description_changed ("Downloading movie list ...");
87                                 downloader.download (url + "movies.list.gz", Path.build_filename (cache_dir, "movies.list.gz"));
88                         }
89                         percent_finished = 10;
90                         if (GENRES in flags) {
91                                 description_changed ("Downloading genre data ...");
92                                 downloader.download (url + "genres.list.gz", Path.build_filename (cache_dir, "genres.list.gz"));
93                         }
94                         percent_finished = 20;
95                         if (RATINGS in flags) {
96                                 description_changed ("Downloading rating data ...");
97                                 downloader.download (url + "ratings.list.gz", Path.build_filename (cache_dir, "ratings.list.gz"));
98                         }
99                         percent_finished = 30;
100                         if (AKAS in flags) {
101                                 description_changed ("Downloading alternative titles ...");
102                                 downloader.download (url + "aka-titles.list.gz", Path.build_filename (cache_dir, "aka-titles.list.gz"));
103                         }
104                         percent_finished = 40;
105                         if (PLOTS in flags) {
106                                 description_changed ("Downloading plots ...");
107                                 downloader.download (url + "plot.list.gz", Path.build_filename (cache_dir, "plot.list.gz"));
108                         }
109                         if (ACTORS in flags) {
110                                 description_changed ("Downloading actors ...");
111                                 downloader.download (url + "actors.list.gz", Path.build_filename (cache_dir, "actors.list.gz"));
112                                 description_changed ("Downloading actresses ...");
113                                 downloader.download (url + "actresses.list.gz", Path.build_filename (cache_dir, "actresses.list.gz"));
114                         }
115                         // composers
116                         // costume-designers
117                         if (DIRECTORS in flags) {
118                                 description_changed ("Downloading directors ...");
119                                 downloader.download (url + "directors.list.gz", Path.build_filename (cache_dir, "directors.list.gz"));
120                         }
121                         // editors
122                         // producers
123                         // production-designers
124                         if (WRITERS in flags) {
125                                 description_changed ("Downloading writers ...");
126                                 downloader.download (url + "writers.list.gz", Path.build_filename (cache_dir, "writers.list.gz"));
127                         }
128
129                         percent_finished = 50;
130                         if (MOVIES in flags) {
131                                 description_changed ("Parsing movie list ...");
132                                 parser.parse (Path.build_filename (cache_dir, "movies.list.gz"), movie_parser);
133                         }
134                         percent_finished = 60;
135                         if (GENRES in flags) {
136                                 description_changed ("Parsing genre data ...");
137                                 parser.parse (Path.build_filename (cache_dir, "genres.list.gz"), genre_parser);
138                         }
139                         percent_finished = 70;
140                         if (RATINGS in flags) {
141                                 description_changed ("Parsing rating data ...");
142                                 parser.parse (Path.build_filename (cache_dir, "ratings.list.gz"), rating_parser);
143                         }
144                         percent_finished = 80;
145                         if (AKAS in flags) {
146                                 description_changed ("Parsing alternative titles ...");
147                                 parser.parse (Path.build_filename (cache_dir, "aka-titles.list.gz"), aka_parser);
148                         }
149                         percent_finished = 90;
150                         if (PLOTS in flags) {
151                                 description_changed ("Parsing plots ...");
152                                 parser.parse (Path.build_filename (cache_dir, "plot.list.gz"), plot_parser);
153                         }
154                         if (ACTORS in flags) {
155                                 description_changed ("Parsing actors ...");
156                                 parser.parse (Path.build_filename (cache_dir, "actors.list.gz"), person_parser);
157                                 person_parser.reset ();
158                                 description_changed ("Parsing actresses ...");
159                                 parser.parse (Path.build_filename (cache_dir, "actresses.list.gz"), person_parser);
160                         }
161                 } catch (Error e2) {
162                         if (e2 is IOError.CANCELLED)
163                                 stdout.printf ("Download cancelled.\n");
164                         else
165                                 warning ("Failed to open/read stream: %s\n", e2.message);
166                 }
167
168                 description_changed ("Creating indices ...");
169                 if (AKAS in flags)
170                         sqlite.create_aka_index ();
171                 if (MOVIES in flags)
172                         sqlite.create_votes_index ();
173
174                 if (!cancellable.is_cancelled ()) {
175                         stdout.printf ("Download complete.\n");
176                         progress (100);
177                 }
178
179                 sqlite = null;
180                 running = false;
181
182                 timeout_quit ();
183
184                 return null;
185         }
186
187         private void on_progress (int dltotal, int dlnow) {
188                 stdout.printf ("%d / %d\r", dlnow, dltotal);
189                 if (dltotal > 0) {
190                         int p = percent_finished + 10*dlnow/dltotal;
191                         if (p < 100)
192                                 progress (p);
193                 }
194         }
195
196         private void timeout_quit () {
197                 source_id = Timeout.add (3000, quit);
198         }
199
200         private bool quit () {
201                 loop.quit ();
202
203                 // One-shot only
204                 return false;
205         }
206
207         public void run () {
208                 loop.run ();
209         }
210
211         public static void main () {
212                 Curl.global_init (Curl.GLOBAL_DEFAULT);
213
214                 try {
215                         var conn = DBus.Bus.get (DBus.BusType.SESSION);
216                         dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
217                                                                    "/org/freedesktop/DBus",
218                                                                    "org.freedesktop.DBus");
219
220                         // Try to register service in session bus
221                         uint request_name_result = bus.request_name (DBUS_SERVICE, (uint) 0);
222
223                         if (request_name_result == DBus.RequestNameReply.PRIMARY_OWNER) {
224                                 // Start server
225                                 var server = new IMDbDownloadServer ();
226                                 conn.register_object (DBUS_OBJECT, server);
227
228                                 server.run ();
229                         } else {        
230                                 critical ("Service \"org.maemo.cinaest.IMDb\" already registered. Abort.\n");
231                         }
232                 } catch (Error e) {
233                         critical ("Oops: %s\n", e.message);
234                 }
235
236                 Curl.global_cleanup ();
237         }
238 }