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/>.
19 errordomain ParserError {
24 public class GoogleMovie : Movie {
26 public string runtime;
28 public string showtimes;
31 public class GoogleParser : Object {
32 private MovieSource.ReceiveMovieFunction _get_callback;
38 public int next_tag_offset () {
40 while (current[++i] != '<' && current[i] != 0);
44 public void next_tag () {
47 current += next_tag_offset ();
50 public void finish_tag () {
51 while (current[0] != '>' && current[0] != 0)
53 if (current[0] == '>')
57 public weak string parse_tag (bool finish = true) throws Error {
61 while (current[++i].isalnum ());
63 throw new ParserError.EOF ("EOF in tag");
64 if (current[i] == '>')
67 tag = (string) (current + 1);
74 public void expect_tag (string tag) throws Error {
75 var found = parse_tag (true);
77 throw new ParserError.WRONG_TAG ("Wrong tag \"%s\", expected \"%s\"",
82 public string parse_text () {
83 string text = ((string) current).ndup (next_tag_offset ());
88 public void parse_attribute (string _attr, out string value) {
93 while (current[++i] != '=' && current[i] != '>' && current[i] != 0) {
96 attr = ((string) current).ndup (i);
102 while (!current[++i].isspace () && current[i] != '>' && current[i] != 0) {
103 if (current[i] == '"')
104 while (current[++i] != '"' && current[i] != 0);
107 if (current[0] == '"')
108 value = ((string) current).substring (1, i - 2);
110 value = ((string) current).ndup (i);
115 public void skip_whitespace () {
119 while (current[++i].isspace () && current[i] != 0);
123 public string? parse_tag_attribute (string tag, string attribute) throws Error {
124 var found = parse_tag (false);
126 throw new ParserError.WRONG_TAG ("Wrong tag \"%s\", expected \"%s\"",
130 string? value = null;
132 while (current[0] != '>' && current[0] != 0) {
133 parse_attribute (attribute, out value);
136 // Skip the closing '>' bracket
143 public void parse_movie () throws Error {
144 expect_tag ("div"); // class=movie
145 expect_tag ("div"); // class=name
146 expect_tag ("a"); // href="/movies?near=city&mid=..."
147 expect_tag ("span"); // dir=ltr
148 var title = convert (parse_text ().replace ("'", "'").replace ("&", "&"), -1, "utf-8", "iso-8859-1"); // FIXME
149 expect_tag ("/span");
152 expect_tag ("span"); // class=info
153 string[] runtime_and_fsk = {};
155 if (parse_tag () == "nobr") {
157 weak string rating_string = parse_tag_attribute ("img", "alt").offset (6); // "Rated " ->"0.0 out of 5.0"
158 rating = rating_string.to_double ();
163 expect_tag ("/nobr");
164 expect_tag ("/nobr");
165 runtime_and_fsk = parse_text ().replace ("‎", "").offset (3).split (" - ");
166 expect_tag ("/span");
168 expect_tag ("div"); // class=times
169 var showtimes = parse_text ().replace (" ", ",");
173 if (pattern == null) {
174 if (!title.has_prefix (_filter.title))
177 if (!pattern.match ((uint) title.length, title, null))
181 var movie = new GoogleMovie ();
183 movie.title = strip_tags (title);
185 movie.rating = (int) (rating * 10);
187 movie.cinema = cinema_name;
188 if (runtime_and_fsk.length >= 2) {
189 movie.runtime = runtime_and_fsk[0];
190 movie.fsk = runtime_and_fsk[1];
192 movie.showtimes = showtimes;
194 // TODO - could be configurable by settings
195 if (movie.runtime != null)
196 movie.secondary = "%s - %s - %s".printf (movie.runtime, cinema_name, showtimes);
198 movie.secondary = "%s - %s".printf (cinema_name, showtimes);
200 _get_callback (movie);
203 // FIXME - this is specific for Germany
204 private string strip_tags (string title) {
205 string tag_suffix = " (OmU)"; // original audio with subtitles
206 if (title.has_suffix (tag_suffix))
207 return title.substring (0, title.length - tag_suffix.length);
208 tag_suffix = " (OV)"; // original audio
209 if (title.has_suffix (tag_suffix))
210 return title.substring (0, title.length - tag_suffix.length);
214 public void parse_cinema () throws Error {
215 expect_tag ("div"); // class=theater
216 expect_tag ("div"); // class=desc id=theater_...
217 expect_tag ("h2"); // class=name
218 expect_tag ("a"); // href="/movies?near=city&tid=..."
219 expect_tag ("span"); // dir=ltr
220 string name = convert (parse_text ().replace ("&", "&"), -1, "utf-8", "iso-8859-1"); // FIXME
221 expect_tag ("/span");
224 expect_tag ("div"); // class=info
225 var address_and_phone = parse_text ().replace (" ", " ").split (" - ");
226 if (address_and_phone.length >= 2) {
227 // string address = address_and_phone[0];
228 // string phone = address_and_phone[1];
230 expect_tag ("a"); // target=_top
236 // FIXME - store cinema address for movie detail window
239 public void parse (ref char[] buf) throws Error {
245 while (current[i++] != '>');
246 if (((string) current).has_prefix ("<a href=\"/movies?near=")) {
247 weak string href = parse_tag_attribute ("a", "href").offset (13); // "/movies?near=" ->"berlin&date=1"
248 // TODO - extract location and cache it
253 while (current[0] != 0) {
255 while (current[i++] != '>');
256 if (((string) current).has_prefix ("<div class=movie>")) {
258 } else if (((string) current).has_prefix("<div class=theater>")) {
267 public async void query (MovieFilter filter, string? location, MovieSource.ReceiveMovieFunction callback, Cancellable? cancellable) {
268 _get_callback = callback;
270 if (filter.title.chr(filter.title.length, '*') != null) {
271 pattern = new PatternSpec (filter.title);
276 // TODO - use google.de in Germany, also provides genres
277 string uri = "http://google.com/movies";
278 if (location != null && location != "")
279 uri += "?near=" + location;
281 stdout.printf ("GET: %s\n", uri);
283 File file = File.new_for_uri (uri);
284 InputStream stream = yield file.read_async (Priority.DEFAULT_IDLE, null);
286 char[] buf = new char[256*1024];
289 while (total < 256*1024) {
290 nread = yield stream.read_async ((char *)buf + total, 256*1024 - total, Priority.DEFAULT_IDLE, cancellable);
292 if (cancellable.is_cancelled ())
300 stderr.printf ("Error: %s\n", e.message);