1 From 8bb9ae73464dd76f5fa94f2e9ba76b0bd88114df Mon Sep 17 00:00:00 2001
2 From: Manuel Bachmann <manuel.bachmann@iot.bzh>
3 Date: Mon, 26 Oct 2015 04:18:33 +0000
4 Subject: [PATCH] Add LightMediaScanner plugin
6 Add a new plugin based on LightMediaScanner :
7 https://github.com/profusion/lightmediascanner
9 Shorty put, this plugin does not do the indexing itself as
10 "media-export" does, but defers this task to the
11 "lightmediascannerd" daemon, with which it communicates
12 via a D-Bus interface.
13 The remote indexing daemon installs itself as a separate
14 package, and is designed to be ultra-lightweight and fast.
16 This commit is the rebase and fusion of all work done
17 first on Maemo (lms.garage.maemo.org), then on Tizen IVI
18 (review.tizen.org/git/?p=profile/ivi/rygel.git) and right
19 now on AGL (automotivelinux.org) where several components
20 depend on LightMediaScanner.
21 A splitted version (13 commits) can also be seen on :
22 https://github.com/Tarnyko/rygel
24 It difffers mostly in that it lets "media-export" plugin
25 as the default, just adding itself in "rygel.conf" as an
28 (note : reporter is not code Author, see patch for details)
29 (note 2 : rebased on top of 0.26.1)
31 Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
32 Author: Alexander Kanavin <alex.kanavin@gmail.com>
33 Signed-off-by: Manuel Bachmann <manuel.bachmann@iot.bzh>
36 data/rygel.conf | 6 +-
37 src/plugins/Makefile.am | 5 +
38 src/plugins/lms/Makefile.am | 46 +++
39 src/plugins/lms/README | 17 +
40 src/plugins/lms/lms.plugin.in | 7 +
41 src/plugins/lms/rygel-lms-album.vala | 173 +++++++++
42 src/plugins/lms/rygel-lms-albums.vala | 175 +++++++++
43 src/plugins/lms/rygel-lms-all-images.vala | 95 +++++
44 src/plugins/lms/rygel-lms-all-music.vala | 169 ++++++++
45 src/plugins/lms/rygel-lms-all-videos.vala | 123 ++++++
46 src/plugins/lms/rygel-lms-artist.vala | 75 ++++
47 src/plugins/lms/rygel-lms-artists.vala | 62 +++
48 src/plugins/lms/rygel-lms-category-container.vala | 428 +++++++++++++++++++++
49 src/plugins/lms/rygel-lms-collate.c | 49 +++
50 src/plugins/lms/rygel-lms-database.vala | 294 ++++++++++++++
51 src/plugins/lms/rygel-lms-dbus-interfaces.vala | 30 ++
52 src/plugins/lms/rygel-lms-image-root.vala | 35 ++
53 src/plugins/lms/rygel-lms-image-year.vala | 114 ++++++
54 src/plugins/lms/rygel-lms-image-years.vala | 59 +++
55 src/plugins/lms/rygel-lms-music-root.vala | 36 ++
56 src/plugins/lms/rygel-lms-plugin-factory.vala | 40 ++
57 src/plugins/lms/rygel-lms-plugin.vala | 35 ++
58 src/plugins/lms/rygel-lms-root-container.vala | 58 +++
59 src/plugins/lms/rygel-lms-sql-function.vala | 31 ++
60 src/plugins/lms/rygel-lms-sql-operator.vala | 73 ++++
61 26 files changed, 2252 insertions(+), 1 deletion(-)
62 create mode 100644 src/plugins/lms/Makefile.am
63 create mode 100644 src/plugins/lms/README
64 create mode 100644 src/plugins/lms/lms.plugin.in
65 create mode 100644 src/plugins/lms/rygel-lms-album.vala
66 create mode 100644 src/plugins/lms/rygel-lms-albums.vala
67 create mode 100644 src/plugins/lms/rygel-lms-all-images.vala
68 create mode 100644 src/plugins/lms/rygel-lms-all-music.vala
69 create mode 100644 src/plugins/lms/rygel-lms-all-videos.vala
70 create mode 100644 src/plugins/lms/rygel-lms-artist.vala
71 create mode 100644 src/plugins/lms/rygel-lms-artists.vala
72 create mode 100644 src/plugins/lms/rygel-lms-category-container.vala
73 create mode 100644 src/plugins/lms/rygel-lms-collate.c
74 create mode 100644 src/plugins/lms/rygel-lms-database.vala
75 create mode 100644 src/plugins/lms/rygel-lms-dbus-interfaces.vala
76 create mode 100644 src/plugins/lms/rygel-lms-image-root.vala
77 create mode 100644 src/plugins/lms/rygel-lms-image-year.vala
78 create mode 100644 src/plugins/lms/rygel-lms-image-years.vala
79 create mode 100644 src/plugins/lms/rygel-lms-music-root.vala
80 create mode 100644 src/plugins/lms/rygel-lms-plugin-factory.vala
81 create mode 100644 src/plugins/lms/rygel-lms-plugin.vala
82 create mode 100644 src/plugins/lms/rygel-lms-root-container.vala
83 create mode 100644 src/plugins/lms/rygel-lms-sql-function.vala
84 create mode 100644 src/plugins/lms/rygel-lms-sql-operator.vala
86 diff --git a/configure.ac b/configure.ac
87 index 7ae1105..275fd99 100644
90 @@ -170,6 +170,18 @@ AS_IF([test "x$enable_ruih_plugin" = "xyes"],
91 libxml-2.0 >= $LIBXML_REQUIRED])
95 +RYGEL_ADD_PLUGIN([lms],[LightMediaScanner],[yes])
96 +AS_IF([test "x$enable_lms_plugin" = "xyes"],
98 + PKG_CHECK_MODULES([RYGEL_PLUGIN_LMS_DEPS],
99 + [$RYGEL_COMMON_MODULES
100 + gio-2.0 >= $GIO_REQUIRED
101 + sqlite3 >= $LIBSQLITE3_REQUIRED])
102 + RYGEL_PLUGIN_LMS_DEPS_VALAFLAGS="$RYGEL_COMMON_MODULES_VALAFLAGS --pkg gio-2.0 --pkg gee-0.8 --pkg sqlite3"
103 + AC_SUBST([RYGEL_PLUGIN_LMS_DEPS_VALAFLAGS])
106 AS_IF([test "x$with_media_engine" = "xgstreamer"],
108 RYGEL_ADD_PLUGIN([playbin],[GStreamer playbin],[yes])
109 @@ -332,6 +344,11 @@ then
113 +dnl Check additional requirements for LMS plugin
114 +if test "x$enable_lms_plugin" = "xyes";
116 + RYGEL_CHECK_PACKAGES([sqlite3])
119 RYGEL_ADD_PLUGIN([tracker],[Tracker],[yes])
120 AS_IF([test "x$enable_tracker_plugin" = "xyes"],
121 @@ -513,6 +530,7 @@ echo "
122 version: ${tracker_api_version}
123 mediathek: ${enable_mediathek_plugin}
124 media-export ${enable_media_export_plugin}
125 + lightmediascanner ${enable_lms_plugin}
126 external: ${enable_external_plugin}
127 MPRIS2: ${enable_mpris_plugin}
128 gst-launch: ${enable_gst_launch_plugin}
129 diff --git a/data/rygel.conf b/data/rygel.conf
130 index 6b1c1c4..8677a0d 100644
131 --- a/data/rygel.conf
132 +++ b/data/rygel.conf
133 @@ -99,7 +99,7 @@ strict-sharing=false
134 title=@REALNAME@'s media on @PRETTY_HOSTNAME@
139 title=@REALNAME@'s media on @PRETTY_HOSTNAME@
140 # List of URIs to export. Following variables are automatically substituted by
141 # the appropriate XDG standard media folders by Rygel for you.
142 @@ -114,6 +114,10 @@ monitor-changes=true
143 monitor-grace-timeout=5
152 title=Audio/Video playback on @PRETTY_HOSTNAME@
153 diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
154 index d116f09..40791f0 100644
155 --- a/src/plugins/Makefile.am
156 +++ b/src/plugins/Makefile.am
157 @@ -10,6 +10,10 @@ if BUILD_MEDIA_EXPORT_PLUGIN
158 MEDIA_EXPORT_PLUGIN = media-export
165 if BUILD_EXTERNAL_PLUGIN
166 EXTERNAL_PLUGIN = external
168 @@ -33,6 +37,7 @@ endif
169 SUBDIRS = $(TRACKER_PLUGIN) \
170 $(MEDIATHEK_PLUGIN) \
171 $(MEDIA_EXPORT_PLUGIN) \
175 $(GST_LAUNCH_PLUGIN) \
176 diff --git a/src/plugins/lms/Makefile.am b/src/plugins/lms/Makefile.am
178 index 0000000..f96a2ab
180 +++ b/src/plugins/lms/Makefile.am
182 +include $(top_srcdir)/common.am
184 +plugin_LTLIBRARIES = librygel-lms.la
185 +plugin_DATA = lms.plugin
187 +librygel_lms_la_SOURCES = \
188 + rygel-lms-plugin.vala \
189 + rygel-lms-plugin-factory.vala \
190 + rygel-lms-root-container.vala \
191 + rygel-lms-music-root.vala \
192 + rygel-lms-image-root.vala \
193 + rygel-lms-category-container.vala \
194 + rygel-lms-all-music.vala \
195 + rygel-lms-album.vala \
196 + rygel-lms-albums.vala \
197 + rygel-lms-artist.vala \
198 + rygel-lms-artists.vala \
199 + rygel-lms-all-videos.vala \
200 + rygel-lms-database.vala \
201 + rygel-lms-all-images.vala \
202 + rygel-lms-image-years.vala \
203 + rygel-lms-image-year.vala \
204 + rygel-lms-sql-function.vala \
205 + rygel-lms-sql-operator.vala \
206 + rygel-lms-collate.c \
207 + rygel-lms-dbus-interfaces.vala
209 +librygel_lms_la_VALAFLAGS = \
210 + --enable-experimental \
211 + $(RYGEL_PLUGIN_LMS_DEPS_VALAFLAGS) \
212 + $(RYGEL_COMMON_LIBRYGEL_SERVER_VALAFLAGS) \
213 + $(RYGEL_COMMON_VALAFLAGS)
215 +librygel_lms_la_CFLAGS = \
216 + $(RYGEL_PLUGIN_LMS_DEPS_CFLAGS) \
217 + $(RYGEL_COMMON_LIBRYGEL_SERVER_CFLAGS) \
218 + -DG_LOG_DOMAIN='"Lms"'
220 +librygel_lms_la_LIBADD = \
221 + $(RYGEL_PLUGIN_LMS_DEPS_LIBS) \
222 + $(RYGEL_COMMON_LIBRYGEL_SERVER_LIBS)
224 +librygel_lms_la_LDFLAGS = \
225 + $(RYGEL_PLUGIN_LINKER_FLAGS)
227 +EXTRA_DIST = lms.plugin.in
228 diff --git a/src/plugins/lms/README b/src/plugins/lms/README
230 index 0000000..b741806
232 +++ b/src/plugins/lms/README
237 +A rygel mediaserver plugin that exposes a lightmediascanner database
240 +Configuration in rygel.conf:
242 + [LightMediaScanner]
243 + db-path=/path/to/lightmediascannerd.sqlite3
246 +* Supports browsing and searching (but in many cases searches will
247 + still fall back to the inefficient simple_search()).
248 +* UpdateIDs are not yet supported as lightmediascanner seems to have
249 + not change signal support yet
250 +* No real DLNA CTT testing has been done so far
251 diff --git a/src/plugins/lms/lms.plugin.in b/src/plugins/lms/lms.plugin.in
253 index 0000000..9db9895
255 +++ b/src/plugins/lms/lms.plugin.in
262 +Description = LMS DMS plugin for Rygel
263 +Copyright = Copyright © Intel
264 diff --git a/src/plugins/lms/rygel-lms-album.vala b/src/plugins/lms/rygel-lms-album.vala
266 index 0000000..4fea17a
268 +++ b/src/plugins/lms/rygel-lms-album.vala
271 + * Copyright (C) 2013 Intel Corporation.
273 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
275 + * This file is part of Rygel.
277 + * Rygel is free software; you can redistribute it and/or modify
278 + * it under the terms of the GNU Lesser General Public License as published by
279 + * the Free Software Foundation; either version 2 of the License, or
280 + * (at your option) any later version.
282 + * Rygel is distributed in the hope that it will be useful,
283 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
284 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
285 + * GNU Lesser General Public License for more details.
287 + * You should have received a copy of the GNU Lesser General Public License
288 + * along with this program; if not, write to the Free Software Foundation,
289 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
295 +public class Rygel.LMS.Album : Rygel.LMS.CategoryContainer {
296 + private static const string SQL_ALL_TEMPLATE =
297 + "SELECT files.id, files.path, files.size, " +
298 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
299 + "audio_artists.name as artist, " +
300 + "audio_albums.name " +
301 + "FROM audios, files " +
302 + "LEFT JOIN audio_artists " +
303 + "ON audios.artist_id = audio_artists.id " +
304 + "LEFT JOIN audio_albums " +
305 + "ON audios.album_id = audio_albums.id " +
306 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = %s " +
307 + "LIMIT ? OFFSET ?;";
309 + private static const string SQL_COUNT_TEMPLATE =
310 + "SELECT COUNT(audios.id) " +
311 + "FROM audios, files " +
312 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = %s;";
314 + private static const string SQL_COUNT_WITH_FILTER_TEMPLATE =
315 + "SELECT COUNT(audios.id), audios.title as title, " +
316 + "audio_artists.name as artist, " +
317 + "audio_albums.name " +
318 + "FROM audios, files " +
319 + "LEFT JOIN audio_artists " +
320 + "ON audios.artist_id = audio_artists.id " +
321 + "LEFT JOIN audio_albums " +
322 + "ON audios.album_id = audio_albums.id " +
323 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = %s;";
325 + private static const string SQL_FIND_OBJECT_TEMPLATE =
326 + "SELECT files.id, files.path, files.size, " +
327 + "audios.title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
328 + "audio_artists.name, " +
329 + "audio_albums.name " +
330 + "FROM audios, files " +
331 + "LEFT JOIN audio_artists " +
332 + "ON audios.artist_id = audio_artists.id " +
333 + "LEFT JOIN audio_albums " +
334 + "ON audios.album_id = audio_albums.id " +
335 + "WHERE dtime = 0 AND files.id = ? AND audios.id = files.id AND audios.album_id = %s;";
337 + private static const string SQL_ADDED_TEMPLATE =
338 + "SELECT files.id, files.path, files.size, " +
339 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
340 + "audio_artists.name as artist, " +
341 + "audio_albums.name " +
342 + "FROM audios, files " +
343 + "LEFT JOIN audio_artists " +
344 + "ON audios.artist_id = audio_artists.id " +
345 + "LEFT JOIN audio_albums " +
346 + "ON audios.album_id = audio_albums.id " +
347 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = %s " +
348 + "AND update_id > ? AND update_id <= ?;";
350 + private static const string SQL_REMOVED_TEMPLATE =
351 + "SELECT files.id, files.path, files.size, " +
352 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
353 + "audio_artists.name as artist, " +
354 + "audio_albums.name " +
355 + "FROM audios, files " +
356 + "LEFT JOIN audio_artists " +
357 + "ON audios.artist_id = audio_artists.id " +
358 + "LEFT JOIN audio_albums " +
359 + "ON audios.album_id = audio_albums.id " +
360 + "WHERE dtime <> 0 AND audios.id = files.id AND audios.album_id = %s " +
361 + "AND update_id > ? AND update_id <= ?;";
363 + protected override MediaObject? object_from_statement (Statement statement) {
364 + var id = statement.column_int (0);
365 + var path = statement.column_text (1);
366 + var mime_type = statement.column_text(10);
368 + if (mime_type == null || mime_type.length == 0) {
369 + /* TODO is this correct? */
370 + debug ("Music item %d (%s) has no MIME type",
375 + var title = statement.column_text(3);
376 + var song_id = this.build_child_id (id);
377 + var song = new MusicItem (song_id, this, title);
378 + song.ref_id = this.build_reference_id (id);
379 + song.size = statement.column_int(2);
380 + song.track_number = statement.column_int(4);
381 + song.duration = statement.column_int(5);
382 + song.channels = statement.column_int(6);
383 + song.sample_freq = statement.column_int(7);
384 + song.bitrate = statement.column_int(8);
385 + song.dlna_profile = statement.column_text(9);
386 + song.mime_type = mime_type;
387 + song.artist = statement.column_text(11);
388 + song.album = statement.column_text(12);
389 + File file = File.new_for_path (path);
390 + song.add_uri (file.get_uri ());
395 + private static string get_sql_all (string db_id) {
396 + return (SQL_ALL_TEMPLATE.printf (db_id));
398 + private static string get_sql_find_object (string db_id) {
399 + return (SQL_FIND_OBJECT_TEMPLATE.printf (db_id));
401 + private static string get_sql_count (string db_id) {
402 + return (SQL_COUNT_TEMPLATE.printf (db_id));
404 + private static string get_sql_added (string db_id) {
405 + return (SQL_ADDED_TEMPLATE.printf (db_id));
407 + private static string get_sql_removed (string db_id) {
408 + return (SQL_REMOVED_TEMPLATE.printf (db_id));
411 + protected override string get_sql_all_with_filter (string filter) {
412 + if (filter.length == 0) {
413 + return this.sql_all;
415 + var filter_str = "%s AND %s".printf (this.db_id, filter);
416 + return (SQL_ALL_TEMPLATE.printf (filter_str));
419 + protected override string get_sql_count_with_filter (string filter) {
420 + if (filter.length == 0) {
421 + return this.sql_count;
423 + var filter_str = "%s AND %s".printf (this.db_id, filter);
424 + return (SQL_COUNT_WITH_FILTER_TEMPLATE.printf (filter_str));
427 + public Album (string db_id,
428 + MediaContainer parent,
430 + LMS.Database lms_db) {
435 + get_sql_all (db_id),
436 + get_sql_find_object (db_id),
437 + get_sql_count (db_id),
438 + get_sql_added (db_id),
439 + get_sql_removed (db_id)
443 diff --git a/src/plugins/lms/rygel-lms-albums.vala b/src/plugins/lms/rygel-lms-albums.vala
445 index 0000000..309a352
447 +++ b/src/plugins/lms/rygel-lms-albums.vala
450 + * Copyright (C) 2013 Intel Corporation.
452 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
454 + * This file is part of Rygel.
456 + * Rygel is free software; you can redistribute it and/or modify
457 + * it under the terms of the GNU Lesser General Public License as published by
458 + * the Free Software Foundation; either version 2 of the License, or
459 + * (at your option) any later version.
461 + * Rygel is distributed in the hope that it will be useful,
462 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
463 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
464 + * GNU Lesser General Public License for more details.
466 + * You should have received a copy of the GNU Lesser General Public License
467 + * along with this program; if not, write to the Free Software Foundation,
468 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
474 +public class Rygel.LMS.Albums : Rygel.LMS.CategoryContainer {
475 + private static const string SQL_ALL =
476 + "SELECT audio_albums.id, audio_albums.name as title, " +
477 + "audio_artists.name as artist " +
478 + "FROM audio_albums " +
479 + "LEFT JOIN audio_artists " +
480 + "ON audio_albums.artist_id = audio_artists.id " +
481 + "LIMIT ? OFFSET ?;";
483 + private static const string SQL_ALL_WITH_FILTER_TEMPLATE =
484 + "SELECT audio_albums.id, audio_albums.name as title, " +
485 + "audio_artists.name as artist " +
486 + "FROM audio_albums " +
487 + "LEFT JOIN audio_artists " +
488 + "ON audio_albums.artist_id = audio_artists.id " +
490 + "LIMIT ? OFFSET ?;";
492 + private static const string SQL_COUNT =
493 + "SELECT COUNT(audio_albums.id) " +
494 + "FROM audio_albums;";
496 + private static const string SQL_COUNT_WITH_FILTER_TEMPLATE =
497 + "SELECT COUNT(audio_albums.id), audio_albums.name as title, " +
498 + "audio_artists.name as artist " +
499 + "FROM audio_albums " +
500 + "LEFT JOIN audio_artists " +
501 + "ON audio_albums.artist_id = audio_artists.id " +
504 + /* count songs inside albums */
505 + private static const string SQL_CHILD_COUNT_WITH_FILTER_TEMPLATE =
506 + "SELECT COUNT(audios.id), audios.title as title, " +
507 + "audio_artists.name as artist " +
508 + "FROM audios, files, audio_albums " +
509 + "LEFT JOIN audio_artists " +
510 + "ON audios.artist_id = audio_artists.id " +
511 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = audio_albums.id %s;";
513 + /* select songs inside albums */
514 + private static const string SQL_CHILD_ALL_WITH_FILTER_TEMPLATE =
515 + "SELECT files.id, files.path, files.size, " +
516 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
517 + "audio_artists.name as artist, " +
518 + "audio_albums.name, audio_albums.id " +
519 + "FROM audios, files, audio_albums " +
520 + "LEFT JOIN audio_artists " +
521 + "ON audios.artist_id = audio_artists.id " +
522 + "WHERE dtime = 0 AND audios.id = files.id AND audios.album_id = audio_albums.id %s " +
523 + "LIMIT ? OFFSET ?;";
526 + private static const string SQL_FIND_OBJECT =
527 + "SELECT audio_albums.id, audio_albums.name " +
528 + "FROM audio_albums " +
529 + "WHERE audio_albums.id = ?;";
531 + protected override string get_sql_all_with_filter (string filter) {
532 + if (filter.length == 0) {
533 + return Albums.SQL_ALL;
535 + return (Albums.SQL_ALL_WITH_FILTER_TEMPLATE.printf (filter));
538 + protected override string get_sql_count_with_filter (string filter) {
539 + if (filter.length == 0) {
540 + return Albums.SQL_COUNT;
542 + return (Albums.SQL_COUNT_WITH_FILTER_TEMPLATE.printf (filter));
545 + protected override uint get_child_count_with_filter (string where_filter,
549 + /* search the children (albums) as usual */
550 + var count = base.get_child_count_with_filter (where_filter, args);
552 + /* now search the album contents */
554 + if (where_filter.length > 0) {
555 + filter = "AND %s".printf (where_filter);
557 + var query = Albums.SQL_CHILD_COUNT_WITH_FILTER_TEMPLATE.printf (filter);
559 + var stmt = this.lms_db.prepare_and_init (query, args.values);
560 + if (stmt.step () == Sqlite.ROW) {
561 + count += stmt.column_int (0);
563 + } catch (DatabaseError e) {
564 + warning ("Query failed: %s", e.message);
570 + protected override MediaObjects? get_children_with_filter (string where_filter,
572 + string sort_criteria,
575 + var children = base. get_children_with_filter (where_filter,
581 + if (where_filter.length > 0) {
582 + filter = "AND %s".printf (where_filter);
584 + var query = Albums.SQL_CHILD_ALL_WITH_FILTER_TEMPLATE.printf (filter);
586 + var stmt = this.lms_db.prepare_and_init (query, args.values);
587 + while (Database.get_children_step (stmt)) {
588 + var album_id = stmt.column_text (13);
589 + var album = new Album (album_id, this, "", this.lms_db);
591 + var song = album.object_from_statement (stmt);
592 + song.parent_ref = song.parent;
593 + children.add (song);
596 + } catch (DatabaseError e) {
597 + warning ("Query failed: %s", e.message);
603 + protected override MediaObject? object_from_statement (Statement statement) {
604 + var id = "%d".printf (statement.column_int (0));
605 + LMS.Album album = new LMS.Album (id,
607 + statement.column_text (1),
612 + public Albums (MediaContainer parent,
613 + LMS.Database lms_db) {
619 + Albums.SQL_FIND_OBJECT,
624 diff --git a/src/plugins/lms/rygel-lms-all-images.vala b/src/plugins/lms/rygel-lms-all-images.vala
626 index 0000000..0b54c7f
628 +++ b/src/plugins/lms/rygel-lms-all-images.vala
631 + * Copyright (C) 2013 Intel Corporation.
633 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
635 + * This file is part of Rygel.
637 + * Rygel is free software; you can redistribute it and/or modify
638 + * it under the terms of the GNU Lesser General Public License as published by
639 + * the Free Software Foundation; either version 2 of the License, or
640 + * (at your option) any later version.
642 + * Rygel is distributed in the hope that it will be useful,
643 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
644 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 + * GNU Lesser General Public License for more details.
647 + * You should have received a copy of the GNU Lesser General Public License
648 + * along with this program; if not, write to the Free Software Foundation,
649 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
655 +public class Rygel.LMS.AllImages : Rygel.LMS.CategoryContainer {
656 + private static const string SQL_ALL =
657 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime " +
658 + "FROM images, files " +
659 + "WHERE dtime = 0 AND images.id = files.id " +
660 + "LIMIT ? OFFSET ?;";
662 + private static const string SQL_COUNT =
663 + "SELECT count(images.id) " +
664 + "FROM images, files " +
665 + "WHERE dtime = 0 AND images.id = files.id;";
667 + private static const string SQL_FIND_OBJECT =
668 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime " +
669 + "FROM images, files " +
670 + "WHERE dtime = 0 AND files.id = ? AND images.id = files.id;";
672 + private static const string SQL_ADDED =
673 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime " +
674 + "FROM images, files " +
675 + "WHERE dtime = 0 AND images.id = files.id " +
676 + "AND update_id > ? AND update_id <= ?;";
678 + private static const string SQL_REMOVED =
679 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime " +
680 + "FROM images, files " +
681 + "WHERE dtime <> 0 AND images.id = files.id " +
682 + "AND update_id > ? AND update_id <= ?;";
684 + protected override MediaObject? object_from_statement (Statement statement) {
685 + var id = statement.column_int(0);
686 + var path = statement.column_text(6);
687 + var mime_type = statement.column_text(9);
689 + if (mime_type == null || mime_type.length == 0){
690 + /* TODO is this correct? */
691 + debug ("Image item %d (%s) has no MIME type",
696 + var title = statement.column_text(1);
697 + var image = new ImageItem(this.build_child_id (id), this, title);
698 + image.creator = statement.column_text(2);
699 + TimeVal tv = { (long) statement.column_int(3), (long) 0 };
700 + image.date = tv.to_iso8601 ();
701 + image.width = statement.column_int(4);
702 + image.height = statement.column_int(5);
703 + image.size = statement.column_int(7);
704 + image.mime_type = mime_type;
705 + image.dlna_profile = statement.column_text(8);
706 + File file = File.new_for_path(path);
707 + image.add_uri (file.get_uri ());
712 + public AllImages (MediaContainer parent, LMS.Database lms_db) {
718 + AllImages.SQL_FIND_OBJECT,
719 + AllImages.SQL_COUNT,
720 + AllImages.SQL_ADDED,
721 + AllImages.SQL_REMOVED
725 diff --git a/src/plugins/lms/rygel-lms-all-music.vala b/src/plugins/lms/rygel-lms-all-music.vala
727 index 0000000..2a7226f
729 +++ b/src/plugins/lms/rygel-lms-all-music.vala
732 + * Copyright (C) 2013 Intel Corporation.
734 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
736 + * This file is part of Rygel.
738 + * Rygel is free software; you can redistribute it and/or modify
739 + * it under the terms of the GNU Lesser General Public License as published by
740 + * the Free Software Foundation; either version 2 of the License, or
741 + * (at your option) any later version.
743 + * Rygel is distributed in the hope that it will be useful,
744 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
745 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
746 + * GNU Lesser General Public License for more details.
748 + * You should have received a copy of the GNU Lesser General Public License
749 + * along with this program; if not, write to the Free Software Foundation,
750 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
756 +public class Rygel.LMS.AllMusic : Rygel.LMS.CategoryContainer {
757 + private static const string SQL_ALL_TEMPLATE =
758 + "SELECT files.id, files.path, files.size, " +
759 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
760 + "audio_artists.name as artist, " +
761 + "audio_albums.name, " +
763 + "audio_genres.name " +
764 + "FROM audios, files " +
765 + "LEFT JOIN audio_artists " +
766 + "ON audios.artist_id = audio_artists.id " +
767 + "LEFT JOIN audio_albums " +
768 + "ON audios.album_id = audio_albums.id " +
769 + "LEFT JOIN audio_genres " +
770 + "ON audios.genre_id = audio_genres.id " +
771 + "WHERE dtime = 0 AND audios.id = files.id %s " +
772 + "LIMIT ? OFFSET ?;";
774 + private static const string SQL_COUNT =
775 + "SELECT COUNT(audios.id) " +
776 + "FROM audios, files " +
777 + "WHERE dtime = 0 AND audios.id = files.id;";
779 + private static const string SQL_COUNT_WITH_FILTER_TEMPLATE =
780 + "SELECT COUNT(audios.id), audios.title as title, " +
781 + "audio_artists.name as artist " +
782 + "FROM audios, files " +
783 + "LEFT JOIN audio_artists " +
784 + "ON audios.artist_id = audio_artists.id " +
785 + "WHERE dtime = 0 AND audios.id = files.id %s;";
787 + private static const string SQL_FIND_OBJECT =
788 + "SELECT files.id, files.path, files.size, " +
789 + "audios.title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
790 + "audio_artists.name, " +
791 + "audio_albums.name, " +
793 + "audio_genres.name " +
794 + "FROM audios, files " +
795 + "LEFT JOIN audio_artists " +
796 + "ON audios.artist_id = audio_artists.id " +
797 + "LEFT JOIN audio_albums " +
798 + "ON audios.album_id = audio_albums.id " +
799 + "LEFT JOIN audio_genres " +
800 + "ON audios.genre_id = audio_genres.id " +
801 + "WHERE dtime = 0 AND files.id = ? AND audios.id = files.id;";
803 + private static const string SQL_ADDED =
804 + "SELECT files.id, files.path, files.size, " +
805 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
806 + "audio_artists.name as artist, " +
807 + "audio_albums.name, " +
809 + "audio_genres.name " +
810 + "FROM audios, files " +
811 + "LEFT JOIN audio_artists " +
812 + "ON audios.artist_id = audio_artists.id " +
813 + "LEFT JOIN audio_albums " +
814 + "ON audios.album_id = audio_albums.id " +
815 + "LEFT JOIN audio_genres " +
816 + "ON audios.genre_id = audio_genres.id " +
817 + "WHERE dtime = 0 AND audios.id = files.id " +
818 + "AND update_id > ? AND update_id <= ?;";
820 + private static const string SQL_REMOVED =
821 + "SELECT files.id, files.path, files.size, " +
822 + "audios.title as title, audios.trackno, audios.length, audios.channels, audios.sampling_rate, audios.bitrate, audios.dlna_profile, audios.dlna_mime, " +
823 + "audio_artists.name as artist, " +
824 + "audio_albums.name, " +
826 + "audio_genres.name " +
827 + "FROM audios, files " +
828 + "LEFT JOIN audio_artists " +
829 + "ON audios.artist_id = audio_artists.id " +
830 + "LEFT JOIN audio_albums " +
831 + "ON audios.album_id = audio_albums.id " +
832 + "LEFT JOIN audio_genres " +
833 + "ON audios.genre_id = audio_genres.id " +
834 + "WHERE dtime <> 0 AND audios.id = files.id " +
835 + "AND update_id > ? AND update_id <= ?;";
837 + protected override string get_sql_all_with_filter (string filter) {
838 + if (filter.length == 0) {
839 + return this.sql_all;
841 + var filter_str = "AND %s".printf (filter);
842 + return (AllMusic.SQL_ALL_TEMPLATE.printf (filter_str));
845 + protected override string get_sql_count_with_filter (string filter) {
846 + if (filter.length == 0) {
847 + return this.sql_count;
849 + var filter_str = "AND %s".printf (filter);
850 + return (AllMusic.SQL_COUNT_WITH_FILTER_TEMPLATE.printf (filter_str));
853 + protected override MediaObject? object_from_statement (Statement statement) {
854 + var id = statement.column_int (0);
855 + var path = statement.column_text (1);
856 + var mime_type = statement.column_text(10);
858 + if (mime_type == null || mime_type.length == 0) {
859 + /* TODO is this correct? */
860 + debug ("Music item %d (%s) has no MIME type",
865 + var title = statement.column_text(3);
866 + var song_id = this.build_child_id (id);
867 + var song = new MusicItem (song_id, this, title);
868 + song.size = statement.column_int(2);
869 + song.track_number = statement.column_int(4);
870 + song.duration = statement.column_int(5);
871 + song.channels = statement.column_int(6);
872 + song.sample_freq = statement.column_int(7);
873 + song.bitrate = statement.column_int(8);
874 + song.dlna_profile = statement.column_text(9);
875 + song.mime_type = mime_type;
876 + song.artist = statement.column_text(11);
877 + song.album = statement.column_text(12);
878 + TimeVal tv = { (long) statement.column_int(13), (long) 0 };
879 + song.date = tv.to_iso8601 ();
880 + song.genre = statement.column_text(14);
881 + File file = File.new_for_path (path);
882 + song.add_uri (file.get_uri ());
887 + public AllMusic (MediaContainer parent, LMS.Database lms_db) {
892 + AllMusic.SQL_ALL_TEMPLATE.printf (""),
893 + AllMusic.SQL_FIND_OBJECT,
894 + AllMusic.SQL_COUNT,
895 + AllMusic.SQL_ADDED,
896 + AllMusic.SQL_REMOVED
900 diff --git a/src/plugins/lms/rygel-lms-all-videos.vala b/src/plugins/lms/rygel-lms-all-videos.vala
902 index 0000000..dbde0db
904 +++ b/src/plugins/lms/rygel-lms-all-videos.vala
907 + * Copyright (C) 2013 Intel Corporation.
909 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
911 + * This file is part of Rygel.
913 + * Rygel is free software; you can redistribute it and/or modify
914 + * it under the terms of the GNU Lesser General Public License as published by
915 + * the Free Software Foundation; either version 2 of the License, or
916 + * (at your option) any later version.
918 + * Rygel is distributed in the hope that it will be useful,
919 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
920 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
921 + * GNU Lesser General Public License for more details.
923 + * You should have received a copy of the GNU Lesser General Public License
924 + * along with this program; if not, write to the Free Software Foundation,
925 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
931 +public class Rygel.LMS.AllVideos : Rygel.LMS.CategoryContainer {
932 + private static const string SQL_ALL =
933 + "SELECT videos.id, title, artist, length, path, mtime, size, dlna_profile, dlna_mime " +
934 + "FROM videos, files " +
935 + "WHERE dtime = 0 AND videos.id = files.id " +
936 + "LIMIT ? OFFSET ?;";
938 + private static const string SQL_COUNT =
939 + "SELECT count(videos.id) " +
940 + "FROM videos, files " +
941 + "WHERE dtime = 0 AND videos.id = files.id;";
943 + private static const string SQL_FIND_OBJECT =
944 + "SELECT videos.id, title, artist, length, path, mtime, size, dlna_profile, dlna_mime " +
945 + "FROM videos, files " +
946 + "WHERE dtime = 0 AND files.id = ? AND videos.id = files.id;";
948 + private static const string SQL_ADDED =
949 + "SELECT videos.id, title, artist, length, path, mtime, size, dlna_profile, dlna_mime " +
950 + "FROM videos, files " +
951 + "WHERE dtime = 0 AND videos.id = files.id " +
952 + "AND update_id > ? AND update_id <= ?;";
954 + private static const string SQL_REMOVED =
955 + "SELECT videos.id, title, artist, length, path, mtime, size, dlna_profile, dlna_mime " +
956 + "FROM videos, files " +
957 + "WHERE dtime <> 0 AND videos.id = files.id " +
958 + "AND update_id > ? AND update_id <= ?;";
960 + protected override MediaObject? object_from_statement (Statement statement) {
961 + var id = statement.column_int(0);
962 + var mime_type = statement.column_text(8);
963 + var path = statement.column_text(4);
964 + var file = File.new_for_path(path);
966 + /* TODO: Temporary code to extract the MIME TYPE. LMS does not seem
967 + to compute the mime type of videos. Don't know why. */
969 +/* if (mime_type == null || mime_type.length == 0) {
971 + FileInfo info = file.query_info(FileAttribute.STANDARD_CONTENT_TYPE,
972 + FileQueryInfoFlags.NONE, null);
973 + mime_type = info.get_content_type();
978 + if (mime_type == null || mime_type.length == 0) {
979 + /* TODO is this correct? */
980 + debug ("Video item %d (%s) has no MIME type",
985 + var title = statement.column_text(1);
986 + var video = new VideoItem(this.build_child_id (id), this, title);
987 + video.creator = statement.column_text(2);
988 + video.duration = statement.column_int(3);
989 + TimeVal tv = { (long) statement.column_int(5), (long) 0 };
990 + video.date = tv.to_iso8601 ();
991 + video.size = statement.column_int(6);
992 + video.dlna_profile = statement.column_text(7);
993 + video.mime_type = mime_type;
994 + video.add_uri (file.get_uri ());
996 + // Rygel does not support multiple video and audio tracks in a single file,
997 + // so we just take the first one
998 + var video_data = "select videos_videos.bitrate + videos_audios.bitrate, width, height, channels, sampling_rate " +
999 + "from videos, videos_audios, videos_videos where videos.id = ? " +
1000 + "and videos.id = videos_audios.video_id and videos.id = videos_videos.video_id;";
1002 + var stmt = this.lms_db.prepare(video_data);
1003 + Rygel.LMS.Database.find_object("%d".printf(id), stmt);
1004 + video.bitrate = stmt.column_int(0) / 8; //convert bits per second into bytes per second
1005 + video.width = stmt.column_int(1);
1006 + video.height = stmt.column_int(2);
1007 + video.channels = stmt.column_int(3);
1008 + video.sample_freq = stmt.column_int(4);
1009 + } catch (DatabaseError e) {
1010 + warning ("Query failed: %s", e.message);
1016 + public AllVideos (string id, MediaContainer parent, string title, LMS.Database lms_db){
1021 + AllVideos.SQL_ALL,
1022 + AllVideos.SQL_FIND_OBJECT,
1023 + AllVideos.SQL_COUNT,
1024 + AllVideos.SQL_ADDED,
1025 + AllVideos.SQL_REMOVED
1029 diff --git a/src/plugins/lms/rygel-lms-artist.vala b/src/plugins/lms/rygel-lms-artist.vala
1030 new file mode 100644
1031 index 0000000..31e9070
1033 +++ b/src/plugins/lms/rygel-lms-artist.vala
1036 + * Copyright (C) 2013 Intel Corporation.
1038 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
1040 + * This file is part of Rygel.
1042 + * Rygel is free software; you can redistribute it and/or modify
1043 + * it under the terms of the GNU Lesser General Public License as published by
1044 + * the Free Software Foundation; either version 2 of the License, or
1045 + * (at your option) any later version.
1047 + * Rygel is distributed in the hope that it will be useful,
1048 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1049 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1050 + * GNU Lesser General Public License for more details.
1052 + * You should have received a copy of the GNU Lesser General Public License
1053 + * along with this program; if not, write to the Free Software Foundation,
1054 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1060 +public class Rygel.LMS.Artist : Rygel.LMS.CategoryContainer {
1061 + private static const string SQL_ALL_TEMPLATE =
1062 + "SELECT audio_albums.id, audio_albums.name " +
1063 + "FROM audio_albums " +
1064 + "WHERE audio_albums.artist_id = %s " +
1065 + "LIMIT ? OFFSET ?;";
1067 + private static const string SQL_COUNT_TEMPLATE =
1068 + "SELECT COUNT(audio_albums.id) " +
1069 + "FROM audio_albums " +
1070 + "WHERE audio_albums.artist_id = %s";
1072 + private static const string SQL_FIND_OBJECT_TEMPLATE =
1073 + "SELECT audio_albums.id, audio_albums.name " +
1074 + "FROM audio_albums " +
1075 + "WHERE audio_albums.id = ? AND audio_albums.artist_id = %s;";
1077 + private static string get_sql_all (string id) {
1078 + return (SQL_ALL_TEMPLATE.printf (id));
1080 + private static string get_sql_find_object (string id) {
1081 + return (SQL_FIND_OBJECT_TEMPLATE.printf (id));
1083 + private static string get_sql_count (string id) {
1084 + return (SQL_COUNT_TEMPLATE.printf (id));
1087 + protected override MediaObject? object_from_statement (Statement statement) {
1088 + var db_id = "%d".printf (statement.column_int (0));
1089 + var title = statement.column_text (1);
1090 + return new LMS.Album (db_id, this, title, this.lms_db);
1093 + public Artist (string id,
1094 + MediaContainer parent,
1096 + LMS.Database lms_db) {
1103 + get_sql_find_object (id),
1104 + get_sql_count (id),
1105 + null, // LMS does not track adding or removing albums
1110 diff --git a/src/plugins/lms/rygel-lms-artists.vala b/src/plugins/lms/rygel-lms-artists.vala
1111 new file mode 100644
1112 index 0000000..a00b2ce
1114 +++ b/src/plugins/lms/rygel-lms-artists.vala
1117 + * Copyright (C) 2013 Intel Corporation.
1119 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
1121 + * This file is part of Rygel.
1123 + * Rygel is free software; you can redistribute it and/or modify
1124 + * it under the terms of the GNU Lesser General Public License as published by
1125 + * the Free Software Foundation; either version 2 of the License, or
1126 + * (at your option) any later version.
1128 + * Rygel is distributed in the hope that it will be useful,
1129 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1130 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1131 + * GNU Lesser General Public License for more details.
1133 + * You should have received a copy of the GNU Lesser General Public License
1134 + * along with this program; if not, write to the Free Software Foundation,
1135 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1141 +public class Rygel.LMS.Artists : Rygel.LMS.CategoryContainer {
1142 + private static const string SQL_ALL =
1143 + "SELECT audio_artists.id, audio_artists.name " +
1144 + "FROM audio_artists " +
1145 + "LIMIT ? OFFSET ?;";
1147 + private static const string SQL_COUNT =
1148 + "SELECT COUNT(audio_artists.id) " +
1149 + "FROM audio_artists;";
1151 + private static const string SQL_FIND_OBJECT =
1152 + "SELECT audio_artists.id, audio_artists.name " +
1153 + "FROM audio_artists " +
1154 + "WHERE audio_artists.id = ?;";
1156 + protected override MediaObject? object_from_statement (Statement statement) {
1157 + var db_id = "%d".printf (statement.column_int (0));
1158 + var title = statement.column_text (1);
1160 + return new LMS.Artist (db_id, this, title, this.lms_db);
1163 + public Artists (string id,
1164 + MediaContainer parent,
1166 + LMS.Database lms_db) {
1172 + Artists.SQL_FIND_OBJECT,
1173 + Artists.SQL_COUNT,
1178 diff --git a/src/plugins/lms/rygel-lms-category-container.vala b/src/plugins/lms/rygel-lms-category-container.vala
1179 new file mode 100644
1180 index 0000000..e5430d1
1182 +++ b/src/plugins/lms/rygel-lms-category-container.vala
1185 + * Copyright (C) 2009,2010 Jens Georg <mail@jensge.org>,
1186 + * (C) 2013 Intel Corporation.
1188 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
1190 + * This file is part of Rygel.
1192 + * Rygel is free software; you can redistribute it and/or modify
1193 + * it under the terms of the GNU Lesser General Public License as published by
1194 + * the Free Software Foundation; either version 2 of the License, or
1195 + * (at your option) any later version.
1197 + * Rygel is distributed in the hope that it will be useful,
1198 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1199 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1200 + * GNU Lesser General Public License for more details.
1202 + * You should have received a copy of the GNU Lesser General Public License
1203 + * along with this program; if not, write to the Free Software Foundation,
1204 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1211 +public errordomain Rygel.LMS.CategoryContainerError {
1215 + UNSUPPORTED_SEARCH
1218 +public abstract class Rygel.LMS.CategoryContainer : Rygel.MediaContainer,
1219 + Rygel.TrackableContainer,
1220 + Rygel.SearchableContainer {
1221 + public ArrayList<string> search_classes { get; set; }
1223 + public unowned LMS.Database lms_db { get; construct; }
1225 + public string db_id { get; construct; }
1227 + public string sql_all { get; construct; }
1228 + public string sql_find_object { get; construct; }
1229 + public string sql_count { get; construct; }
1230 + public string sql_added { get; construct; }
1231 + public string sql_removed { get; construct; }
1233 + protected Statement stmt_all;
1234 + protected Statement stmt_find_object;
1235 + protected Statement stmt_added;
1236 + protected Statement stmt_removed;
1238 + protected string child_prefix;
1239 + protected string ref_prefix;
1241 + protected abstract MediaObject? object_from_statement (Statement statement);
1243 + /* TODO these should be abstract */
1244 + protected virtual string get_sql_all_with_filter (string filter) {
1245 + return this.sql_all;
1247 + protected virtual string get_sql_count_with_filter (string filter) {
1248 + return this.sql_count;
1251 + private static string? map_operand_to_column (string operand,
1252 + out string? collate = null,
1253 + bool for_sort = false)
1255 + string column = null;
1256 + bool use_collation = false;
1258 + // TODO add all used aliases to sql queries
1259 + switch (operand) {
1262 + use_collation = true;
1264 + case "upnp:artist":
1265 + column = "artist";
1266 + use_collation = true;
1268 + case "dc:creator":
1269 + column = "creator";
1270 + use_collation = true;
1273 + var message = "Unsupported column %s".printf (operand);
1275 + throw new CategoryContainerError.UNSUPPORTED_SEARCH (message);
1278 + if (use_collation) {
1279 + collate = "COLLATE CASEFOLD";
1287 + private static string? relational_expression_to_sql
1288 + (RelationalExpression exp,
1289 + GLib.ValueArray args)
1291 + GLib.Value? v = null;
1292 + string collate = null;
1294 + string column = CategoryContainer.map_operand_to_column (exp.operand1,
1296 + SqlOperator operator;
1299 + case GUPnP.SearchCriteriaOp.EXISTS:
1300 + string sql_function;
1301 + if (exp.operand2 == "true") {
1302 + sql_function = "%s IS NOT NULL AND %s != ''";
1304 + sql_function = "%s IS NULL OR %s = ''";
1307 + return sql_function.printf (column, column);
1308 + case GUPnP.SearchCriteriaOp.EQ:
1309 + case GUPnP.SearchCriteriaOp.NEQ:
1310 + case GUPnP.SearchCriteriaOp.LESS:
1311 + case GUPnP.SearchCriteriaOp.LEQ:
1312 + case GUPnP.SearchCriteriaOp.GREATER:
1313 + case GUPnP.SearchCriteriaOp.GEQ:
1315 + operator = new SqlOperator.from_search_criteria_op
1316 + (exp.op, column, collate);
1318 + case GUPnP.SearchCriteriaOp.CONTAINS:
1319 + operator = new SqlFunction ("contains", column);
1322 + case GUPnP.SearchCriteriaOp.DOES_NOT_CONTAIN:
1323 + operator = new SqlFunction ("NOT contains", column);
1326 + case GUPnP.SearchCriteriaOp.DERIVED_FROM:
1327 + operator = new SqlOperator ("LIKE", column);
1328 + v = "%s%%".printf (exp.operand2);
1331 + warning ("Unsupported op %d", exp.op);
1339 + return operator.to_string ();
1342 + private static string logical_expression_to_sql
1343 + (LogicalExpression expression,
1344 + GLib.ValueArray args)
1346 + string left_sql_string = CategoryContainer.search_expression_to_sql
1347 + (expression.operand1,
1349 + string right_sql_string = CategoryContainer.search_expression_to_sql
1350 + (expression.operand2,
1352 + unowned string operator_sql_string = "OR";
1354 + if (expression.op == LogicalOperator.AND) {
1355 + operator_sql_string = "AND";
1358 + return "(%s %s %s)".printf (left_sql_string,
1359 + operator_sql_string,
1360 + right_sql_string);
1363 + private static string? search_expression_to_sql
1364 + (SearchExpression? expression,
1365 + GLib.ValueArray args)
1367 + if (expression == null) {
1371 + if (expression is LogicalExpression) {
1372 + return CategoryContainer.logical_expression_to_sql
1373 + (expression as LogicalExpression, args);
1375 + return CategoryContainer.relational_expression_to_sql
1376 + (expression as RelationalExpression,
1381 + protected virtual uint get_child_count_with_filter (string where_filter,
1384 + var query = this.get_sql_count_with_filter (where_filter);
1386 + var stmt = this.lms_db.prepare_and_init (query, args.values);
1387 + if (stmt.step () != Sqlite.ROW) {
1390 + return stmt.column_int (0);
1391 + } catch (DatabaseError e) {
1392 + warning ("Query failed: %s", e.message);
1397 + protected virtual MediaObjects? get_children_with_filter (string where_filter,
1399 + string sort_criteria,
1402 + var children = new MediaObjects ();
1403 + GLib.Value v = max_count;
1408 + var query = this.get_sql_all_with_filter (where_filter);
1410 + var stmt = this.lms_db.prepare_and_init (query, args.values);
1411 + while (Database.get_children_step (stmt)) {
1412 + children.add (this.object_from_statement (stmt));
1414 + } catch (DatabaseError e) {
1415 + warning ("Query failed: %s", e.message);
1421 + public async MediaObjects? search (SearchExpression? expression,
1424 + out uint total_matches,
1425 + string sort_criteria,
1426 + Cancellable? cancellable)
1428 + debug ("search()");
1430 + var args = new GLib.ValueArray (0);
1431 + var filter = CategoryContainer.search_expression_to_sql (expression,
1433 + total_matches = this.get_child_count_with_filter (filter, args);
1435 + if (expression != null) {
1436 + debug (" Original search: %s", expression.to_string ());
1437 + debug (" Parsed search expression: %s", filter);
1438 + debug (" Filtered cild count is %u", total_matches);
1441 + if (max_count == 0) {
1442 + max_count = uint.MAX;
1444 + return this.get_children_with_filter (filter,
1449 + } catch (Error e) {
1450 + debug (" Falling back to simple_search(): %s", e.message);
1451 + return yield this.simple_search (expression,
1454 + out total_matches,
1460 + public async override MediaObjects? get_children (uint offset,
1462 + string sort_criteria,
1463 + Cancellable? cancellable)
1465 + MediaObjects retval = new MediaObjects ();
1467 + Database.get_children_init (this.stmt_all,
1471 + while (Database.get_children_step (this.stmt_all)) {
1472 + retval.add (this.object_from_statement (this.stmt_all));
1478 + public async override MediaObject? find_object (string id,
1479 + Cancellable? cancellable)
1481 + if (!id.has_prefix (this.child_prefix)) {
1482 + /* can't match anything in this container */
1486 + MediaObject object = null;
1488 + /* remove parent section from id */
1489 + var real_id = id.substring (this.child_prefix.length);
1490 + /* remove grandchildren from id */
1491 + var index = real_id.index_of_char (':');
1493 + real_id = real_id.slice (0, index);
1497 + Database.find_object (real_id, this.stmt_find_object);
1498 + var child = this.object_from_statement (this.stmt_find_object);
1502 + /* try grandchildren */
1503 + var container = child as CategoryContainer;
1504 + object = yield container.find_object (id, cancellable);
1506 + /* tell object to keep a reference to the parent --
1507 + * otherwise parent is freed before object is serialized */
1508 + object.parent_ref = object.parent;
1510 + } catch (DatabaseError e) {
1511 + debug ("find_object %s in %s: %s", id, this.id, e.message);
1512 + /* Happens e.g. if id is not an integer */
1518 + protected string build_child_id (int db_id) {
1519 + return "%s%d".printf (this.child_prefix, db_id);
1522 + protected string build_reference_id (int db_id) {
1523 + return "%s%d".printf (this.ref_prefix, db_id);
1526 + protected async void add_child (MediaObject object) {
1529 + protected async void remove_child (MediaObject object) {
1532 + private void on_db_updated(uint64 old_id, uint64 new_id) {
1534 + var stmt_count = this.lms_db.prepare (this.sql_count);
1536 + if (stmt_count.step () == Sqlite.ROW) {
1537 + this.child_count = stmt_count.column_int (0);
1540 + Database.get_children_with_update_id_init (this.stmt_added,
1543 + while (Database.get_children_step (this.stmt_added)) {
1544 + this.add_child_tracked.begin(this.object_from_statement (this.stmt_added));
1547 + Database.get_children_with_update_id_init (this.stmt_removed,
1550 + while (Database.get_children_step (this.stmt_removed)) {
1551 + this.remove_child_tracked.begin(this.object_from_statement (this.stmt_removed));
1554 + } catch (DatabaseError e) {
1555 + warning ("Can't perform container update: %s", e.message);
1560 + public CategoryContainer (string db_id,
1561 + MediaContainer parent,
1563 + LMS.Database lms_db,
1565 + string sql_find_object,
1567 + string? sql_added,
1568 + string? sql_removed
1570 + Object (id : "%s:%s".printf (parent.id, db_id),
1575 + sql_all : sql_all,
1576 + sql_find_object : sql_find_object,
1577 + sql_count : sql_count,
1578 + sql_added : sql_added,
1579 + sql_removed: sql_removed
1584 + this.search_classes = new ArrayList<string> ();
1586 + this.child_prefix = "%s:".printf (this.id);
1588 + var index = this.id.index_of_char (':');
1589 + this.ref_prefix = this.id.slice (0, index) + ":all:";
1592 + this.stmt_all = this.lms_db.prepare (this.sql_all);
1593 + this.stmt_find_object = this.lms_db.prepare (this.sql_find_object);
1594 + var stmt_count = this.lms_db.prepare (this.sql_count);
1596 + if (stmt_count.step () == Sqlite.ROW) {
1597 + this.child_count = stmt_count.column_int (0);
1599 + // some container implementations don't have a reasonable way to provide
1600 + // id-based statements to fetch added or removed items
1601 + if (this.sql_added != null && this.sql_removed != null) {
1602 + this.stmt_added = this.lms_db.prepare (this.sql_added);
1603 + this.stmt_removed = this.lms_db.prepare (this.sql_removed);
1604 + lms_db.db_updated.connect(this.on_db_updated);
1606 + } catch (DatabaseError e) {
1607 + warning ("Container %s: %s", this.title, e.message);
1612 diff --git a/src/plugins/lms/rygel-lms-collate.c b/src/plugins/lms/rygel-lms-collate.c
1613 new file mode 100644
1614 index 0000000..8eee80b
1616 +++ b/src/plugins/lms/rygel-lms-collate.c
1619 + * Copyright (C) 2012 Jens Georg <mail@jensge.org>.
1621 + * Author: Jens Georg <mail@jensge.org>
1623 + * This file is part of Rygel.
1625 + * Rygel is free software; you can redistribute it and/or modify
1626 + * it under the terms of the GNU Lesser General Public License as published by
1627 + * the Free Software Foundation; either version 2 of the License, or
1628 + * (at your option) any later version.
1630 + * Rygel is distributed in the hope that it will be useful,
1631 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1632 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1633 + * GNU Lesser General Public License for more details.
1635 + * You should have received a copy of the GNU Lesser General Public License
1636 + * along with this program; if not, write to the Free Software Foundation,
1637 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1642 +#ifdef HAVE_UNISTRING
1643 +# include <unistr.h>
1646 +gint rygel_lms_utf8_collate_str (const char *a, gsize alen,
1647 + const char *b, gsize blen)
1649 + char *a_str, *b_str;
1652 + /* Make sure the passed strings are null terminated */
1653 + a_str = g_strndup (a, alen);
1654 + b_str = g_strndup (b, blen);
1656 +#ifdef HAVE_UNISTRING
1657 + result = u8_strcoll (a_str, b_str);
1659 + return g_utf8_collate (a_str, b_str);
1667 diff --git a/src/plugins/lms/rygel-lms-database.vala b/src/plugins/lms/rygel-lms-database.vala
1668 new file mode 100644
1669 index 0000000..e898d66
1671 +++ b/src/plugins/lms/rygel-lms-database.vala
1674 + * Copyright (C) 2009,2011 Jens Georg <mail@jensge.org>,
1675 + * (C) 2013 Intel Corporation.
1677 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
1679 + * This file is part of Rygel.
1681 + * Rygel is free software; you can redistribute it and/or modify
1682 + * it under the terms of the GNU Lesser General Public License as published by
1683 + * the Free Software Foundation; either version 2 of the License, or
1684 + * (at your option) any later version.
1686 + * Rygel is distributed in the hope that it will be useful,
1687 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1688 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1689 + * GNU Lesser General Public License for more details.
1691 + * You should have received a copy of the GNU Lesser General Public License
1692 + * along with this program; if not, write to the Free Software Foundation,
1693 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1700 +public errordomain Rygel.LMS.DatabaseError {
1708 +namespace Rygel.LMS {
1709 + extern static int utf8_collate_str (uint8[] a, uint8[] b);
1712 +public class Rygel.LMS.Database {
1714 + public signal void db_updated(uint64 old_update_id, uint64 new_update_id);
1716 + private Sqlite.Database db;
1717 + private LMS.DBus lms_proxy;
1718 + private uint64 update_id;
1721 + * Function to implement the custom SQL function 'contains'
1723 + private static void utf8_contains (Sqlite.Context context,
1724 + Sqlite.Value[] args)
1725 + requires (args.length == 2) {
1726 + if (args[0].to_text () == null ||
1727 + args[1].to_text () == null) {
1728 + context.result_int (0);
1733 + var pattern = Regex.escape_string (args[1].to_text ());
1734 + if (Regex.match_simple (pattern,
1735 + args[0].to_text (),
1736 + RegexCompileFlags.CASELESS)) {
1737 + context.result_int (1);
1739 + context.result_int (0);
1744 + * Function to implement the custom SQLite collation 'CASEFOLD'.
1746 + * Uses utf8 case-fold to compare the strings.
1748 + private static int utf8_collate (int alen, void* a, int blen, void* b) {
1749 + // unowned to prevent array copy
1750 + unowned uint8[] _a = (uint8[]) a;
1753 + unowned uint8[] _b = (uint8[]) b;
1756 + return LMS.utf8_collate_str (_a, _b);
1759 + public Database () throws DatabaseError {
1762 + lms_proxy = Bus.get_proxy_sync (BusType.SESSION,
1763 + "org.lightmediascanner",
1764 + "/org/lightmediascanner/Scanner1");
1765 + db_path = lms_proxy.data_base_path;
1766 + debug ("Got db path %s from LMS over dbus", db_path);
1767 + update_id = lms_proxy.update_id;
1768 + debug ("Got updated id %lld from LMS over dbus", update_id);
1769 + lms_proxy.g_properties_changed.connect (this.on_lms_properties_changed);
1771 + } catch (IOError e) {
1772 + warning("Couldn't get LMS Dbus proxy: %s", e.message);
1773 + db_path = Environment.get_user_config_dir() +
1774 + "/lightmediascannerd/db.sqlite3";
1775 + debug ("Using default sqlite database location %s", db_path);
1778 + Sqlite.Database.open (db_path, out this.db);
1779 + if (this.db.errcode () != Sqlite.OK) {
1780 + throw new DatabaseError.OPEN ("Failed to open '%s': %d",
1782 + this.db.errcode () );
1785 + this.db.create_function ("contains",
1789 + LMS.Database.utf8_contains,
1793 + this.db.create_collation ("CASEFOLD",
1795 + LMS.Database.utf8_collate);
1799 + private void on_lms_properties_changed (DBusProxy lms_proxy,
1801 + string[] invalidated) {
1802 + if (!changed.get_type().equal (VariantType.VARDICT)) {
1806 + foreach (var changed_prop in changed) {
1807 + var key = (string) changed_prop.get_child_value (0);
1808 + var value = changed_prop.get_child_value (1).get_child_value (0);
1810 + debug ("LMS property %s changed value to %s", key, value.print(true));
1814 + db_updated(update_id, (uint64)value);
1815 + update_id = (uint64)value;
1822 + public Statement prepare (string query_string) throws DatabaseError {
1823 + Statement statement;
1825 + var err = this.db.prepare_v2 (query_string, -1, out statement);
1826 + if (err != Sqlite.OK)
1827 + throw new DatabaseError.PREPARE ("Unable to create statement '%s': %d",
1834 + public Statement prepare_and_init (string query,
1835 + GLib.Value[]? arguments)
1836 + throws DatabaseError {
1838 + Statement statement;
1840 + var err = this.db.prepare_v2 (query, -1, out statement);
1841 + if (err != Sqlite.OK)
1842 + throw new DatabaseError.PREPARE ("Unable to create statement '%s': %d",
1846 + for (var i = 1; i <= arguments.length; ++i) {
1848 + unowned GLib.Value current_value = arguments[i - 1];
1850 + if (current_value.holds (typeof (int))) {
1851 + sqlite_err = statement.bind_int (i, current_value.get_int ());
1852 + if (sqlite_err != Sqlite.OK)
1853 + throw new DatabaseError.BIND("Unable to bind value %d",
1855 + } else if (current_value.holds (typeof (int64))) {
1856 + sqlite_err = statement.bind_int64 (i, current_value.get_int64 ());
1857 + if (sqlite_err != Sqlite.OK)
1858 + throw new DatabaseError.BIND("Unable to bind value %d",
1860 + } else if (current_value.holds (typeof (uint64))) {
1861 + sqlite_err = statement.bind_int64 (i, (int64) current_value.get_uint64 ());
1862 + if (sqlite_err != Sqlite.OK)
1863 + throw new DatabaseError.BIND("Unable to bind value %d",
1865 + } else if (current_value.holds (typeof (long))) {
1866 + sqlite_err = statement.bind_int64 (i, current_value.get_long ());
1867 + if (sqlite_err != Sqlite.OK)
1868 + throw new DatabaseError.BIND("Unable to bind value %d",
1870 + } else if (current_value.holds (typeof (uint))) {
1871 + sqlite_err = statement.bind_int64 (i, current_value.get_uint ());
1872 + if (sqlite_err != Sqlite.OK)
1873 + throw new DatabaseError.BIND("Unable to bind value %d",
1875 + } else if (current_value.holds (typeof (string))) {
1876 + sqlite_err = statement.bind_text (i, current_value.get_string ());
1877 + if (sqlite_err != Sqlite.OK)
1878 + throw new DatabaseError.BIND("Unable to bind value %d",
1880 + } else if (current_value.holds (typeof (void *))) {
1881 + if (current_value.peek_pointer () == null) {
1882 + sqlite_err = statement.bind_null (i);
1883 + if (sqlite_err != Sqlite.OK)
1884 + throw new DatabaseError.BIND("Unable to bind value %d",
1887 + assert_not_reached ();
1890 + var type = current_value.type ();
1891 + warning (_("Unsupported type %s"), type.name ());
1892 + assert_not_reached ();
1899 + public static void find_object(string id, Statement stmt) throws DatabaseError {
1901 + (void) stmt.reset();
1903 + int integer_id = int.parse(id);
1904 + int sqlite_err = stmt.bind_int(1, integer_id);
1905 + if (sqlite_err != Sqlite.OK)
1906 + throw new DatabaseError.BIND("Unable to bind id %d", sqlite_err);
1908 + sqlite_err = stmt.step();
1909 + if (sqlite_err != Sqlite.ROW)
1910 + throw new DatabaseError.STEP("Unable to find id %s", id);
1913 + public static void get_children_init (Statement stmt,
1914 + uint offset, uint max_count, string sort_criteria) throws DatabaseError {
1918 + (void) stmt.reset();
1920 + sqlite_err = stmt.bind_int(1, (int) max_count);
1921 + if (sqlite_err != Sqlite.OK)
1922 + throw new DatabaseError.BIND("Unable to bind max_count %d",
1925 + sqlite_err = stmt.bind_int(2, (int) offset);
1926 + if (sqlite_err != Sqlite.OK)
1927 + throw new DatabaseError.BIND("Unable to bind offset %d",
1931 + public static void get_children_with_update_id_init (Statement stmt,
1932 + uint64 old_id, uint64 new_id) throws DatabaseError {
1936 + (void) stmt.reset();
1938 + if (new_id < old_id) // id value wrapped over
1939 + sqlite_err = stmt.bind_int64(1, 0);
1941 + sqlite_err = stmt.bind_int64(1, (int64)old_id);
1942 + if (sqlite_err != Sqlite.OK)
1943 + throw new DatabaseError.BIND("Unable to bind old_id %d",
1946 + sqlite_err = stmt.bind_int64(2, (int64)new_id);
1947 + if (sqlite_err != Sqlite.OK)
1948 + throw new DatabaseError.BIND("Unable to bind new_id %d",
1952 + public static bool get_children_step(Statement stmt) throws DatabaseError {
1957 + sqlite_err = stmt.step();
1958 + retval = sqlite_err == Sqlite.ROW;
1960 + if (!retval && (sqlite_err != Sqlite.DONE))
1961 + throw new DatabaseError.STEP("Error iterating through rows %d",
1967 diff --git a/src/plugins/lms/rygel-lms-dbus-interfaces.vala b/src/plugins/lms/rygel-lms-dbus-interfaces.vala
1968 new file mode 100644
1969 index 0000000..13f00cb
1971 +++ b/src/plugins/lms/rygel-lms-dbus-interfaces.vala
1974 + * Copyright (C) 2014 Intel Corporation.
1976 + * Author: Alexander Kanavin <alex.kanavin@gmail.com>
1978 + * This file is part of Rygel.
1980 + * Rygel is free software; you can redistribute it and/or modify
1981 + * it under the terms of the GNU Lesser General Public License as published by
1982 + * the Free Software Foundation; either version 2 of the License, or
1983 + * (at your option) any later version.
1985 + * Rygel is distributed in the hope that it will be useful,
1986 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1987 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1988 + * GNU Lesser General Public License for more details.
1990 + * You should have received a copy of the GNU Lesser General Public License
1991 + * along with this program; if not, write to the Free Software Foundation,
1992 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1995 +[DBus (name = "org.lightmediascanner.Scanner1")]
1996 +interface Rygel.LMS.DBus : DBusProxy {
1997 + public abstract string data_base_path { owned get; }
1998 + [DBus (name = "UpdateID")]
1999 + public abstract uint64 update_id { get; }
2001 + //TODO: add all the other API items which are currently unused
2003 \ No newline at end of file
2004 diff --git a/src/plugins/lms/rygel-lms-image-root.vala b/src/plugins/lms/rygel-lms-image-root.vala
2005 new file mode 100644
2006 index 0000000..466bbe2
2008 +++ b/src/plugins/lms/rygel-lms-image-root.vala
2011 + * Copyright (C) 2013 Intel Corporation.
2013 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2015 + * This file is part of Rygel.
2017 + * Rygel is free software; you can redistribute it and/or modify
2018 + * it under the terms of the GNU Lesser General Public License as published by
2019 + * the Free Software Foundation; either version 2 of the License, or
2020 + * (at your option) any later version.
2022 + * Rygel is distributed in the hope that it will be useful,
2023 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2024 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2025 + * GNU Lesser General Public License for more details.
2027 + * You should have received a copy of the GNU Lesser General Public License
2028 + * along with this program; if not, write to the Free Software Foundation,
2029 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2034 +public class Rygel.LMS.ImageRoot : Rygel.SimpleContainer {
2035 + public ImageRoot (string id,
2036 + MediaContainer parent,
2038 + LMS.Database lms_db) {
2039 + base (id, parent, title);
2041 + this.add_child_container (new AllImages (this, lms_db));
2042 + this.add_child_container (new ImageYears (this, lms_db));
2045 diff --git a/src/plugins/lms/rygel-lms-image-year.vala b/src/plugins/lms/rygel-lms-image-year.vala
2046 new file mode 100644
2047 index 0000000..a7768f0
2049 +++ b/src/plugins/lms/rygel-lms-image-year.vala
2052 + * Copyright (C) 2013 Intel Corporation.
2054 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2056 + * This file is part of Rygel.
2058 + * Rygel is free software; you can redistribute it and/or modify
2059 + * it under the terms of the GNU Lesser General Public License as published by
2060 + * the Free Software Foundation; either version 2 of the License, or
2061 + * (at your option) any later version.
2063 + * Rygel is distributed in the hope that it will be useful,
2064 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2065 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2066 + * GNU Lesser General Public License for more details.
2068 + * You should have received a copy of the GNU Lesser General Public License
2069 + * along with this program; if not, write to the Free Software Foundation,
2070 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2076 +public class Rygel.LMS.ImageYear : Rygel.LMS.CategoryContainer {
2077 + private static const string SQL_ALL_TEMPLATE =
2078 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime, strftime('%Y', date, 'unixepoch') as year " +
2079 + "FROM images, files " +
2080 + "WHERE dtime = 0 AND images.id = files.id AND year = '%s' " +
2081 + "LIMIT ? OFFSET ?;";
2083 + private static const string SQL_COUNT_TEMPLATE =
2084 + "SELECT count(images.id), strftime('%Y', date, 'unixepoch') as year " +
2085 + "FROM images, files " +
2086 + "WHERE dtime = 0 AND images.id = files.id AND year = '%s';";
2088 + private static const string SQL_FIND_OBJECT_TEMPLATE =
2089 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime, strftime('%Y', date, 'unixepoch') as year " +
2090 + "FROM images, files " +
2091 + "WHERE dtime = 0 AND files.id = ? AND images.id = files.id AND year = '%s';";
2093 + private static const string SQL_ADDED_TEMPLATE =
2094 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime, strftime('%Y', date, 'unixepoch') as year " +
2095 + "FROM images, files " +
2096 + "WHERE dtime = 0 AND images.id = files.id AND year = '%s' " +
2097 + "AND update_id > ? AND update_id <= ?;";
2099 + private static const string SQL_REMOVED_TEMPLATE =
2100 + "SELECT images.id, title, artist, date, width, height, path, size, dlna_profile, dlna_mime, strftime('%Y', date, 'unixepoch') as year " +
2101 + "FROM images, files " +
2102 + "WHERE dtime <> 0 AND images.id = files.id AND year = '%s' " +
2103 + "AND update_id > ? AND update_id <= ?;";
2105 + protected override MediaObject? object_from_statement (Statement statement) {
2106 + var id = statement.column_int(0);
2107 + var path = statement.column_text(6);
2108 + var mime_type = statement.column_text(9);
2110 + if (mime_type == null || mime_type.length == 0){
2111 + /* TODO is this correct? */
2112 + debug ("Image item %d (%s) has no MIME type",
2117 + var title = statement.column_text(1);
2118 + var image = new ImageItem(this.build_child_id (id), this, title);
2119 + image.ref_id = this.build_reference_id (id);
2120 + image.creator = statement.column_text(2);
2121 + TimeVal tv = { (long) statement.column_int(3), (long) 0 };
2122 + image.date = tv.to_iso8601 ();
2123 + image.width = statement.column_int(4);
2124 + image.height = statement.column_int(5);
2125 + image.size = statement.column_int(7);
2126 + image.mime_type = mime_type;
2127 + image.dlna_profile = statement.column_text(8);
2128 + File file = File.new_for_path(path);
2129 + image.add_uri (file.get_uri ());
2134 + private static string get_sql_all (string year) {
2135 + return (SQL_ALL_TEMPLATE.printf (year));
2137 + private static string get_sql_find_object (string year) {
2138 + return (SQL_FIND_OBJECT_TEMPLATE.printf (year));
2140 + private static string get_sql_count (string year) {
2141 + return (SQL_COUNT_TEMPLATE.printf (year));
2143 + private static string get_sql_added (string year) {
2144 + return (SQL_ADDED_TEMPLATE.printf (year));
2146 + private static string get_sql_removed (string year) {
2147 + return (SQL_REMOVED_TEMPLATE.printf (year));
2150 + public ImageYear (MediaContainer parent,
2152 + LMS.Database lms_db) {
2153 + base ("%s".printf (year),
2157 + get_sql_all (year),
2158 + get_sql_find_object (year),
2159 + get_sql_count (year),
2160 + get_sql_added (year),
2161 + get_sql_removed (year)
2165 diff --git a/src/plugins/lms/rygel-lms-image-years.vala b/src/plugins/lms/rygel-lms-image-years.vala
2166 new file mode 100644
2167 index 0000000..636f4d1
2169 +++ b/src/plugins/lms/rygel-lms-image-years.vala
2172 + * Copyright (C) 2013 Intel Corporation.
2174 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2176 + * This file is part of Rygel.
2178 + * Rygel is free software; you can redistribute it and/or modify
2179 + * it under the terms of the GNU Lesser General Public License as published by
2180 + * the Free Software Foundation; either version 2 of the License, or
2181 + * (at your option) any later version.
2183 + * Rygel is distributed in the hope that it will be useful,
2184 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2185 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2186 + * GNU Lesser General Public License for more details.
2188 + * You should have received a copy of the GNU Lesser General Public License
2189 + * along with this program; if not, write to the Free Software Foundation,
2190 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2196 +public class Rygel.LMS.ImageYears : Rygel.LMS.CategoryContainer {
2197 + private static const string SQL_ALL =
2198 + "SELECT DISTINCT(strftime('%Y', images.date, 'unixepoch')) as year " +
2200 + "LIMIT ? OFFSET ?;";
2202 + private static const string SQL_COUNT =
2203 + "SELECT COUNT(DISTINCT(strftime('%Y', images.date, 'unixepoch'))) " +
2206 + /* actually returns multiple times the same result (because no DISTINCT) */
2207 + /* Casting the year is a workaround so we can keep using
2208 + * Database.find_object() without making the argument a variant or something like it*/
2209 + private static const string SQL_FIND_OBJECT =
2210 + "SELECT strftime('%Y', images.date, 'unixepoch') as year " +
2212 + "WHERE year = CAST(? AS TEXT)";
2214 + protected override MediaObject? object_from_statement (Statement statement) {
2215 + return new LMS.ImageYear (this, statement.column_text (0), this.lms_db);
2218 + public ImageYears (MediaContainer parent, LMS.Database lms_db) {
2223 + ImageYears.SQL_ALL,
2224 + ImageYears.SQL_FIND_OBJECT,
2225 + ImageYears.SQL_COUNT,
2230 diff --git a/src/plugins/lms/rygel-lms-music-root.vala b/src/plugins/lms/rygel-lms-music-root.vala
2231 new file mode 100644
2232 index 0000000..7b1eb0f
2234 +++ b/src/plugins/lms/rygel-lms-music-root.vala
2237 + * Copyright (C) 2013 Intel Corporation.
2239 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2241 + * This file is part of Rygel.
2243 + * Rygel is free software; you can redistribute it and/or modify
2244 + * it under the terms of the GNU Lesser General Public License as published by
2245 + * the Free Software Foundation; either version 2 of the License, or
2246 + * (at your option) any later version.
2248 + * Rygel is distributed in the hope that it will be useful,
2249 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2250 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2251 + * GNU Lesser General Public License for more details.
2253 + * You should have received a copy of the GNU Lesser General Public License
2254 + * along with this program; if not, write to the Free Software Foundation,
2255 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2260 +public class Rygel.LMS.MusicRoot : Rygel.SimpleContainer {
2261 + public MusicRoot (string id,
2262 + MediaContainer parent,
2264 + LMS.Database lms_db) {
2265 + base (id, parent, title);
2267 + this.add_child_container (new AllMusic (this, lms_db));
2268 + this.add_child_container (new Artists ("artists", this, _("Artists"), lms_db));
2269 + this.add_child_container (new Albums (this, lms_db));
2272 diff --git a/src/plugins/lms/rygel-lms-plugin-factory.vala b/src/plugins/lms/rygel-lms-plugin-factory.vala
2273 new file mode 100644
2274 index 0000000..9fa8ccd
2276 +++ b/src/plugins/lms/rygel-lms-plugin-factory.vala
2279 + * Copyright (C) 2013 Intel Corporation.
2281 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2283 + * This file is part of Rygel.
2285 + * Rygel is free software; you can redistribute it and/or modify
2286 + * it under the terms of the GNU Lesser General Public License as published by
2287 + * the Free Software Foundation; either version 2 of the License, or
2288 + * (at your option) any later version.
2290 + * Rygel is distributed in the hope that it will be useful,
2291 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2292 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2293 + * GNU Lesser General Public License for more details.
2295 + * You should have received a copy of the GNU Lesser General Public License
2296 + * along with this program; if not, write to the Free Software Foundation,
2297 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2302 +private Rygel.LMS.PluginFactory plugin_factory;
2304 +public void module_init(PluginLoader loader) {
2305 + plugin_factory = new Rygel.LMS.PluginFactory(loader);
2308 +public class Rygel.LMS.PluginFactory {
2310 + PluginLoader loader;
2312 + public PluginFactory(PluginLoader loader) {
2313 + this.loader = loader;
2314 + this.loader.add_plugin(new LMS.Plugin());
2318 diff --git a/src/plugins/lms/rygel-lms-plugin.vala b/src/plugins/lms/rygel-lms-plugin.vala
2319 new file mode 100644
2320 index 0000000..8bf1284
2322 +++ b/src/plugins/lms/rygel-lms-plugin.vala
2325 + * Copyright (C) 2013 Intel Corporation.
2327 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2329 + * This file is part of Rygel.
2331 + * Rygel is free software; you can redistribute it and/or modify
2332 + * it under the terms of the GNU Lesser General Public License as published by
2333 + * the Free Software Foundation; either version 2 of the License, or
2334 + * (at your option) any later version.
2336 + * Rygel is distributed in the hope that it will be useful,
2337 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2338 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2339 + * GNU Lesser General Public License for more details.
2341 + * You should have received a copy of the GNU Lesser General Public License
2342 + * along with this program; if not, write to the Free Software Foundation,
2343 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2348 +public class Rygel.LMS.Plugin : Rygel.MediaServerPlugin {
2349 + public const string NAME = "LMS";
2351 + private static RootContainer root;
2355 + root = new RootContainer();
2356 + base(root, Plugin.NAME, null, PluginCapabilities.TRACK_CHANGES);
2359 diff --git a/src/plugins/lms/rygel-lms-root-container.vala b/src/plugins/lms/rygel-lms-root-container.vala
2360 new file mode 100644
2361 index 0000000..1623fa3
2363 +++ b/src/plugins/lms/rygel-lms-root-container.vala
2366 + * Copyright (C) 2013 Intel Corporation.
2368 + * Author: Jussi Kukkonen <jussi.kukkonen@intel.com>
2370 + * This file is part of Rygel.
2372 + * Rygel is free software; you can redistribute it and/or modify
2373 + * it under the terms of the GNU Lesser General Public License as published by
2374 + * the Free Software Foundation; either version 2 of the License, or
2375 + * (at your option) any later version.
2377 + * Rygel is distributed in the hope that it will be useful,
2378 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2379 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2380 + * GNU Lesser General Public License for more details.
2382 + * You should have received a copy of the GNU Lesser General Public License
2383 + * along with this program; if not, write to the Free Software Foundation,
2384 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2391 +public class Rygel.LMS.RootContainer : Rygel.SimpleContainer {
2393 + private LMS.Database lms_db = null;
2395 + public RootContainer() {
2396 + var config = MetaConfig.get_default ();
2398 + var title = _("Shared media");
2400 + title = config.get_string ("LightMediaScanner", "title");
2401 + } catch (GLib.Error error) {}
2406 + this.lms_db = new LMS.Database ();
2408 + this.add_child_container (new MusicRoot ("music", this, _("Music"), this.lms_db));
2409 + this.add_child_container (new AllVideos ("all-videos", this, _("Videos"), this.lms_db));
2410 + this.add_child_container (new ImageRoot ("images", this, _("Pictures"), this.lms_db));
2412 + } catch (DatabaseError e) {
2413 + warning ("%s\n", e.message);
2415 + /* TODO if db does not exist we should
2416 + wait for it to be created and then add folders. Best to wait for the
2417 + LMS notification API. */
2423 diff --git a/src/plugins/lms/rygel-lms-sql-function.vala b/src/plugins/lms/rygel-lms-sql-function.vala
2424 new file mode 100644
2425 index 0000000..e8580cc
2427 +++ b/src/plugins/lms/rygel-lms-sql-function.vala
2430 + * Copyright (C) 2010 Jens Georg <mail@jensge.org>.
2432 + * Author: Jens Georg <mail@jensge.org>
2434 + * This file is part of Rygel.
2436 + * Rygel is free software; you can redistribute it and/or modify
2437 + * it under the terms of the GNU Lesser General Public License as published by
2438 + * the Free Software Foundation; either version 2 of the License, or
2439 + * (at your option) any later version.
2441 + * Rygel is distributed in the hope that it will be useful,
2442 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2443 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2444 + * GNU Lesser General Public License for more details.
2446 + * You should have received a copy of the GNU Lesser General Public License
2447 + * along with this program; if not, write to the Free Software Foundation,
2448 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2451 +internal class Rygel.LMS.SqlFunction : SqlOperator {
2452 + public SqlFunction (string name, string arg) {
2456 + public override string to_string () {
2457 + return "%s(%s,?)".printf (name, arg);
2460 diff --git a/src/plugins/lms/rygel-lms-sql-operator.vala b/src/plugins/lms/rygel-lms-sql-operator.vala
2461 new file mode 100644
2462 index 0000000..fc4e907
2464 +++ b/src/plugins/lms/rygel-lms-sql-operator.vala
2467 + * Copyright (C) 2010 Jens Georg <mail@jensge.org>.
2469 + * Author: Jens Georg <mail@jensge.org>
2471 + * This file is part of Rygel.
2473 + * Rygel is free software; you can redistribute it and/or modify
2474 + * it under the terms of the GNU Lesser General Public License as published by
2475 + * the Free Software Foundation; either version 2 of the License, or
2476 + * (at your option) any later version.
2478 + * Rygel is distributed in the hope that it will be useful,
2479 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2480 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2481 + * GNU Lesser General Public License for more details.
2483 + * You should have received a copy of the GNU Lesser General Public License
2484 + * along with this program; if not, write to the Free Software Foundation,
2485 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2490 +internal class Rygel.LMS.SqlOperator : GLib.Object {
2491 + protected string name;
2492 + protected string arg;
2493 + protected string collate;
2495 + public SqlOperator (string name,
2497 + string collate = "") {
2500 + this.collate = collate;
2503 + public SqlOperator.from_search_criteria_op (SearchCriteriaOp op,
2506 + string sql = null;
2508 + case SearchCriteriaOp.EQ:
2511 + case SearchCriteriaOp.NEQ:
2514 + case SearchCriteriaOp.LESS:
2517 + case SearchCriteriaOp.LEQ:
2520 + case SearchCriteriaOp.GREATER:
2523 + case SearchCriteriaOp.GEQ:
2527 + assert_not_reached ();
2530 + this (sql, arg, collate);
2533 + public virtual string to_string () {
2534 + return "(%s %s ? %s)".printf (arg, name, collate);