1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/content_instance.h"
6 #include "content/content_filter.h"
8 #include <media_content.h>
9 #include <media_filter.h>
16 #include "common/picojson.h"
19 const std::string STR_FILTER("filter");
20 const std::string STR_CONTENT_URI("contentURI");
21 const std::string STR_EVENT_TYPE("eventType");
23 std::string createUriFromLocalPath(const std::string path) {
24 static std::string fileScheme("file://");
26 return fileScheme + path;
29 std::string getUriPath(const std::string uri) {
30 static std::string fileScheme("file://");
31 std::string _fileScheme = uri.substr(0, fileScheme.size());
33 if (_fileScheme == fileScheme)
34 return uri.substr(fileScheme.size());
40 unsigned ContentInstance::m_instanceCount = 0;
42 ContentInstance::ContentInstance() {
44 if (media_content_connect() != MEDIA_CONTENT_ERROR_NONE) {
45 std::cerr << "media_content_connect: DB connection error" << std::endl;
50 ContentInstance::~ContentInstance() {
51 assert(m_instanceCount > 0);
52 if (--m_instanceCount > 0)
54 if (media_content_disconnect() != MEDIA_CONTENT_ERROR_NONE)
55 std::cerr << "media_discontent_connect: error\n";
58 void ContentInstance::HandleMessage(const char* message) {
60 picojson::value::object o;
63 picojson::parse(v, message, message + strlen(message), &err);
65 std::cerr << "Ignoring message.\n";
69 std::cout << "HandleMessage: " << message << std::endl;
71 std::string cmd = v.get("cmd").to_str();
72 if (cmd == "ContentManager.getDirectories") {
73 HandleGetDirectoriesRequest(v);
74 } else if (cmd == "ContentManager.find") {
76 } else if (cmd == "ContentManager.scanFile") {
77 HandleScanFileRequest(v);
79 std::cerr << "Message " + cmd + " is not supported.\n";
83 void ContentInstance::PostAsyncErrorReply(const picojson::value& msg,
84 WebApiAPIErrors error_code) {
85 picojson::value::object o;
86 o["isError"] = picojson::value(true);
87 o["errorCode"] = picojson::value(static_cast<double>(error_code));
88 o["replyId"] = picojson::value(msg.get("replyId").get<double>());
91 PostMessage(v.serialize().c_str());
94 void ContentInstance::PostAsyncSuccessReply(const picojson::value& msg,
95 picojson::value::object& reply) {
96 reply["isError"] = picojson::value(false);
97 reply["replyId"] = picojson::value(msg.get("replyId").get<double>());
98 if (msg.contains(STR_EVENT_TYPE))
99 reply[STR_EVENT_TYPE] = picojson::value(msg.get(STR_EVENT_TYPE));
100 #ifdef DEBUG_JSON_CMD
101 std::cout << "reply: " << msg.serialize().c_str() << std::endl;
103 picojson::value v(reply);
104 PostMessage(v.serialize().c_str());
107 void ContentInstance::PostAsyncSuccessReply(const picojson::value& msg) {
108 picojson::value::object reply;
109 PostAsyncSuccessReply(msg, reply);
112 void ContentInstance::PostAsyncSuccessReply(const picojson::value& msg,
113 picojson::value& value) {
114 picojson::value::object reply;
115 reply["value"] = value;
116 PostAsyncSuccessReply(msg, reply);
119 void ContentInstance::HandleSyncMessage(const char* message) {
121 picojson::value::object o;
124 picojson::parse(v, message, message + strlen(message), &err);
126 std::cerr << "Ignoring message.\n";
129 #ifdef DEBUG_JSON_CMD
130 std::cout << "HandleSyncMessage: " << message << std::endl;
132 std::string cmd = v.get("cmd").to_str();
133 int rc = MEDIA_CONTENT_ERROR_INVALID_OPERATION;
135 if (cmd == "ContentManager.setChangeListener") {
136 rc = media_content_set_db_updated_cb(MediaContentChangeCallback, this);
137 } else if (cmd == "ContentManager.unsetChangeListener") {
138 rc = media_content_unset_db_updated_cb();
140 std::cerr << "Message " + cmd + " is not supported.\n";
143 if (rc != MEDIA_CONTENT_ERROR_NONE)
144 std::cerr << "error " << cmd << std::endl;
146 #ifdef DEBUG_JSON_CMD
147 std::cout << "Reply: " << v.serialize().c_str() << std::endl;
149 SendSyncReply(v.serialize().c_str());
152 void ContentInstance::HandleGetDirectoriesRequest(const picojson::value& msg) {
153 ContentFolderList folderList;
154 if (media_folder_foreach_folder_from_db(NULL,
156 reinterpret_cast<void*>(&folderList)) != MEDIA_CONTENT_ERROR_NONE) {
157 std::cerr << "media_folder_foreach_folder_from_db: error" << std::endl;
159 HandleGetDirectoriesReply(msg, &folderList);
163 void ContentInstance::HandleGetDirectoriesReply(const picojson::value& msg,
164 ContentFolderList* folderList) {
165 const std::vector<ContentFolder*>& results = folderList->getAllItems();
166 picojson::value::array folders;
168 for (unsigned i = 0; i < results.size(); i++) {
169 ContentFolder* folder = results[i];
171 picojson::value::object o;
173 o["id"] = picojson::value(folder->id());
174 o["directoryURI"] = picojson::value(folder->directoryURI());
175 o["title"] = picojson::value(folder->title());
176 o["storageType"] = picojson::value(folder->storageType());
177 o["modifiedDate"] = picojson::value(folder->modifiedDate());
179 folders.push_back(picojson::value(o));
181 picojson::value value(folders);
182 PostAsyncSuccessReply(msg, value);
185 bool ContentInstance::MediaFolderCallback(media_folder_h handle,
190 ContentFolderList* folderList =
191 reinterpret_cast<ContentFolderList*>(user_data);
193 ContentFolder* folder = new ContentFolder;
194 folder->init(handle);
195 folderList->addFolder(folder);
202 void ContentInstance::HandleFindRequest(const picojson::value& msg) {
203 ContentItemList itemList;
204 filter_h filterHandle = NULL;
206 ContentFilter& filter = ContentFilter::instance();
207 if (msg.contains(STR_FILTER)) {
208 picojson::value filterValue = msg.get(STR_FILTER);
209 if (!filterValue.is<picojson::null>() &&
210 filterValue.is<picojson::object>()) {
211 std::string condition = filter.convert(msg.get(STR_FILTER));
212 if (media_filter_create(&filterHandle) == MEDIA_CONTENT_ERROR_NONE)
213 media_filter_set_condition(filterHandle,
214 condition.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
218 if (media_info_foreach_media_from_db(filterHandle,
220 reinterpret_cast<ContentFolderList*>(&itemList))
221 != MEDIA_CONTENT_ERROR_NONE) {
222 std::cerr << "media_info_foreach_media_from_db: error" << std::endl;
224 HandleFindReply(msg, &itemList);
227 if (filterHandle != NULL && media_filter_destroy(filterHandle)
228 != MEDIA_CONTENT_ERROR_NONE)
229 std::cerr << "media_filter_destroy failed" << std::endl;
232 void ContentInstance::HandleFindReply(
233 const picojson::value& msg,
234 ContentItemList* itemList) {
235 const std::vector<ContentItem*> &results = itemList->getAllItems();
237 picojson::value::array items;
239 for (unsigned i = 0; i < results.size(); i++) {
240 ContentItem* item = results[i];
242 picojson::value::object o;
244 o["id"] = picojson::value(item->id());
245 o["name"] = picojson::value(item->name());
246 o["type"] = picojson::value(item->type());
247 o["mimeType"] = picojson::value(item->mimeType());
248 o["title"] = picojson::value(item->title());
249 o["contentURI"] = picojson::value(item->contentURI());
250 picojson::value::array uris;
251 uris.push_back(picojson::value(item->thumbnailURIs()));
252 o["thumbnailURIs"] = picojson::value(uris);
253 o["releaseDate"] = picojson::value(item->releaseDate());
255 picojson::value(item->modifiedDate());
256 o["size"] = picojson::value(static_cast<double>(item->size()));
257 o["description"] = picojson::value(item->description());
258 o["rating"] = picojson::value(static_cast<double>(item->rating()));
260 if (item->type() == "AUDIO") {
261 o["album"] = picojson::value(item->album());
262 picojson::value::array genres;
263 genres.push_back(picojson::value(item->genres()));
264 o["genres"] = picojson::value(genres);
265 picojson::value::array artists;
266 artists.push_back(picojson::value(item->artists()));
267 o["artists"] = picojson::value(artists);
268 picojson::value::array composers;
269 composers.push_back(picojson::value(item->composer()));
270 o["composers"] = picojson::value(composers);
271 o["copyright"] = picojson::value(item->copyright());
272 o["bitrate"] = picojson::value(static_cast<double>(item->bitrate()));
273 o["trackNumber"] = picojson::value(
274 static_cast<double>(item->trackNumber()));
275 o["duration"] = picojson::value(static_cast<double>(item->duration()));
276 } else if (item->type() == "IMAGE") {
277 o["width"] = picojson::value(static_cast<double>(item->width()));
278 o["height"] = picojson::value(static_cast<double>(item->height()));
279 o["orientation"] = picojson::value(item->orientation());
280 } else if (item->type() == "VIDEO") {
281 o["album"] = picojson::value(item->album());
282 picojson::value::array artists;
283 artists.push_back(picojson::value(item->artists()));
284 o["artists"] = picojson::value(artists);
285 o["duration"] = picojson::value(static_cast<double>(item->duration()));
286 o["width"] = picojson::value(static_cast<double>(item->width()));
287 o["height"] = picojson::value(static_cast<double>(item->height()));
290 picojson::value v(o);
292 items.push_back(picojson::value(o));
294 picojson::value value(items);
295 #ifdef DEBUG_JSON_REPLY
296 std::cout << "JSON reply: " << std::endl <<
297 value.serialize().c_str() << std::endl;
299 PostAsyncSuccessReply(msg, value);
302 bool ContentInstance::MediaInfoCallback(media_info_h handle, void* user_data) {
306 ContentItemList* itemList = reinterpret_cast<ContentItemList*>(user_data);
308 ContentItem* item = new ContentItem;
310 itemList->addItem(item);
317 void ContentInstance::MediaContentChangeCallback(
318 media_content_error_e error,
320 media_content_db_update_item_type_e update_item,
321 media_content_db_update_type_e update_type,
322 media_content_type_e media_type,
328 std::cout << "MediaContentChangeCallback: error=" << error <<
329 ", item=" << update_item << ", type=" << update_type << ", " <<
330 uuid << ", " << path << std::endl;
335 ContentInstance* self =
336 reinterpret_cast<ContentInstance*>(user_data);
338 picojson::value::object om;
339 om["replyId"] = picojson::value(static_cast<double>(0));
340 const std::string type = (update_type == MEDIA_CONTENT_INSERT ?
341 "INSERT" : (update_type == MEDIA_CONTENT_DELETE ? "DELETE" : "UPDATE"));
342 om[STR_EVENT_TYPE] = picojson::value(type);
343 picojson::value::object ov;
344 ov["type"] = picojson::value(static_cast<double>(media_type));
347 ov["id"] = picojson::value(uuid);
350 ov["contentURI"] = picojson::value(path);
353 ov["mimeType"] = picojson::value(mime_type);
355 picojson::value msg(om);
356 picojson::value value(ov);
357 #ifdef DEBUG_JSON_REPLY
358 std::cout << "JSON event msg: " << msg.serialize().c_str() << std::endl;
359 std::cout << "JSON event val: " << value.serialize().c_str() << std::endl;
362 self->PostAsyncSuccessReply(msg, value);
365 void ContentFolder::init(media_folder_h handle) {
368 media_content_storage_e storageType;
370 if (media_folder_get_folder_id(handle, &str) == MEDIA_CONTENT_ERROR_NONE) {
375 if (media_folder_get_path(handle, &str) == MEDIA_CONTENT_ERROR_NONE) {
376 setDirectoryURI(createUriFromLocalPath(str));
380 if (media_folder_get_name(handle, &str) == MEDIA_CONTENT_ERROR_NONE) {
385 if (media_folder_get_storage_type(
386 handle, &storageType) == MEDIA_CONTENT_ERROR_NONE) {
388 if (storageType == MEDIA_CONTENT_STORAGE_INTERNAL) {
390 } else if (storageType == MEDIA_CONTENT_STORAGE_EXTERNAL) {
395 setStorageType(type);
398 if (media_folder_get_modified_time(
399 handle, &date) == MEDIA_CONTENT_ERROR_NONE) {
402 setModifiedDate(tmp);
407 void ContentFolder::print(void) {
408 std::cout << "ID: " << id() << std::endl;
409 std::cout << "URI: " << directoryURI() << std::endl;
410 std::cout << "Title: " << title() << std::endl;
411 std::cout << "Type: " << storageType() << std::endl;
412 std::cout << "Date: " << modifiedDate() << std::endl;
416 void ContentItem::init(media_info_h handle) {
419 // NOTE: the Tizen CAPI media_info_* functions assumes
420 // the caller frees the char**. The CAPI can also return NULL char**
421 // even the return code is MEDIA_CONTENT_ERROR_NONE.
423 if (media_info_get_media_id(handle, &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
428 if (media_info_get_mime_type(handle, &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
433 if (media_info_get_title(handle, &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
438 if (media_info_get_display_name(handle,
439 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
444 if (media_info_get_file_path(handle, &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
445 setContentURI(createUriFromLocalPath(pc));
449 if (media_info_get_thumbnail_path(handle,
450 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
451 setThumbnailURIs(createUriFromLocalPath(pc));
455 if (media_info_get_description(handle,
456 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
462 if (media_info_get_modified_time(handle, &date) == MEDIA_CONTENT_ERROR_NONE) {
465 setModifiedDate(tmp);
469 if (media_info_get_rating(handle, &i) == MEDIA_CONTENT_ERROR_NONE)
472 unsigned long long ll; // NOLINT
473 if (media_info_get_size(handle, &ll) == MEDIA_CONTENT_ERROR_NONE)
476 media_content_type_e type;
477 if (media_info_get_media_type(handle, &type) == MEDIA_CONTENT_ERROR_NONE) {
478 if (type == MEDIA_CONTENT_TYPE_IMAGE) {
482 if (media_info_get_image(handle, &image) == MEDIA_CONTENT_ERROR_NONE) {
483 if (image_meta_get_width(image, &i) == MEDIA_CONTENT_ERROR_NONE)
486 if (image_meta_get_height(image, &i) == MEDIA_CONTENT_ERROR_NONE)
489 // TODO(spoussa): coordinates do not return sensible values...
491 if (media_info_get_latitude(handle, &d) == MEDIA_CONTENT_ERROR_NONE)
494 if (media_info_get_longitude(handle, &d) == MEDIA_CONTENT_ERROR_NONE)
497 media_content_orientation_e orientation;
498 if (image_meta_get_orientation(image, &orientation)
499 == MEDIA_CONTENT_ERROR_NONE) {
500 std::string result("");
502 switch (orientation) {
504 case 1: result = "NORMAL"; break;
505 case 2: result = "FLIP_HORIZONTAL"; break;
506 case 3: result = "ROTATE_180"; break;
507 case 4: result = "FLIP_VERTICAL"; break;
508 case 5: result = "TRANSPOSE"; break;
509 case 6: result = "ROTATE_90"; break;
510 case 7: result = "TRANSVERSE"; break;
511 case 8: result = "ROTATE_270"; break;
512 default: result = "Unknown"; break;
514 setOrientation(result);
516 image_meta_destroy(image);
518 } else if (type == MEDIA_CONTENT_TYPE_VIDEO) {
522 if (media_info_get_video(handle, &video) == MEDIA_CONTENT_ERROR_NONE) {
523 if (video_meta_get_recorded_date(video,
524 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
529 if (video_meta_get_album(video,
530 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
535 if (video_meta_get_artist(video,
536 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
541 if (video_meta_get_width(video, &i) == MEDIA_CONTENT_ERROR_NONE) {
545 if (video_meta_get_height(video, &i) == MEDIA_CONTENT_ERROR_NONE) {
549 if (video_meta_get_duration(video, &i) == MEDIA_CONTENT_ERROR_NONE) {
553 video_meta_destroy(video);
555 } else if (type == MEDIA_CONTENT_TYPE_MUSIC) {
559 if (media_info_get_audio(handle, &audio) == MEDIA_CONTENT_ERROR_NONE) {
560 if (audio_meta_get_recorded_date(audio,
561 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
566 if (audio_meta_get_album(audio,
567 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
572 if (audio_meta_get_artist(audio,
573 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
578 if (audio_meta_get_composer(audio,
579 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
584 if (audio_meta_get_duration(audio, &i) == MEDIA_CONTENT_ERROR_NONE)
587 if (audio_meta_get_copyright(audio,
588 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
593 if (audio_meta_get_track_num(audio,
594 &pc) == MEDIA_CONTENT_ERROR_NONE && pc) {
600 if (audio_meta_get_bit_rate(audio, &i) == MEDIA_CONTENT_ERROR_NONE)
603 audio_meta_destroy(audio);
604 } else if (type == MEDIA_CONTENT_TYPE_OTHERS) {
611 void ContentItem::print(void) {
612 std::cout << "----" << std::endl;
613 std::cout << "ID: " << id() << std::endl;
614 std::cout << "Name: " << name() << std::endl;
615 std::cout << "Type: " << type() << std::endl;
616 std::cout << "MIME: " << mimeType() << std::endl;
617 std::cout << "Title: " << title() << std::endl;
618 std::cout << "URI: " << contentURI() << std::endl;
619 std::cout << "ThumbnailURIs: " << thumbnailURIs() << std::endl;
620 std::cout << "Modified: " << modifiedDate();
621 std::cout << "Size: " << size() << std::endl;
622 std::cout << "Description: " << description() << std::endl;
623 std::cout << "Rating: " << rating() << std::endl;
624 if (type() == "AUDIO") {
625 std::cout << "Release Date: " << releaseDate() << std::endl;
626 std::cout << "Album: " << album() << std::endl;
627 std::cout << "Genres: " << genres() << std::endl;
628 std::cout << "Artists: " << artists() << std::endl;
629 std::cout << "Composer: " << composer() << std::endl;
630 std::cout << "Copyright: " << copyright() << std::endl;
631 std::cout << "Bitrate: " << bitrate() << std::endl;
632 std::cout << "Track num: " << trackNumber() << std::endl;
633 std::cout << "Duration: " << duration() << std::endl;
634 } else if (type() == "IMAGE") {
635 std::cout << "Width/Height: " << width() << "/" << height() << std::endl;
636 std::cout << "Latitude: " << latitude() << std::endl;
637 std::cout << "Longitude: " << longitude() << std::endl;
638 std::cout << "Orientation: " << orientation() << std::endl;
639 } else if (type() == "VIDEO") {
640 std::cout << "Album: " << album() << std::endl;
641 std::cout << "Artists: " << artists() << std::endl;
642 std::cout << "Duration: " << duration() << std::endl;
643 std::cout << "Width/Height: " << width() << "/" << height() << std::endl;
648 void ContentInstance::HandleScanFileRequest(const picojson::value& msg) {
649 if (msg.contains(STR_CONTENT_URI)) {
650 picojson::value uriValue = msg.get(STR_CONTENT_URI);
651 if (!uriValue.is<picojson::null>()) {
652 std::string uri = uriValue.to_str();
653 std::string path = getUriPath(uri);
656 int result = media_content_scan_file(path.c_str());
657 if (result == MEDIA_CONTENT_ERROR_NONE) {
658 HandleScanFileReply(msg);
660 std::cerr << "media_content_scan_file error:" << result << std::endl;
661 PostAsyncErrorReply(msg, WebApiAPIErrors::DATABASE_ERR);
667 void ContentInstance::HandleScanFileReply(const picojson::value& msg) {
668 PostAsyncSuccessReply(msg);