summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Ryan <mark.d.ryan@intel.com>2013-06-17 14:05:14 +0200
committerLudovic Ferrandis <ludovic.ferrandis@intel.com>2013-06-24 11:11:22 +0200
commit2beeb7b4ba8b91a255de6213d980ef71824446f6 (patch)
tree609e6a8330b29933a684068ac1cb89b750d2c67d
parent961414d2e3e5545e6469427e46ace381748d22fe (diff)
downloaddleyna-server-2beeb7b4ba8b91a255de6213d980ef71824446f6.tar.gz
Added the new TypeEx property and fixed Type
The Type property is now almost consistent with the MediaServer2Spec Type property. This should remove a source of constant confusion and fix a number of bugs, in for example the download sync controller. dLeyna-server's implementation of Type differs only from MediaServer2Spec in one way. dLeyna-server has introduced one new value, item.unclassified, which is used when the object in question is an item but not one of the items supported by MediaServer2Spec, i.e., not an audio, video or image item. A new property, TypeEx has been introduced. TypeEx contains the extended type information for an object, i.e., the value Type used to hold (althought the values have changed), so if applications want the extended type they can still retrieve it. Both Type and TypeEx are searchable. The superset of values specified by TypeEx are permitted when creating a container, both in the type of the container and it its create classes. The extended type is exposed in the CreateClasses of a container and in the LastChange event. Type is no longer permitted in Update. TypeEx must be used. From an API point of view there are two breakagaes: 1. Applications that used Type to retrieve extended type information, need to be updated to use TypeEx. Applications that used Type in their search queries might also need to be updated. 2. Applications that updated the Type of an object will need to be updated to use TypeEx instead. There is one slightly unpleasent hack required to report the correct values for Search and Sort Caps. A correct solution would require making the p_map hash table many to many, which would result in a bigger change. I've left this improvement for another commit. Signed-off-by: Mark Ryan <mark.d.ryan@intel.com>
-rw-r--r--doc/server/dbus/API.txt97
-rw-r--r--libdleyna/server/device.c36
-rw-r--r--libdleyna/server/interface.h1
-rw-r--r--libdleyna/server/props.c270
-rw-r--r--libdleyna/server/props.h5
-rw-r--r--libdleyna/server/search.c13
-rw-r--r--libdleyna/server/server.c2
7 files changed, 252 insertions, 172 deletions
diff --git a/doc/server/dbus/API.txt b/doc/server/dbus/API.txt
index dfb05ed..3f121b1 100644
--- a/doc/server/dbus/API.txt
+++ b/doc/server/dbus/API.txt
@@ -262,13 +262,13 @@ not necessarily finished. There are two return values. The UploadId,
which can be used to monitor the progress of the upload and to
cancel it and ObjectPath, which contains the path of the newly created object.
-CreateContainerInAnyContainer(s DisplayName, s Type, as ChildTypes)
+CreateContainerInAnyContainer(s DisplayName, s TypeEx, as ChildTypes)
-> o ObjectPath
Creates a new container. The DMS chooses the best place to create
this new container. DisplayName is the name of the new container,
-Type is the type and ChildTypes is an array of types that can be stored
-in the new folder, e.g., ['video','container'].
+TypeEx is the extended type and ChildTypes is an array of extended types that
+can be stored in the new folder, e.g., ['video','container'].
A special value of ['*'] indicates that no restriction is placed
on the types of objects that can be stored in the container.
The path of the newly created object is returned.
@@ -361,7 +361,7 @@ The value of each dictionary entry depends on the key value.
| o | M | Contains the Object Path property of the parent container to which |
| | | this object was added. |
|------------------------------------------------------------------------------|
-| s | M | Contains the value of the Type (converted upnp:class property) |
+| s | M | Contains the value of the TypeEx (converted upnp:class property) |
| | | of the object that was added. |
|------------------------------------------------------------------------------|
@@ -446,18 +446,33 @@ additional values for the Type property.
Additional Values for the Type Property:
----------------------------------------
-The Type property tells what kind of object we are dealing with and
-which can take the below values in addition to those described
-in MediaServer2Spec specification (3).
-in case of Items:
- 'video.musicclip', 'video.broadcast'
- 'audio.broadcast', 'audio.book',
- 'item.playlist', 'item'
-and in case of Containers:
- 'album', 'album.music', 'album.photo'
- 'person', 'person.musicartist'
- 'genre', 'genre.music', 'genre.movie'
- 'playlist', 'storage'
+The MediaServer2Spec type property can be set to one of only seven
+possible values: 'container', 'video', 'video.movie', 'audio',
+'music', 'image' or 'image.photo'. This causes a number of problems
+for dLeyna-server. First, there is only one value for container, so
+it is not possible to indicate if the container is an album or a
+playlist, for instance. Secondly, there is no way to represent an item
+that is not an audio, video or image item, such as a generic item
+(UPnP class of object.item) or a playlist item.
+
+dLeyna-server solves these two different problems in two ways:
+
+- It provides a new property called TypeEx, which provides extended
+ type information. TypeEx is a superset of Type, providing specific
+ values of object types that are not supported by Type. For example,
+ for an album, Type will be container but TypeEx will be set to
+ container.album.musicAlbum.
+
+- It provides a new catch all value for Type, item.unclassified.
+ item.unclassified means that the object is an item but it is not
+ an audio, video, or image item, e.g.,a generic item or a playlist item.
+ The actual type of the item is stored in TypeEx. item.unclassified
+ may be specified as a value for the Type property in searches. In such
+ cases it is treated as the UPnP type object.item.
+
+Adding an additional value to MediaServer2Spec's Type property breaks our
+compliance with this spec a little, but it is necessary to achieve UPnP
+certification.
Additional Properties:
----------------------
@@ -482,6 +497,25 @@ Additional Properties:
| | | | for the object relative to the |
| | | | SystemUpdateID state variable. |
|---------------------------------------------------------------------------|
+| TypeEx | s | m | The extended Type of the object. |
+| | | | TypeEx permits a superset of the values |
+| | | | supported by the Type property. When |
+| | | | the Type of an object is accurately |
+| | | | described by Type, i.e., the object |
+| | | | is a basic container or is an image, |
+| | | | audio, or video file, TypeEx = Type. |
+| | | | For objects where this is not the case, |
+| | | | e.g., an album, the TypeEx value is |
+| | | | formed by removing the |
+| | | | "object." the prefix from the upnp |
+| | | | class. For example, the TypeEx value |
+| | | | for the upnp class, |
+| | | | object.container.playlistContainer is |
+| | | | container.playlistContainer. |
+| | | | |
+| | | | Since version 0.2.0. |
+|---------------------------------------------------------------------------|
+
Additional Methods:
@@ -502,7 +536,7 @@ property name value pairs that are to be updated, or added if they do
not already exist. ToDelete is an array of properties to be deleted.
The same property cannot appear in both ToAddUpdate and ToDelete.
Only the following properties can be updated: 'Date', 'DisplayName',
-'Artists', 'Album', 'Type', 'TrackNumber'.
+'Artists', 'Album', 'TypeEx', 'TrackNumber'.
GetMetaData() -> s
@@ -548,12 +582,23 @@ Additional properties unsupported by org.gnome.MediaContainer2
|---------------------------------------------------------------------------|
| Name | Type | m/o* | Description |
|---------------------------------------------------------------------------|
-| CreateClasses |a(sb) | o | Array of UPnP createClasses |
-| | | | property. A createClasses |
-| | | | property is a tuple (sb) |
-| | | | representing the class and |
-| | | | whether derived values are |
-| | | | allowed for this class. |
+| CreateClasses |a(sb) | o | The CreateClasses property is an |
+| | | | array of tuples (sb) that lists |
+| | | | the extended types of objects that|
+| | | | that can be created in a |
+| | | | container. A boolean value is |
+| | | | associated with each type. This |
+| | | | boolean indicates whether objects |
+| | | | of types derived from the |
+| | | | specified extended type can also |
+| | | | be created in this container. |
+| | | | For example, |
+| | | | ("container.album.","true") |
+| | | | would indicate that objects of |
+| | | | type container.album, |
+| | | | container.album.music and |
+| | | | container.album.photo can be |
+| | | | created in the container. |
|---------------------------------------------------------------------------|
| ContainerUpdateID | u | o | Contains the value of the |
| | | | SystemUpdateID state variable |
@@ -607,7 +652,7 @@ SearchObjectsEx(s Query, u Offset, u Max, as Filter, s SortBy) -> aa{sv}
Upload(s DisplayName, s FilePath) -> (u UploadId, o ObjectPath)
-CreateContainer(s DisplayName, s Type, as ChildTypes) -> o ObjectPath
+CreateContainer(s DisplayName, s TypeEx, as ChildTypes) -> o ObjectPath
CreateReference(o ObjectPath) -> o ObjectPath
@@ -676,8 +721,8 @@ suitable location for the file.
The CreateContainer method creates a new child container.
DisplayName is the name of the new container,
-Type is the type and ChildTypes is an array of types that can be stored
-in the new folder, e.g., ['video','container'].
+TypeEx is the extended type and ChildTypes is an array of extended
+types that can be stored in the new folder, e.g., ['video','container'].
A special value of ['*'] indicates that no restriction is placed
on the types of objects that can be stored in the container.
The path of the newly created object is returned.
diff --git a/libdleyna/server/device.c b/libdleyna/server/device.c
index 1c3b6a4..36588b5 100644
--- a/libdleyna/server/device.c
+++ b/libdleyna/server/device.c
@@ -295,7 +295,7 @@ static void prv_last_change_decode(GUPnPCDSLastChangeEntry *entry,
if (!mclass)
goto on_error;
- media_class = dls_props_upnp_class_to_media_spec(mclass);
+ media_class = dls_props_upnp_class_to_media_spec_ex(mclass);
if (!media_class)
goto on_error;
@@ -769,9 +769,19 @@ static void prv_get_capabilities_analyze(GHashTable *property_map,
while (caps && *caps) {
prop_name = g_hash_table_lookup(property_map, *caps);
- if (prop_name)
+ if (prop_name) {
g_variant_builder_add(&caps_vb, "s", prop_name);
+ /* TODO: Okay this is not very nice. A better
+ way to fix this would be to change the p_map
+ to be many : many. */
+
+ if (!strcmp(*caps, "upnp:class"))
+ g_variant_builder_add(
+ &caps_vb, "s",
+ DLS_INTERFACE_PROP_TYPE_EX);
+ }
+
caps++;
}
@@ -2858,11 +2868,11 @@ static gchar *prv_create_new_container_didl(const gchar *parent_id,
GUPnPDIDLLiteContainer *container;
GVariantIter iter;
GVariant *child_type;
- const gchar *actual_type;
+ gchar *actual_type;
GUPnPOCMFlags flags;
gchar *retval = NULL;
- actual_type = dls_props_media_spec_to_upnp_class(
+ actual_type = dls_props_media_spec_ex_to_upnp_class(
task->ut.create_container.type);
if (!actual_type)
goto on_error;
@@ -2878,6 +2888,7 @@ static gchar *prv_create_new_container_didl(const gchar *parent_id,
task->ut.create_container.display_name);
gupnp_didl_lite_object_set_parent_id(item, parent_id);
gupnp_didl_lite_object_set_upnp_class(item, actual_type);
+ g_free(actual_type);
gupnp_didl_lite_object_set_restricted(item, FALSE);
flags = GUPNP_OCM_FLAGS_UPLOAD |
@@ -2890,11 +2901,13 @@ static gchar *prv_create_new_container_didl(const gchar *parent_id,
g_variant_iter_init(&iter, task->ut.create_container.child_types);
while ((child_type = g_variant_iter_next_value(&iter))) {
- actual_type = dls_props_media_spec_to_upnp_class(
+ actual_type = dls_props_media_spec_ex_to_upnp_class(
g_variant_get_string(child_type, NULL));
- if (actual_type != NULL)
+ if (actual_type != NULL) {
gupnp_didl_lite_container_add_create_class(container,
actual_type);
+ g_free(actual_type);
+ }
g_variant_unref(child_type);
}
@@ -3785,7 +3798,7 @@ static gchar *prv_get_current_xml_fragment(GUPnPDIDLLiteObject *object,
retval = gupnp_didl_lite_object_get_album_xml_string(object);
else if (mask & DLS_UPNP_MASK_PROP_DATE)
retval = gupnp_didl_lite_object_get_date_xml_string(object);
- else if (mask & DLS_UPNP_MASK_PROP_TYPE)
+ else if (mask & DLS_UPNP_MASK_PROP_TYPE_EX)
retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
object);
else if (mask & DLS_UPNP_MASK_PROP_TRACK_NUMBER)
@@ -3803,7 +3816,7 @@ static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
{
GUPnPDIDLLiteContributor *artist;
const gchar *artist_name;
- const gchar *upnp_class;
+ gchar *upnp_class;
GVariantIter viter;
gchar *retval = NULL;
@@ -3825,13 +3838,14 @@ static gchar *prv_get_new_xml_fragment(GUPnPDIDLLiteObject *object,
g_variant_get_string(value, NULL));
retval = gupnp_didl_lite_object_get_date_xml_string(object);
- } else if (mask & DLS_UPNP_MASK_PROP_TYPE) {
- upnp_class = dls_props_media_spec_to_upnp_class(
- g_variant_get_string(value, NULL));
+ } else if (mask & DLS_UPNP_MASK_PROP_TYPE_EX) {
+ upnp_class = dls_props_media_spec_ex_to_upnp_class(
+ g_variant_get_string(value, NULL));
if (!upnp_class)
goto on_error;
gupnp_didl_lite_object_set_upnp_class(object, upnp_class);
+ g_free(upnp_class);
retval = gupnp_didl_lite_object_get_upnp_class_xml_string(
object);
diff --git a/libdleyna/server/interface.h b/libdleyna/server/interface.h
index 14a214b..be3b125 100644
--- a/libdleyna/server/interface.h
+++ b/libdleyna/server/interface.h
@@ -43,6 +43,7 @@ enum dls_interface_type_ {
#define DLS_INTERFACE_PROP_RESTRICTED "Restricted"
#define DLS_INTERFACE_PROP_DISPLAY_NAME "DisplayName"
#define DLS_INTERFACE_PROP_TYPE "Type"
+#define DLS_INTERFACE_PROP_TYPE_EX "TypeEx"
#define DLS_INTERFACE_PROP_CREATOR "Creator"
#define DLS_INTERFACE_PROP_DLNA_MANAGED "DLNAManaged"
#define DLS_INTERFACE_PROP_OBJECT_UPDATE_ID "ObjectUpdateID"
diff --git a/libdleyna/server/props.c b/libdleyna/server/props.c
index ad36174..cd6c30a 100644
--- a/libdleyna/server/props.c
+++ b/libdleyna/server/props.c
@@ -32,80 +32,46 @@
#include "path.h"
#include "props.h"
+static const gchar gUPnPObject[] = "object";
static const gchar gUPnPContainer[] = "object.container";
-static const gchar gUPnPAlbum[] = "object.container.album";
-static const gchar gUPnPPerson[] = "object.container.person";
-static const gchar gUPnPGenre[] = "object.container.genre";
-static const gchar gUPnPPlaylist[] = "object.container.playlistContainer";
-static const gchar gUPnPStorage[] = "object.container.storageFolder";
static const gchar gUPnPAudioItem[] = "object.item.audioItem";
static const gchar gUPnPVideoItem[] = "object.item.videoItem";
static const gchar gUPnPImageItem[] = "object.item.imageItem";
-static const gchar gUPnPPlaylistItem[] = "object.item.playlistItem";
static const gchar gUPnPItem[] = "object.item";
+static const unsigned int gUPnPObjectLen =
+ (sizeof(gUPnPObject) / sizeof(gchar)) - 1;
static const unsigned int gUPnPContainerLen =
(sizeof(gUPnPContainer) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPAlbumLen =
- (sizeof(gUPnPAlbum) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPPersonLen =
- (sizeof(gUPnPPerson) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPGenreLen =
- (sizeof(gUPnPGenre) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPPlaylistLen =
- (sizeof(gUPnPPlaylist) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPStorageLen =
- (sizeof(gUPnPStorage) / sizeof(gchar)) - 1;
static const unsigned int gUPnPAudioItemLen =
(sizeof(gUPnPAudioItem) / sizeof(gchar)) - 1;
static const unsigned int gUPnPVideoItemLen =
(sizeof(gUPnPVideoItem) / sizeof(gchar)) - 1;
static const unsigned int gUPnPImageItemLen =
(sizeof(gUPnPImageItem) / sizeof(gchar)) - 1;
-static const unsigned int gUPnPPlaylistItemLen =
- (sizeof(gUPnPPlaylistItem) / sizeof(gchar)) - 1;
static const unsigned int gUPnPItemLen =
(sizeof(gUPnPItem) / sizeof(gchar)) - 1;
-static const gchar gUPnPPhotoAlbum[] = "object.container.album.photoAlbum";
-static const gchar gUPnPMusicAlbum[] = "object.container.album.musicAlbum";
-static const gchar gUPnPMusicArtist[] = "object.container.person.musicArtist";
-static const gchar gUPnPMovieGenre[] = "object.container.genre.movieGenre";
-static const gchar gUPnPMusicGenre[] = "object.container.genre.musicGenre";
static const gchar gUPnPMusicTrack[] = "object.item.audioItem.musicTrack";
-static const gchar gUPnPAudioBroadcast[] =
- "object.item.audioItem.audioBroadcast";
-static const gchar gUPnPAudioBook[] = "object.item.audioItem.audioBook";
static const gchar gUPnPMovie[] = "object.item.videoItem.movie";
-static const gchar gUPnPMusicVideoClip[] =
- "object.item.videoItem.musicVideoClip";
-static const gchar gUPnPVideoBroadcast[] =
- "object.item.videoItem.videoBroadcast";
static const gchar gUPnPPhoto[] = "object.item.imageItem.photo";
static const gchar gMediaSpec2Container[] = "container";
-static const gchar gMediaSpec2Album[] = "album";
-static const gchar gMediaSpec2AlbumPhoto[] = "album.photo";
-static const gchar gMediaSpec2AlbumMusic[] = "album.music";
-static const gchar gMediaSpec2Person[] = "person";
-static const gchar gMediaSpec2PersonMusicArtist[] = "person.musicartist";
-static const gchar gMediaSpec2Genre[] = "genre";
-static const gchar gMediaSpec2GenreMovie[] = "genre.movie";
-static const gchar gMediaSpec2GenreMusic[] = "genre.music";
-static const gchar gMediaSpec2AudioMusic[] = "audio.music";
-static const gchar gMediaSpec2AudioBroadcast[] = "audio.broadcast";
+static const gchar gMediaSpec2AudioMusic[] = "music";
static const gchar gMediaSpec2Audio[] = "audio";
-static const gchar gMediaSpec2AudioBook[] = "audio.book";
static const gchar gMediaSpec2Video[] = "video";
static const gchar gMediaSpec2VideoMovie[] = "video.movie";
-static const gchar gMediaSpec2VideoMusicClip[] = "video.musicclip";
-static const gchar gMediaSpec2VideoBroadcast[] = "video.broadcast";
static const gchar gMediaSpec2Image[] = "image";
static const gchar gMediaSpec2ImagePhoto[] = "image.photo";
-static const gchar gMediaSpec2Playlist[] = "playlist";
-static const gchar gMediaSpec2PlaylistItem[] = "item.playlist";
-static const gchar gMediaSpec2Item[] = "item";
-static const gchar gMediaSpec2Storage[] = "storage";
+static const gchar gMediaSpec2ItemUnclassified[] = "item.unclassified";
+
+static const gchar gMediaSpec2ExItem[] = "item";
+
+static const unsigned int gMediaSpec2ExItemLen =
+ (sizeof(gMediaSpec2ExItem) / sizeof(gchar)) - 1;
+
+static const unsigned int gMediaSpec2ContainerLen =
+ (sizeof(gMediaSpec2Container) / sizeof(gchar)) - 1;
typedef struct dls_prop_dlna_t_ dls_prop_dlna_t;
struct dls_prop_dlna_t_ {
@@ -396,10 +362,15 @@ void dls_prop_maps_new(GHashTable **property_map, GHashTable **filter_map)
/* upnp:class */
prop_t = prv_prop_map_new("upnp:class",
DLS_UPNP_MASK_PROP_TYPE,
- FALSE, TRUE, TRUE);
+ FALSE, TRUE, FALSE);
g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE, prop_t);
g_hash_table_insert(p_map, "upnp:class", DLS_INTERFACE_PROP_TYPE);
+ prop_t = prv_prop_map_new("upnp:class",
+ DLS_UPNP_MASK_PROP_TYPE_EX,
+ FALSE, TRUE, TRUE);
+ g_hash_table_insert(f_map, DLS_INTERFACE_PROP_TYPE_EX, prop_t);
+
/* upnp:containerUpdateID */
prop_t = prv_prop_map_new("upnp:containerUpdateID",
DLS_UPNP_MASK_PROP_CONTAINER_UPDATE_ID,
@@ -1147,7 +1118,7 @@ static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
create_class = ptr->data;
content = gupnp_didl_lite_create_class_get_content(
create_class);
- ms2_class = dls_props_upnp_class_to_media_spec(content);
+ ms2_class = dls_props_upnp_class_to_media_spec_ex(content);
inc_derived = gupnp_didl_lite_create_class_get_include_derived(
create_class);
g_variant_builder_add(&create_classes_vb,
@@ -1160,66 +1131,75 @@ static GVariant *prv_compute_create_classes(GUPnPDIDLLiteContainer *container)
return g_variant_builder_end(&create_classes_vb);
}
-const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
+static const gchar *prv_media_spec_to_upnp_class(const gchar *m2spec_class)
{
const gchar *retval = NULL;
- if (!m2spec_class)
- goto on_error;
-
- if (!strcmp(m2spec_class, gMediaSpec2AlbumPhoto))
- retval = gUPnPPhotoAlbum;
- else if (!strcmp(m2spec_class, gMediaSpec2AlbumMusic))
- retval = gUPnPMusicAlbum;
- else if (!strcmp(m2spec_class, gMediaSpec2Album))
- retval = gUPnPAlbum;
- else if (!strcmp(m2spec_class, gMediaSpec2PersonMusicArtist))
- retval = gUPnPMusicArtist;
- else if (!strcmp(m2spec_class, gMediaSpec2Person))
- retval = gUPnPPerson;
- else if (!strcmp(m2spec_class, gMediaSpec2GenreMovie))
- retval = gUPnPMovieGenre;
- else if (!strcmp(m2spec_class, gMediaSpec2GenreMusic))
- retval = gUPnPMusicGenre;
- else if (!strcmp(m2spec_class, gMediaSpec2Genre))
- retval = gUPnPGenre;
- else if (!strcmp(m2spec_class, gMediaSpec2Container))
+ if (!strcmp(m2spec_class, gMediaSpec2Container))
retval = gUPnPContainer;
else if (!strcmp(m2spec_class, gMediaSpec2AudioMusic))
retval = gUPnPMusicTrack;
- else if (!strcmp(m2spec_class, gMediaSpec2AudioBroadcast))
- retval = gUPnPAudioBroadcast;
- else if (!strcmp(m2spec_class, gMediaSpec2AudioBook))
- retval = gUPnPAudioBook;
else if (!strcmp(m2spec_class, gMediaSpec2Audio))
retval = gUPnPAudioItem;
else if (!strcmp(m2spec_class, gMediaSpec2VideoMovie))
retval = gUPnPMovie;
- else if (!strcmp(m2spec_class, gMediaSpec2VideoMusicClip))
- retval = gUPnPMusicVideoClip;
- else if (!strcmp(m2spec_class, gMediaSpec2VideoBroadcast))
- retval = gUPnPVideoBroadcast;
else if (!strcmp(m2spec_class, gMediaSpec2Video))
retval = gUPnPVideoItem;
else if (!strcmp(m2spec_class, gMediaSpec2ImagePhoto))
retval = gUPnPPhoto;
else if (!strcmp(m2spec_class, gMediaSpec2Image))
retval = gUPnPImageItem;
- else if (!strcmp(m2spec_class, gMediaSpec2Playlist))
- retval = gUPnPPlaylist;
- else if (!strcmp(m2spec_class, gMediaSpec2PlaylistItem))
- retval = gUPnPPlaylistItem;
- else if (!strcmp(m2spec_class, gMediaSpec2Item))
+
+ return retval;
+}
+
+const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class)
+{
+ const gchar *retval = NULL;
+
+ if (!m2spec_class)
+ goto on_error;
+
+ retval = prv_media_spec_to_upnp_class(m2spec_class);
+
+ if (!retval && !strcmp(m2spec_class, gMediaSpec2ItemUnclassified))
retval = gUPnPItem;
- else if (!strcmp(m2spec_class, gMediaSpec2Storage))
- retval = gUPnPStorage;
on_error:
return retval;
}
-const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
+gchar *dls_props_media_spec_ex_to_upnp_class(const gchar *m2spec_class)
+{
+ gchar *retval = NULL;
+ const gchar *basic_type;
+ const gchar *ptr = NULL;
+
+ if (!m2spec_class)
+ goto on_error;
+
+ basic_type = prv_media_spec_to_upnp_class(m2spec_class);
+ if (basic_type) {
+ retval = g_strdup(basic_type);
+ } else {
+ if (!strncmp(m2spec_class, gMediaSpec2ExItem,
+ gMediaSpec2ExItemLen))
+ ptr = m2spec_class + gMediaSpec2ExItemLen;
+ else if (!strncmp(m2spec_class, gMediaSpec2Container,
+ gMediaSpec2ContainerLen))
+ ptr = m2spec_class + gMediaSpec2ContainerLen;
+ if (ptr && (!*ptr || *ptr == '.'))
+ retval = g_strdup_printf("object.%s", m2spec_class);
+ }
+
+on_error:
+
+ return retval;
+}
+
+static const gchar *prv_upnp_class_to_media_spec(const gchar *upnp_class,
+ gboolean *exact)
{
const gchar *retval = NULL;
const gchar *ptr;
@@ -1227,71 +1207,73 @@ const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
if (!upnp_class)
goto on_error;
- if (!strncmp(upnp_class, gUPnPAlbum, gUPnPAlbumLen)) {
- ptr = upnp_class + gUPnPAlbumLen;
- if (!strcmp(ptr, ".photoAlbum"))
- retval = gMediaSpec2AlbumPhoto;
- else if (!strcmp(ptr, ".musicAlbum"))
- retval = gMediaSpec2AlbumMusic;
- else
- retval = gMediaSpec2Album;
- } else if (!strncmp(upnp_class, gUPnPPerson, gUPnPPersonLen)) {
- ptr = upnp_class + gUPnPPersonLen;
- if (!strcmp(ptr, ".musicArtist"))
- retval = gMediaSpec2PersonMusicArtist;
- else
- retval = gMediaSpec2Person;
- } else if (!strncmp(upnp_class, gUPnPGenre, gUPnPGenreLen)) {
- ptr = upnp_class + gUPnPGenreLen;
- if (!strcmp(ptr, ".movieGenre"))
- retval = gMediaSpec2GenreMovie;
- else if (!strcmp(ptr, ".musicGenre"))
- retval = gMediaSpec2GenreMusic;
- else
- retval = gMediaSpec2Genre;
- } else if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
+ if (!strncmp(upnp_class, gUPnPContainer, gUPnPContainerLen)) {
ptr = upnp_class + gUPnPContainerLen;
- if (!*ptr || *ptr == '.')
+ if (!*ptr || *ptr == '.') {
retval = gMediaSpec2Container;
+ *exact = *ptr == 0;
+ }
} else if (!strncmp(upnp_class, gUPnPAudioItem, gUPnPAudioItemLen)) {
ptr = upnp_class + gUPnPAudioItemLen;
- if (!strcmp(ptr, ".musicTrack"))
+ if (!strcmp(ptr, ".musicTrack")) {
retval = gMediaSpec2AudioMusic;
- else if (!strcmp(ptr, ".audioBroadcast"))
- retval = gMediaSpec2AudioBroadcast;
- else if (!strcmp(ptr, ".audioBook"))
- retval = gMediaSpec2AudioBook;
- else
+ *exact = TRUE;
+ } else if (!*ptr || *ptr == '.') {
retval = gMediaSpec2Audio;
+ *exact = *ptr == 0;
+ }
} else if (!strncmp(upnp_class, gUPnPVideoItem, gUPnPVideoItemLen)) {
ptr = upnp_class + gUPnPVideoItemLen;
- if (!strcmp(ptr, ".movie"))
+ if (!strcmp(ptr, ".movie")) {
retval = gMediaSpec2VideoMovie;
- else if (!strcmp(ptr, ".musicVideoClip"))
- retval = gMediaSpec2VideoMusicClip;
- else if (!strcmp(ptr, ".videoBroadcast"))
- retval = gMediaSpec2VideoBroadcast;
- else
+ *exact = TRUE;
+ } else if (!*ptr || *ptr == '.') {
retval = gMediaSpec2Video;
+ *exact = *ptr == 0;
+ }
} else if (!strncmp(upnp_class, gUPnPImageItem, gUPnPImageItemLen)) {
ptr = upnp_class + gUPnPImageItemLen;
- if (!strcmp(ptr, ".photo"))
+ if (!strcmp(ptr, ".photo")) {
retval = gMediaSpec2ImagePhoto;
- else
+ *exact = TRUE;
+ } else if (!*ptr || *ptr == '.') {
retval = gMediaSpec2Image;
- } else if (!strncmp(upnp_class, gUPnPPlaylist,
- gUPnPPlaylistLen)) {
- retval = gMediaSpec2Playlist;
- } else if (!strncmp(upnp_class, gUPnPPlaylistItem,
- gUPnPPlaylistItemLen)) {
- retval = gMediaSpec2PlaylistItem;
+ *exact = *ptr == 0;
+ }
} else if (!strncmp(upnp_class, gUPnPItem, gUPnPItemLen)) {
ptr = upnp_class + gUPnPItemLen;
- if (!*ptr || *ptr == '.')
- retval = gMediaSpec2Item;
- } else if (!strncmp(upnp_class, gUPnPStorage,
- gUPnPStorageLen)) {
- retval = gMediaSpec2Storage;
+ if (!*ptr || *ptr == '.') {
+ retval = gMediaSpec2ItemUnclassified;
+ *exact = *ptr == 0;
+ }
+ }
+
+on_error:
+
+ return retval;
+}
+
+const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class)
+{
+ gboolean exact;
+
+ return prv_upnp_class_to_media_spec(upnp_class, &exact);
+}
+
+const gchar *dls_props_upnp_class_to_media_spec_ex(const gchar *upnp_class)
+{
+ const gchar *retval;
+ gboolean exact;
+
+ retval = prv_upnp_class_to_media_spec(upnp_class, &exact);
+ if (!retval)
+ goto on_error;
+
+ if (exact) {
+ if (retval == gMediaSpec2ItemUnclassified)
+ retval = gMediaSpec2ExItem;
+ } else {
+ retval = upnp_class + gUPnPObjectLen + 1;
}
on_error:
@@ -1368,6 +1350,7 @@ gboolean dls_props_add_object(GVariantBuilder *item_vb,
const char *creator;
const char *upnp_class;
const char *media_spec_type;
+ const char *media_spec_type_ex;
gboolean retval = FALSE;
gboolean rest;
GUPnPOCMFlags flags;
@@ -1383,6 +1366,8 @@ gboolean dls_props_add_object(GVariantBuilder *item_vb,
if (!media_spec_type)
goto on_error;
+ media_spec_type_ex = dls_props_upnp_class_to_media_spec_ex(upnp_class);
+
title = gupnp_didl_lite_object_get_title(object);
creator = gupnp_didl_lite_object_get_creator(object);
rest = gupnp_didl_lite_object_get_restricted(object);
@@ -1407,6 +1392,10 @@ gboolean dls_props_add_object(GVariantBuilder *item_vb,
prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE,
media_spec_type);
+ if (filter_mask & DLS_UPNP_MASK_PROP_TYPE_EX)
+ prv_add_string_prop(item_vb, DLS_INTERFACE_PROP_TYPE_EX,
+ media_spec_type_ex);
+
if (filter_mask & DLS_UPNP_MASK_PROP_RESTRICTED)
prv_add_bool_prop(item_vb, DLS_INTERFACE_PROP_RESTRICTED, rest);
@@ -1785,6 +1774,17 @@ GVariant *dls_props_get_object_prop(const gchar *prop, const gchar *root_path,
retval = g_variant_ref_sink(g_variant_new_string(
media_spec_type));
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE_EX)) {
+ upnp_class = gupnp_didl_lite_object_get_upnp_class(object);
+ media_spec_type =
+ dls_props_upnp_class_to_media_spec_ex(upnp_class);
+ if (!media_spec_type)
+ goto on_error;
+
+ DLEYNA_LOG_DEBUG("Prop %s = %s", prop, media_spec_type);
+
+ retval = g_variant_ref_sink(g_variant_new_string(
+ media_spec_type));
} else if (!strcmp(prop, DLS_INTERFACE_PROP_DISPLAY_NAME)) {
title = gupnp_didl_lite_object_get_title(object);
if (!title)
diff --git a/libdleyna/server/props.h b/libdleyna/server/props.h
index f219573..c4628f5 100644
--- a/libdleyna/server/props.h
+++ b/libdleyna/server/props.h
@@ -64,6 +64,7 @@
#define DLS_UPNP_MASK_PROP_DLNA_CONVERSION (1LL << 35)
#define DLS_UPNP_MASK_PROP_DLNA_OPERATION (1LL << 36)
#define DLS_UPNP_MASK_PROP_DLNA_FLAGS (1LL << 37)
+#define DLS_UPNP_MASK_PROP_TYPE_EX (1LL << 38)
#define DLS_UPNP_MASK_ALL_PROPS 0xffffffffffffffff
@@ -134,6 +135,10 @@ GVariant *dls_props_get_item_prop(const gchar *prop, const gchar *root_path,
const gchar *dls_props_media_spec_to_upnp_class(const gchar *m2spec_class);
+gchar *dls_props_media_spec_ex_to_upnp_class(const gchar *m2spec_class);
+
const gchar *dls_props_upnp_class_to_media_spec(const gchar *upnp_class);
+const gchar *dls_props_upnp_class_to_media_spec_ex(const gchar *upnp_class);
+
#endif /* DLS_PROPS_H__ */
diff --git a/libdleyna/server/search.c b/libdleyna/server/search.c
index 264654e..c331ceb 100644
--- a/libdleyna/server/search.c
+++ b/libdleyna/server/search.c
@@ -37,6 +37,7 @@ gchar *dls_search_translate_search_string(GHashTable *filter_map,
gchar *op = NULL;
gchar *value = NULL;
const gchar *translated_value;
+ gchar *translated_type_ex;
dls_prop_map_t *prop_map;
GString *str;
gint start_pos;
@@ -80,6 +81,18 @@ gchar *dls_search_translate_search_string(GHashTable *filter_map,
goto on_error;
g_free(value);
value = g_strdup_printf("\"%s\"", translated_value);
+ } else if (!strcmp(prop, DLS_INTERFACE_PROP_TYPE_EX)) {
+ /* Skip the quotes */
+
+ value[strlen(value) - 1] = 0;
+ translated_type_ex =
+ dls_props_media_spec_ex_to_upnp_class(
+ value + 1);
+ if (!translated_type_ex)
+ goto on_error;
+ g_free(value);
+ value = g_strdup_printf("\"%s\"", translated_type_ex);
+ g_free(translated_type_ex);
} else if (!strcmp(prop, DLS_INTERFACE_PROP_PARENT) ||
!strcmp(prop, DLS_INTERFACE_PROP_PATH)) {
value[strlen(value) - 1] = 0;
diff --git a/libdleyna/server/server.c b/libdleyna/server/server.c
index 4db03e4..caa4701 100644
--- a/libdleyna/server/server.c
+++ b/libdleyna/server/server.c
@@ -120,6 +120,8 @@ static const gchar g_server_introspection[] =
" access='read'/>"
" <property type='s' name='"DLS_INTERFACE_PROP_TYPE"'"
" access='read'/>"
+ " <property type='s' name='"DLS_INTERFACE_PROP_TYPE_EX"'"
+ " access='read'/>"
" <property type='o' name='"DLS_INTERFACE_PROP_PATH"'"
" access='read'/>"
" <property type='s' name='"DLS_INTERFACE_PROP_DISPLAY_NAME"'"