From: Philipp Zabel Date: Mon, 9 Nov 2009 10:11:10 +0000 (+0100) Subject: Add Google plugin X-Git-Tag: v0.0.2~4 X-Git-Url: http://vcs.maemo.org/git/?a=commitdiff_plain;h=aaa2969f0af7680b76197f839002200a25cae01c;p=cinaest Add Google plugin This plugin searches for movies using Google's Movie Showtimes search. For now, the secondary info just contains cinema and today's showtimes. --- diff --git a/Makefile.am b/Makefile.am index 68aa931..76f4638 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,6 +13,7 @@ libexec_PROGRAMS = \ imdb-plaintext-downloader pkglib_LTLIBRARIES = \ + libgoogle-plugin.la \ libimdb-plugin.la dbusservice_DATA = \ @@ -61,6 +62,31 @@ cinaest_CFLAGS = ${HILDON_CFLAGS} ${OSSO_CFLAGS} ${GMODULE_CFLAGS} \ -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\" cinaest_LDADD = ${HILDON_LIBS} ${OSSO_LIBS} ${GMODULE_LIBS} +libgoogle_plugin_la_SOURCES = \ + src/plugins/google-plugin.c \ + src/genres.c \ + src/movie.c \ + src/movie-filter.c \ + src/plugin-interface.c \ + src/plugins/google-parser.c + +libgoogle_plugin_la_VALASOURCES = \ + src/plugins/google-plugin.vala \ + src/genres.vala \ + src/movie.vala \ + src/movie-filter.vala \ + src/plugin-interface.vala \ + src/plugins/google-parser.vala + +libgoogle_plugin_la_VALAFLAGS = --vapidir ./vapi --pkg config \ + --pkg hildon-1 +libgoogle_plugin_la_CFLAGS = ${HILDON_CFLAGS} +libgoogle_plugin_la_LIBADD = ${HILDON_LIBS} +libgoogle_plugin_la_LDFLAGS = -module + +${libgoogle_plugin_la_SOURCES}: ${libgoogle_plugin_la_VALASOURCES} + ${VALAC} -C ${libgoogle_plugin_la_VALASOURCES} ${libgoogle_plugin_la_VALAFLAGS} + libimdb_plugin_la_SOURCES = \ src/plugins/imdb-plugin.c \ src/genres.c \ diff --git a/debian/cinaest-plugin-google.install b/debian/cinaest-plugin-google.install new file mode 100644 index 0000000..5937ff2 --- /dev/null +++ b/debian/cinaest-plugin-google.install @@ -0,0 +1 @@ +usr/lib/cinaest/libgoogle-plugin.so* diff --git a/debian/control b/debian/control index 0fe3091..16acc36 100644 --- a/debian/control +++ b/debian/control @@ -20,3 +20,10 @@ Depends: ${shlibs:Depends}, ${misc:Depends} Description: Look up movies on IMDb This Cinaest plugin can look up movies in an offline copy of the Internet Movie Database (IMDb). + +Package: cinaest-plugin-google +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Look up movie showtimes on Google + This Cineast plugin contains an online interface to the + Movie Showtimes Google Search. diff --git a/po/POTFILES.in b/po/POTFILES.in index 6e5fca9..220fac1 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,5 +1,7 @@ src/main.vala src/movie-list-menu.vala src/movie-list-window.vala +src/plugins/imdb-plugin.vala +src/plugins/google-plugin.vala src/settings-dialog.vala src/source-dialog.vala diff --git a/src/plugins/google-parser.vala b/src/plugins/google-parser.vala new file mode 100644 index 0000000..e36cce5 --- /dev/null +++ b/src/plugins/google-parser.vala @@ -0,0 +1,282 @@ +/* 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 . + */ + +errordomain ParserError { + WRONG_TAG, + EOF +} + +public class GoogleMovie : Movie { + public string cinema; + public string runtime; + public string fsk; + public string showtimes; +} + +public class GoogleParser : Object { + private MovieSource.ReceiveMovieFunction _get_callback; + char *current; + string cinema_name; + MovieFilter _filter; + PatternSpec pattern; + + public int next_tag_offset () { + int i = -1; + while (current[++i] != '<' && current[i] != 0); + return i; + } + + public void next_tag () { + if (current[0] == 0) + return; + current += next_tag_offset (); + } + + public void finish_tag () { + while (current[0] != '>' && current[0] != 0) + current++; + if (current[0] == '>') + current++; + } + + public weak string parse_tag (bool finish = true) throws Error { + weak string tag; + next_tag (); + int i = 1; + while (current[++i].isalnum ()); + if (current[i] == 0) + throw new ParserError.EOF ("EOF in tag"); + if (current[i] == '>') + finish = false; + current[i] = 0; + tag = (string) (current + 1); + current += i + 1; + if (finish) + finish_tag (); + return tag; + } + + public void expect_tag (string tag) throws Error { + var found = parse_tag (true); + if (tag != found) { + throw new ParserError.WRONG_TAG ("Wrong tag \"%s\", expected \"%s\"", + found, tag); + } + } + + public string parse_text () { + string text = ((string) current).ndup (next_tag_offset ()); + next_tag (); + return text; + } + + public void parse_attribute (string _attr, out string value) { + string attr; + if (current[0] == 0) + return; + int i = -1; + while (current[++i] != '=' && current[i] != '>' && current[i] != 0) { + + } + attr = ((string) current).ndup (i); + current += i; + if (current[0] == 0) + return; + current++; + i = -1; + while (!current[++i].isspace () && current[i] != '>' && current[i] != 0) { + if (current[i] == '"') + while (current[++i] != '"' && current[i] != 0); + } + if (attr == _attr) { + if (current[0] == '"') + value = ((string) current).substring (1, i - 2); + else + value = ((string) current).ndup (i); + } + current += i; + } + + public void skip_whitespace () { + if (current[0] == 0) + return; + int i = -1; + while (current[++i].isspace () && current[i] != 0); + current += i; + } + + public string? parse_tag_attribute (string tag, string attribute) throws Error { + var found = parse_tag (false); + if (tag != found) { + throw new ParserError.WRONG_TAG ("Wrong tag \"%s\", expected \"%s\"", + found, tag); + } + + string? value = null; + skip_whitespace (); + while (current[0] != '>' && current[0] != 0) { + parse_attribute (attribute, out value); + skip_whitespace (); + } + // Skip the closing '>' bracket + if (current[0] != 0) + current++; + + return value; + } + + public void parse_movie () throws Error { + expect_tag ("a"); // rating + expect_tag ("nobr"); + expect_tag ("nobr"); + weak string rating_string = parse_tag_attribute ("img", "alt").offset (6); // "Rated " ->"0.0 out of 5.0" + double rating = rating_string.to_double (); + + expect_tag ("img"); + expect_tag ("img"); + expect_tag ("img"); + expect_tag ("img"); + expect_tag ("/nobr"); + expect_tag ("/nobr"); + expect_tag ("br"); + expect_tag ("nobr"); + expect_tag ("/nobr"); + expect_tag ("/a"); + expect_tag ("/font"); + expect_tag ("/td"); + expect_tag ("td"); + expect_tag ("font"); + expect_tag ("a"); // --> link + expect_tag ("b"); + var title = convert (parse_text ().replace ("'", "'"), -1, "utf-8", "iso-8859-1"); // FIXME + expect_tag ("/b"); + expect_tag ("/a"); + expect_tag ("br"); + var runtime_and_fsk = parse_text ().replace (" ", " ").replace ("‎", "").split (" - "); + + var showtimes = ""; + if (parse_tag () == "br") { + showtimes = parse_text ().replace (" ", ","); + expect_tag ("/font"); + } + + if (pattern == null) { + if (!title.has_prefix (_filter.title)) + return; + } else { + if (!pattern.match ((uint) title.length, title, null)) + return; + } + + var movie = new GoogleMovie (); + + movie.title = strip_tags (title); + movie.year = 0; + movie.rating = (int) (rating * 10); + + movie.cinema = cinema_name; + if (runtime_and_fsk.length >= 2) { + movie.runtime = runtime_and_fsk[0]; + movie.fsk = runtime_and_fsk[1]; + } + movie.showtimes = showtimes; + + // TODO - could be configurable by settings + if (movie.runtime != null) + movie.secondary = "%s - %s - %s".printf (movie.runtime, cinema_name, showtimes); + else + movie.secondary = "%s - %s".printf (cinema_name, showtimes); + + _get_callback (movie); + } + + // FIXME - this is specific for Germany + private string strip_tags (string title) { + string tag_suffix = " (OmU)"; // original audio with subtitles + if (title.has_suffix (tag_suffix)) + return title.substring (0, title.length - tag_suffix.length); + tag_suffix = " (OV)"; // original audio + if (title.has_suffix (tag_suffix)) + return title.substring (0, title.length - tag_suffix.length); + return title.dup (); + } + + public void parse_cinema () throws Error { + expect_tag ("tr"); + expect_tag ("td"); + expect_tag ("a"); // --> link + expect_tag ("b"); + string name = convert (parse_text ().replace ("&", "&"), -1, "utf-8", "iso-8859-1"); // FIXME + expect_tag ("/b"); + expect_tag ("/a"); + expect_tag ("br"); + expect_tag ("font"); + string address = parse_text ().replace (" ", " "); + expect_tag ("a"); // --> map + expect_tag ("/a"); + expect_tag ("/font"); + expect_tag ("/td"); + expect_tag ("/tr"); + + cinema_name = name; + // FIXME - store cinema address for movie detail window + } + + public void parse (ref char[] buf) throws Error { + current = buf; + next_tag (); + while (current[0] != 0) { + int i = 1; + while (current[i++] != '>'); + if (((string) current).has_prefix (". + */ + +using Gtk; +using Hildon; + +class GooglePlugin : Plugin { + List sources; + + public override void hello (Gtk.Window window) { + stdout.printf ("Google Plugin Loaded.\n"); + + var source = new GoogleSource (); + + sources = new List (); + sources.append (source); + + // FIXME - this forces the inclusion of config.h + (void) Config.GETTEXT_PACKAGE; + } + + public override unowned List get_sources () { + return sources; + } + + public override void settings_dialog (Gtk.Window window) { + GoogleSource source = (GoogleSource) sources.data; + var dialog = new Gtk.Dialog (); + dialog.set_transient_for (window); + dialog.set_title (_("Google plugin settings")); + + var selector = new TouchSelectorEntry.text (); + selector.append_text ("Berlin"); + + var button = new PickerButton (SizeType.FINGER_HEIGHT, ButtonArrangement.HORIZONTAL); + button.set_title (_("Location")); + button.set_selector (selector); + button.set_value (source.location); + + var content = (VBox) dialog.get_content_area (); + content.pack_start (button, true, true, 0); + + dialog.add_button ("Done", ResponseType.ACCEPT); + + dialog.show_all (); + int res = dialog.run (); + if (res == ResponseType.ACCEPT) { + source.location = button.get_value (); + } + dialog.destroy (); + } + + public override unowned string get_name () { + return "Google"; + } +} + +class GoogleSource : MovieSource { + public string location; + public string description; + + public override void get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit) { + var parser = new GoogleParser (filter, location, callback); + } + + public override void add_movie (Movie movie) { + } + + public override unowned string get_name () { + return _("Google"); + } + + public override unowned string get_description () { + if (location != null && location != "") { + description = _("Movie Showtimes near %s").printf (location); + } else { + description = _("Movie Showtimes"); + } + return description; + } +} + +[ModuleInit] +public Type register_plugin () { + // types are registered automatically + return typeof (GooglePlugin); +}