imdb-plaintext-downloader
pkglib_LTLIBRARIES = \
+ libcatalog-plugin.la \
libgoogle-plugin.la \
libimdb-plugin.la
-DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\"
cinaest_LDADD = ${DBUS_LIBS} ${GCONF_LIBS} ${HILDON_LIBS} ${OSSO_LIBS} ${GMODULE_LIBS}
+libcatalog_plugin_la_SOURCES = \
+ src/plugins/catalog-plugin.c \
+ src/cell-renderer-vbox.c \
+ src/genres.c \
+ src/movie.c \
+ src/plugin-interface.c \
+ src/plugins/catalog-sqlite.c \
+ src/source-list-view.c
+
+libcatalog_plugin_la_VALASOURCES = \
+ src/plugins/catalog-plugin.vala \
+ src/cell-renderer-vbox.vala \
+ src/genres.vala \
+ src/movie.vala \
+ src/movie-filter.vala \
+ src/plugin-interface.vala \
+ src/plugins/catalog-sqlite.vala \
+ src/source-list-view.vala
+
+libcatalog_plugin_la_VALAFLAGS = --vapidir ./vapi --pkg config \
+ --pkg hildon-1 --pkg libosso --pkg sqlite3
+libcatalog_plugin_la_CFLAGS = ${HILDON_CFLAGS} ${OSSO_CFLAGS} ${SQLITE3_CFLAGS} \
+ -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\"
+libcatalog_plugin_la_LIBADD = ${HILDON_LIBS} ${OSSO_LIBS} ${SQLITE3_LIBS}
+libcatalog_plugin_la_LDFLAGS = -module
+
+src/plugins/catalog-plugin.c: ${libcatalog_plugin_la_VALASOURCES}
+ ${VALAC} -C ${libcatalog_plugin_la_VALASOURCES} ${libcatalog_plugin_la_VALAFLAGS}
+
libgoogle_plugin_la_SOURCES = \
src/plugins/google-plugin.c \
src/genres.c \
CLEANFILES = \
${cinaest_SOURCES} \
+ ${libcatalog_plugin_la_SOURCES} \
${libgoogle_plugin_la_SOURCES} \
${libimdb_plugin_la_SOURCES} \
${imdb_plaintext_downloader_SOURCES} \
src/main.vala
src/movie-list-menu.vala
src/movie-list-window.vala
+src/plugins/catalog-plugin.vala
src/plugins/imdb-plugin.vala
src/plugins/google-plugin.vala
src/settings-dialog.vala
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+using Hildon;
+
+class CatalogPlugin : Plugin {
+ List<CatalogSource> sources;
+ private CatalogSqlite sqlite;
+ private Gtk.Dialog dialog;
+
+ public override void hello (Gtk.Window window, Osso.Context context) {
+ string data_dir = Path.build_filename (Environment.get_user_data_dir(), "cinaest");
+ string filename = Path.build_filename (data_dir, "catalog.db");
+
+ // Make sure the data directory is available
+ DirUtils.create_with_parents (data_dir, 0770);
+
+ sqlite = new CatalogSqlite (filename);
+ sources = new List<CatalogSource> ();
+
+ var source = new CatalogSource ("Collection", _("Collection"), _("Personal movie list"), sqlite);
+ sources.append (source);
+
+ source = new CatalogSource ("Loaned", _("Loaned movies"), _("Movies loaned to friends"), sqlite);
+ sources.append (source);
+
+ source = new CatalogSource ("Watchlist", _("Watchlist"), _("Movies of interest"), sqlite);
+ sources.append (source);
+
+ stdout.printf ("Catalog Plugin Loaded.\n");
+ }
+
+ public override unowned List<MovieSource> get_sources () {
+ return (List<MovieSource>) sources;
+ }
+
+ public override List<MovieAction> get_actions (Movie movie, Gtk.Window window) {
+ var list = new List<MovieAction> ();
+
+ list.append (new MovieAction (_("Add to catalog"), on_add_to_catalog, movie, window));
+
+ return list;
+ }
+
+ private void on_add_to_catalog (Movie movie, Gtk.Window window) {
+ dialog = new Gtk.Dialog ();
+ dialog.set_transient_for (window);
+ dialog.set_title (_("Add movie to catalog - Select list"));
+
+ int i = 0;
+ var available_sources = new List<MovieSource> ();
+ foreach (CatalogSource s in sources) {
+ if (!s.contains (movie)) {
+ available_sources.append ((MovieSource) s);
+ i++;
+ }
+ }
+
+ var source_list = new SourceListView (available_sources);
+
+ var content = (VBox) dialog.get_content_area ();
+ content.pack_start (source_list, true, true, 0);
+ if (i > 5)
+ i = 5;
+ content.set_size_request (-1, i*70);
+
+ // Connect signals
+ source_list.source_activated.connect (on_source_activated);
+
+ dialog.show_all ();
+ int res = dialog.run ();
+ if (res >= 0) {
+ var source = sources.nth_data (res);
+ source.add_movie (movie);
+
+ var banner = (Banner) Banner.show_information_with_markup (window, null, _("'%s' added to list '%s'").printf (movie.title, source.get_name ()));
+ banner.set_timeout (1500);
+ }
+ dialog.destroy ();
+ dialog = null;
+ }
+
+ private void on_source_activated (MovieSource source) {
+ int n = sources.index ((CatalogSource) source);
+
+ dialog.response (n);
+ }
+
+ public override void settings_dialog (Gtk.Window window) {
+ var dialog = new Gtk.Dialog ();
+ dialog.set_transient_for (window);
+ dialog.set_title (_("Catalog plugin settings"));
+
+ var button = new Hildon.Button (SizeType.FINGER_HEIGHT, ButtonArrangement.VERTICAL);
+ button.set_title (_("Select active movie lists"));
+ button.set_value (_("Collection, Loaned movies, Watchlist"));
+
+ 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) {
+ }
+ dialog.destroy ();
+ }
+
+ public override unowned string get_name () {
+ return _("Catalog");
+ }
+}
+
+class CatalogSource : MovieSource {
+ private string table;
+ private string name;
+ private string description;
+ private CatalogSqlite sqlite;
+
+ public CatalogSource (string _table, string _name, string _description, CatalogSqlite _sqlite) {
+ table = _table;
+ name = _name;
+ description = _description;
+ sqlite = _sqlite;
+ }
+
+ public override async void get_movies (MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
+ yield sqlite.query (table, filter, callback, limit, cancellable);
+ }
+
+ public override void add_movie (Movie movie) {
+ sqlite.add_movie (table, movie);
+ }
+
+ public override void delete_movie (Movie movie) {
+ sqlite.delete_movie (table, movie);
+ }
+
+ internal bool contains (Movie movie) {
+ return sqlite.contains (table, movie);
+ }
+
+ public override unowned string get_name () {
+ return name;
+ }
+
+ public override unowned string get_description () {
+ return description;
+ }
+
+ public override bool get_editable () {
+ return true;
+ }
+}
+
+[ModuleInit]
+public Type register_plugin () {
+ // types are registered automatically
+ return typeof (CatalogPlugin);
+}
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+using Sqlite;
+
+class CatalogSqlite : Object {
+ Database db;
+
+ public delegate void ReceiveMovieFunction (string title, int year, int rating, int genres);
+
+ public CatalogSqlite (string filename) {
+ int rc;
+
+ rc = Database.open (filename, out db);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("Can't open database: %d, %s\n", rc, db.errmsg ());
+ return;
+ }
+
+ rc = db.exec ("PRAGMA locking_mode = EXCLUSIVE;", callback, null);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("Can't get exclusive lock: %d, %s\n", rc, db.errmsg ());
+ return;
+ }
+
+ rc = db.exec ("PRAGMA synchronous = OFF;", callback, null);
+ if (rc != Sqlite.OK)
+ stderr.printf ("Can't turn off synchronous access: %d, %s\n", rc, db.errmsg ());
+
+ prepare ();
+ }
+
+ public static int callback (int n_columns, string[] values,
+ string[] column_names) {
+ for (int i = 0; i < n_columns; i++) {
+ stdout.printf ("%s = %s\n", column_names[i], values[i]);
+ }
+ stdout.printf ("\n");
+
+ return 0;
+ }
+
+ public int add_movie (string table, Movie movie) {
+ string sql = "INSERT INTO %s(Title, Year, Rating, Genres) VALUES (\"%s\", %d, %d, %d);".printf (table, movie.title, movie.year, movie.rating, movie.genres.field);
+ int rc;
+
+ rc = db.exec (sql, callback, null);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("Failed to insert movie \"%s\" (%d): %d, %s\n", movie.title, movie.year, rc, db.errmsg ());
+ return 1;
+ }
+
+ return 0;
+ }
+
+ public int delete_movie (string table, Movie movie) {
+ string sql = "DELETE FROM %s WHERE Title=\"%s\" AND Year=%d".printf (table, movie.title, movie.year);
+ int rc;
+
+ rc = db.exec (sql, callback, null);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("Failed to delete movie \"%s\" (%d): %d, %s\n", movie.title, movie.year, rc, db.errmsg ());
+ return 1;
+ }
+
+ return 0;
+ }
+
+ public bool contains (string table, Movie movie) {
+ string sql = "SELECT count(*) FROM %s WHERE Title=\"%s\" AND Year=%d".printf (table, movie.title, movie.year);
+ Statement stmt;
+ int rc;
+ int count = 0;
+
+ rc = db.prepare_v2 (sql, -1, out stmt);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
+ db.progress_handler (0, null);
+ return false;
+ }
+
+ do {
+ rc = stmt.step ();
+ if (rc == Sqlite.ROW) {
+ count = stmt.column_int (0);
+ }
+ } while (rc == Sqlite.ROW);
+
+ return (count > 0);
+ }
+
+ private int prepare () {
+ int rc;
+
+ rc = db.exec ("CREATE TABLE IF NOT EXISTS Collection (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0); CREATE TABLE IF NOT EXISTS Loaned (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0); CREATE TABLE IF NOT EXISTS Watchlist (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0);", callback, null);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
+ return 1;
+ }
+
+ return 0;
+ }
+
+ public int clear () {
+ int rc;
+
+ rc = db.exec ("DROP TABLE IF EXISTS Collection; CREATE TABLE Collection (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0); DROP TABLE IF EXISTS Loaned; CREATE TABLE Loaned (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0); DROP TABLE IF EXISTS Watchlist; CREATE TABLE Watchlist (Title TEXT NOT NULL, Year INTEGER, Rating INTEGER, Genres INTEGER NOT NULL DEFAULT 0);", callback, null);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private Cancellable? _cancellable;
+ public async int query (string table, MovieFilter filter, MovieSource.ReceiveMovieFunction callback, int limit, Cancellable? cancellable) {
+ var sql = "SELECT Title, Year, Rating, Genres FROM %s".printf (table);
+ var sep = " WHERE ";
+ Statement stmt;
+ int rc;
+
+ // FIXME - how many opcodes until main loop iteration for best responsivity?
+ _cancellable = cancellable;
+ db.progress_handler (1000, progress_handler);
+
+ if (filter.title != null && filter.title != "") {
+ if ("*" in filter.title)
+ sql += sep + "Title GLOB \"%s (*)\"".printf (filter.title);
+ else
+ sql += sep + "Title LIKE \"%s%%\"".printf (filter.title);
+ sep = " AND ";
+ }
+ if (filter.year_min > 0) {
+ sql += sep + "Year >= %d".printf (filter.year_min);
+ sep = " AND ";
+ }
+ if (filter.year_max > 0) {
+ sql += sep + "Year <= %d".printf (filter.year_max);
+ sep = " AND ";
+ }
+ if (filter.rating_min > 0) {
+ sql += sep + "Rating >= = %d".printf (filter.rating_min);
+ sep = " AND ";
+ }
+ if (filter.genres.field != 0) {
+ sql += sep + "Genres&%d = %d".printf (filter.genres.field, filter.genres.field);
+ }
+ sql += " LIMIT %d;".printf (limit);
+
+ stdout.printf("SQL: \"%s\"\n", sql);
+
+ rc = db.prepare_v2 (sql, -1, out stmt);
+ if (rc != Sqlite.OK) {
+ stderr.printf ("SQL error: %d, %s\n", rc, db.errmsg ());
+ db.progress_handler (0, null);
+ return 1;
+ }
+
+ do {
+ Idle.add (query.callback);
+ yield;
+ rc = stmt.step ();
+ if (rc == Sqlite.ROW) {
+ var movie = new Movie ();
+ movie.year = stmt.column_int (1);
+ movie.title = stmt.column_text (0);
+ movie.rating = stmt.column_int (2);
+ movie.genres.field = stmt.column_int (3);
+ // TODO - depending on settings, this could be something else, like director info or runtime
+ movie.secondary = movie.genres.to_string ();
+ callback (movie);
+ }
+ } while (rc == Sqlite.ROW);
+
+ db.progress_handler (0, null);
+ return 0;
+ }
+
+ private int progress_handler () {
+ ((MainContext) null).iteration (false);
+ return (int) _cancellable.is_cancelled ();
+ }
+}