[rygel] core: Add album art as a special type of thumbnail
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] core: Add album art as a special type of thumbnail
- Date: Mon, 19 Jul 2010 17:50:06 +0000 (UTC)
commit 81baade3ecc2b38daf8df9452c2bd075a567f016
Author: Jens Georg <mail jensge org>
Date: Thu May 20 20:31:24 2010 +0200
core: Add album art as a special type of thumbnail
src/rygel/Makefile.am | 2 +
src/rygel/rygel-album-art.vala | 37 ++++++
src/rygel/rygel-http-get-handler.vala | 10 +-
src/rygel/rygel-media-art-store.vala | 229 +++++++++++++++++++++++++++++++++
src/rygel/rygel-media-item.vala | 21 +++
src/rygel/rygel-thumbnail.vala | 4 +-
6 files changed, 297 insertions(+), 6 deletions(-)
---
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index 956d2e1..c700e68 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -74,6 +74,8 @@ VAPI_SOURCE_FILES = rygel-configuration.vala \
rygel-media-item.vala \
rygel-thumbnail.vala \
rygel-thumbnailer.vala \
+ rygel-album-art.vala \
+ rygel-media-art-store.vala \
rygel-subtitle.vala \
rygel-subtitle-manager.vala \
rygel-browse.vala \
diff --git a/src/rygel/rygel-album-art.vala b/src/rygel/rygel-album-art.vala
new file mode 100644
index 0000000..d72882c
--- /dev/null
+++ b/src/rygel/rygel-album-art.vala
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using GUPnP;
+
+public class Rygel.AlbumArt : Thumbnail {
+ public AlbumArt() {
+ base ();
+ }
+
+ internal override DIDLLiteResource? add_resource (DIDLLiteItem didl_item,
+ string protocol) {
+ Xml.Node* node = didl_item.xml_node;
+ node->new_child (null, "upnp:albumArtURI", this.uri);
+
+ return null;
+ }
+}
diff --git a/src/rygel/rygel-http-get-handler.vala b/src/rygel/rygel-http-get-handler.vala
index 21caf69..f23ab17 100644
--- a/src/rygel/rygel-http-get-handler.vala
+++ b/src/rygel/rygel-http-get-handler.vala
@@ -48,11 +48,13 @@ internal abstract class Rygel.HTTPGetHandler: GLib.Object {
var didl_item = didl_writer.add_item ();
try {
var resource = this.add_resource (didl_item, request);
- var tokens = resource.protocol_info.to_string ().split (":", 4);
- assert (tokens.length == 4);
+ if (resource != null) {
+ var tokens = resource.protocol_info.to_string ().split (":", 4);
+ assert (tokens.length == 4);
- request.msg.response_headers.append ("contentFeatures.dlna.org",
- tokens[3]);
+ request.msg.response_headers.append ("contentFeatures.dlna.org",
+ tokens[3]);
+ }
} catch (Error err) {
warning ("Received request for 'contentFeatures.dlna.org' but " +
"failed to provide the value in response headers");
diff --git a/src/rygel/rygel-media-art-store.vala b/src/rygel/rygel-media-art-store.vala
new file mode 100644
index 0000000..bb5e322
--- /dev/null
+++ b/src/rygel/rygel-media-art-store.vala
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+internal errordomain MediaArtStoreError {
+ NO_DIR,
+ NO_MEDIA_ART
+}
+
+public class Rygel.MediaArtStore : GLib.Object {
+ private const string PLACEHOLDER_HASH = "7215ee9c7d9dc229d2921a40e899ec5f";
+ private const string invalid_chars = "()[]<>{}_! #$^&*+=|\\/\"'?~";
+ private const string convert_chars = "\t";
+ private const string block_pattern = "%s[^%s]*%s";
+ private const string[] blocks = { "()", "{}", "[]", "<>" };
+ private static MediaArtStore media_art_store;
+ private static bool first_time = true;
+ private Regex char_remove_regex;
+ private Regex char_convert_regex;
+ private Regex space_compress_regex;
+ private Regex[] block_regexes;
+
+ private string directory;
+
+ private MediaArtStore () throws MediaArtStoreError {
+ var dir = Path.build_filename (Environment.get_user_cache_dir (),
+ "media-art");
+ var file = File.new_for_path (dir);
+
+ if (!file.query_exists (null)) {
+ var message = _("Failed to find media-art directory");
+
+ throw new MediaArtStoreError.NO_DIR (message);
+ }
+
+ this.directory = dir;
+ try {
+ var regex_string = Regex.escape_string (invalid_chars);
+ char_remove_regex = new Regex ("[%s]".printf (regex_string));
+ regex_string = Regex.escape_string (convert_chars);
+ char_convert_regex = new Regex ("[%s]".printf (regex_string));
+ space_compress_regex = new Regex ("\\s+");
+ block_regexes = new Regex[0];
+
+ foreach (var block in blocks) {
+ var block_re = block_pattern.printf (
+ Regex.escape_string ("%C".printf (block[0])),
+ Regex.escape_string ("%C".printf (block[1])),
+ Regex.escape_string ("%C".printf (block[1])));
+ block_regexes += new Regex (block_re);
+ }
+ } catch (RegexError error) {
+ assert_not_reached ();
+ }
+ }
+
+ public static MediaArtStore? get_default () {
+ if (first_time) {
+ try {
+ media_art_store = new MediaArtStore ();
+ } catch (MediaArtStoreError error) {
+ warning (_("No media art available: %s"), error.message);
+ }
+ }
+
+ first_time = false;
+
+ return media_art_store;
+ }
+
+ public Thumbnail? find_media_art_any (MediaItem item) throws Error {
+ var thumb = find_media_art (item);
+ if (thumb == null) {
+ thumb = find_media_art (item, true);
+ }
+
+ return thumb;
+ }
+
+ public File get_media_art_file (string type,
+ MediaItem item,
+ bool simple = false) {
+ string hash;
+
+ if (simple) {
+ hash = get_simple_hash (type, item);
+ } else {
+ hash = get_hash (type, item);
+ }
+ var file_path = "%s-%s.jpeg".printf (type, hash);
+
+ var path = Path.build_filename (this.directory, file_path);
+
+ return File.new_for_path (path);
+ }
+
+ private string normalize_and_hash (string? input, bool utf8_only = true) {
+ string normalized = " ";
+ if (input != null && input != "") {
+ if (utf8_only) {
+ normalized = input;
+ } else {
+ normalized = albumart_strip_invalid_entities (input);
+ }
+ normalized = normalized.normalize (-1, NormalizeMode.ALL);
+ }
+
+ return Checksum.compute_for_string (ChecksumType.MD5,
+ normalized.down ());
+ }
+
+ private string get_simple_hash (string type, MediaItem item) {
+ string hash;
+ switch (type) {
+ case "artist":
+ case "radio":
+ hash = normalize_and_hash (item.author);
+ break;
+ case "podcast":
+ hash = normalize_and_hash (item.title);
+ break;
+ case "album":
+ hash = normalize_and_hash (item.author + "\t" +
+ item.album);
+ break;
+ case "track":
+ hash = normalize_and_hash (item.author + "\t" +
+ item.album + "\t" +
+ item.title);
+ break;
+ default:
+ assert_not_reached ();
+ }
+
+ return hash;
+ }
+
+ private string get_hash (string type, MediaItem item) {
+ string b = null, c = null;
+ switch (type) {
+ case "track":
+ b = normalize_and_hash (item.author, false) + "-" +
+ normalize_and_hash (item.album, false);
+ c = normalize_and_hash (item.title, false);
+ break;
+ case "album":
+ case "artist":
+ b = normalize_and_hash (item.author, false);
+ c = normalize_and_hash (item.album, false);
+ break;
+ case "radio":
+ case "podcast":
+ b = normalize_and_hash (item.title, false);
+ c = PLACEHOLDER_HASH;
+ break;
+ }
+
+ return "%s-%s".printf (b, c);
+ }
+
+ public Thumbnail? find_media_art (MediaItem item,
+ bool simple = false) throws Error {
+ string[] types = { "track", "album", "artist", "podcast", "radio" };
+ File file = null;
+
+ foreach (var type in types) {
+ file = get_media_art_file (type, item, simple);
+ if (file.query_exists (null)) {
+ break;
+ } else {
+ file = null;
+ }
+ }
+
+ if (file == null) {
+ return null;
+ }
+
+ var info = file.query_info (FILE_ATTRIBUTE_ACCESS_CAN_READ,
+ FileQueryInfoFlags.NONE,
+ null);
+ if (!info.get_attribute_boolean (FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
+ return null;
+ }
+
+ var thumb = new AlbumArt ();
+ thumb.uri = file.get_uri ();
+
+ return thumb;
+ }
+
+ string albumart_strip_invalid_entities (string original) {
+ string p;
+
+ p = original;
+
+ try {
+ foreach (var re in block_regexes) {
+ p = re.replace_literal (p, -1, 0, "");
+ }
+
+ p = char_remove_regex.replace (p, -1, 0, "");
+ p = char_convert_regex.replace (p, -1, 0, " ");
+ p = space_compress_regex.replace (p, -1, 0, " ");
+
+ return p;
+ } catch (RegexError error) {
+ assert_not_reached ();
+ }
+ }
+}
diff --git a/src/rygel/rygel-media-item.vala b/src/rygel/rygel-media-item.vala
index 34c95df..e7ada12 100644
--- a/src/rygel/rygel-media-item.vala
+++ b/src/rygel/rygel-media-item.vala
@@ -152,6 +152,27 @@ public class Rygel.MediaItem : MediaObject {
}
}
+ public void lookup_album_art () {
+ if (!this.upnp_class.has_prefix (MediaItem.AUDIO_CLASS)) {
+ return;
+ }
+
+
+ if (!(this.thumbnails.size > 0 && this.thumbnails[0] is AlbumArt)) {
+ var media_art_store = MediaArtStore.get_default ();
+ if (media_art_store == null) {
+ return;
+ }
+
+ try {
+ var thumb = media_art_store.find_media_art_any (this);
+ if (thumb != null) {
+ this.thumbnails.insert (0, thumb);
+ }
+ } catch (Error err) {};
+ }
+ }
+
internal int compare_transcoders (void *a, void *b) {
var transcoder1 = (Transcoder) a;
var transcoder2 = (Transcoder) b;
diff --git a/src/rygel/rygel-thumbnail.vala b/src/rygel/rygel-thumbnail.vala
index da308e4..c83af73 100644
--- a/src/rygel/rygel-thumbnail.vala
+++ b/src/rygel/rygel-thumbnail.vala
@@ -35,8 +35,8 @@ public class Rygel.Thumbnail : Rygel.IconInfo {
this.dlna_profile = dlna_profile;
}
- internal DIDLLiteResource add_resource (DIDLLiteItem didl_item,
- string protocol) {
+ internal virtual DIDLLiteResource? add_resource (DIDLLiteItem didl_item,
+ string protocol) {
var res = didl_item.add_resource ();
res.uri = this.uri;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]