Merge "[Common] Fix preventing crash in argument validator" into tizen
[platform/core/api/webapi-plugins.git] / src / content / content_manager.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include "content/content_manager.h"
18
19 #include <media_content_internal.h>
20 #include <metadata_extractor.h>
21 #include <stdlib.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <algorithm>
25 #include <cstring>
26 #include <map>
27 #include <sstream>
28 #include <string>
29
30 #include "common/converter.h"
31 #include "common/filesystem/filesystem_provider.h"
32 #include "common/logger.h"
33 #include "common/scope_exit.h"
34 #include "common/tools.h"
35
36 #include "content/content_filter.h"
37
38 using namespace std;
39 using namespace common;
40
41 using common::tools::ReportSuccess;
42 using common::tools::ReportError;
43
44 namespace extension {
45 namespace content {
46
47 namespace {
48 static const std::string uri_prefix = "file://";
49 static const std::string uri_absolute_prefix = "file:///";
50 }
51
52 const std::map<std::string, media_content_orientation_e> orientationMap = {
53     {"NORMAL", MEDIA_CONTENT_ORIENTATION_NORMAL},
54     {"FLIP_HORIZONTAL", MEDIA_CONTENT_ORIENTATION_HFLIP},
55     {"ROTATE_180", MEDIA_CONTENT_ORIENTATION_ROT_180},
56     {"FLIP_VERTICAL", MEDIA_CONTENT_ORIENTATION_VFLIP},
57     {"TRANSPOSE", MEDIA_CONTENT_ORIENTATION_TRANSPOSE},
58     {"ROTATE_90", MEDIA_CONTENT_ORIENTATION_ROT_90},
59     {"TRANSVERSE", MEDIA_CONTENT_ORIENTATION_TRANSVERSE},
60     {"ROTATE_270", MEDIA_CONTENT_ORIENTATION_ROT_270},
61 };
62
63 std::string get_date(char* tmpStr) {
64   ScopeLogger();
65   if (tmpStr) {
66     struct tm* result = (struct tm*)calloc(1, sizeof(struct tm));
67     if (nullptr != result) {
68       if (strptime(tmpStr, "%Y:%m:%d %H:%M:%S", result) == NULL) {
69         free(result);
70         return std::string();
71       } else {
72         time_t t = mktime(result);  // + get_utc_offset() * 3600;
73         std::stringstream str_date;
74         str_date << t;
75         free(result);
76         return str_date.str();
77       }
78     }
79   }
80   return std::string();
81 }
82
83 void ContentToJson(media_info_h info, picojson::object& o) {
84   ScopeLogger();
85   int ret;
86   int tmpInt = 0;
87   bool tmpBool = false;
88   char* tmpStr = NULL;
89   time_t tmpDate;
90   double tmpDouble;
91   long long unsigned int tmpLong;
92   media_content_type_e type;
93
94   ret = media_info_get_media_type(info, &type);
95
96   if (ret != MEDIA_CONTENT_ERROR_NONE) {
97     LoggerE("Get media type failed: %d", ret);
98     type = MEDIA_CONTENT_TYPE_OTHERS;
99   }
100
101   if (type == MEDIA_CONTENT_TYPE_IMAGE) {
102     o["type"] = picojson::value(std::string("IMAGE"));
103     image_meta_h img;
104     if (MEDIA_CONTENT_ERROR_NONE == media_info_get_image(info, &img)) {
105       std::unique_ptr<std::remove_pointer<image_meta_h>::type, int (*)(image_meta_h)> img_ptr(
106           img, &image_meta_destroy);  // automatically release the memory
107       if (MEDIA_CONTENT_ERROR_NONE == image_meta_get_date_taken(img, &tmpStr)) {
108         if (tmpStr) {
109           o["releaseDate"] = picojson::value(get_date(tmpStr));
110           free(tmpStr);
111           tmpStr = NULL;
112         }
113       }
114       if (MEDIA_CONTENT_ERROR_NONE == image_meta_get_width(img, &tmpInt)) {
115         o["width"] = picojson::value(static_cast<double>(tmpInt));
116       }
117       if (MEDIA_CONTENT_ERROR_NONE == image_meta_get_height(img, &tmpInt)) {
118         o["height"] = picojson::value(static_cast<double>(tmpInt));
119       }
120       picojson::object geo;
121       if (MEDIA_CONTENT_ERROR_NONE == media_info_get_latitude(info, &tmpDouble)) {
122         geo["latitude"] = picojson::value(tmpDouble);
123       }
124       if (MEDIA_CONTENT_ERROR_NONE == media_info_get_longitude(info, &tmpDouble)) {
125         geo["longitude"] = picojson::value(tmpDouble);
126       }
127       o["geolocation"] = picojson::value(geo);
128       std::string ori;
129       media_content_orientation_e orientation;
130       if (MEDIA_CONTENT_ERROR_NONE == image_meta_get_orientation(img, &orientation)) {
131         switch (orientation) {
132           case MEDIA_CONTENT_ORIENTATION_NOT_AVAILABLE:
133           case MEDIA_CONTENT_ORIENTATION_NORMAL:
134             ori = "NORMAL";
135             break;
136           case MEDIA_CONTENT_ORIENTATION_HFLIP:
137             ori = "FLIP_HORIZONTAL";
138             break;
139           case MEDIA_CONTENT_ORIENTATION_ROT_180:
140             ori = "ROTATE_180";
141             break;
142           case MEDIA_CONTENT_ORIENTATION_VFLIP:
143             ori = "FLIP_VERTICAL";
144             break;
145           case MEDIA_CONTENT_ORIENTATION_TRANSPOSE:
146             ori = "TRANSPOSE";
147             break;
148           case MEDIA_CONTENT_ORIENTATION_ROT_90:
149             ori = "ROTATE_90";
150             break;
151           case MEDIA_CONTENT_ORIENTATION_TRANSVERSE:
152             ori = "TRANSVERSE";
153             break;
154           case MEDIA_CONTENT_ORIENTATION_ROT_270:
155             ori = "ROTATE_270";
156             break;
157         }
158         o["orientation"] = picojson::value(ori);
159       }
160     }
161   } else if (type == MEDIA_CONTENT_TYPE_VIDEO) {
162     o["type"] = picojson::value(std::string("VIDEO"));
163     video_meta_h video;
164     if (MEDIA_CONTENT_ERROR_NONE == media_info_get_video(info, &video)) {
165       std::unique_ptr<std::remove_pointer<video_meta_h>::type, int (*)(video_meta_h)> video_ptr(
166           video, &video_meta_destroy);  // automatically release the memory
167       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_width(video, &tmpInt)) {
168         o["width"] = picojson::value(static_cast<double>(tmpInt));
169       }
170
171       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_height(video, &tmpInt)) {
172         o["height"] = picojson::value(static_cast<double>(tmpInt));
173       }
174       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_artist(video, &tmpStr)) {
175         picojson::array artists;
176         if (tmpStr) {
177           artists.push_back(picojson::value(std::string(tmpStr)));
178           free(tmpStr);
179           tmpStr = NULL;
180         }
181         o["artists"] = picojson::value(artists);
182       }
183       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_album(video, &tmpStr)) {
184         if (tmpStr) {
185           o["album"] = picojson::value(tmpStr);
186           free(tmpStr);
187           tmpStr = NULL;
188         }
189       }
190       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_duration(video, &tmpInt)) {
191         o["duration"] = picojson::value(static_cast<double>(tmpInt));
192       }
193       if (MEDIA_CONTENT_ERROR_NONE == video_meta_get_recorded_date(video, &tmpStr)) {
194         if (tmpStr) {
195           o["releaseDate"] = picojson::value(get_date(tmpStr));
196           free(tmpStr);
197           tmpStr = NULL;
198         }
199       }
200     }
201     picojson::object geo;
202     if (MEDIA_CONTENT_ERROR_NONE == media_info_get_latitude(info, &tmpDouble)) {
203       geo["latitude"] = picojson::value(tmpDouble);
204     }
205     if (MEDIA_CONTENT_ERROR_NONE == media_info_get_longitude(info, &tmpDouble)) {
206       geo["longitude"] = picojson::value(tmpDouble);
207     }
208     o["geolocation"] = picojson::value(geo);
209   } else if (type == MEDIA_CONTENT_TYPE_SOUND || type == MEDIA_CONTENT_TYPE_MUSIC) {
210     o["type"] = picojson::value(std::string("AUDIO"));
211     audio_meta_h audio;
212     if (MEDIA_CONTENT_ERROR_NONE == media_info_get_audio(info, &audio)) {
213       std::unique_ptr<std::remove_pointer<audio_meta_h>::type, int (*)(audio_meta_h)> audio_ptr(
214           audio, &audio_meta_destroy);  // automatically release the memory
215       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_recorded_date(audio, &tmpStr)) {
216         if (tmpStr) {
217           o["releaseDate"] = picojson::value(get_date(tmpStr));
218           free(tmpStr);
219           tmpStr = NULL;
220         }
221       }
222       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_album(audio, &tmpStr)) {
223         if (tmpStr) {
224           o["album"] = picojson::value(std::string(tmpStr));
225           free(tmpStr);
226           tmpStr = NULL;
227         }
228       }
229       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_artist(audio, &tmpStr)) {
230         if (tmpStr) {
231           picojson::array artists;
232           artists.push_back(picojson::value(std::string(tmpStr)));
233           o["artists"] = picojson::value(artists);
234           free(tmpStr);
235           tmpStr = NULL;
236         }
237       }
238       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_genre(audio, &tmpStr)) {
239         if (tmpStr) {
240           picojson::array genres;
241           genres.push_back(picojson::value(std::string(tmpStr)));
242           o["genres"] = picojson::value(genres);
243           free(tmpStr);
244           tmpStr = NULL;
245         }
246       }
247       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_composer(audio, &tmpStr)) {
248         if (tmpStr) {
249           picojson::array composers;
250           composers.push_back(picojson::value(std::string(tmpStr)));
251           o["composers"] = picojson::value(composers);
252           free(tmpStr);
253           tmpStr = NULL;
254         }
255       }
256       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_copyright(audio, &tmpStr)) {
257         if (tmpStr) {
258           o["copyright"] = picojson::value(std::string(tmpStr));
259           free(tmpStr);
260           tmpStr = NULL;
261         }
262       }
263       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_bit_rate(audio, &tmpInt)) {
264         o["bitrate"] = picojson::value(static_cast<double>(tmpInt));
265       }
266       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_track_num(audio, &tmpStr)) {
267         if (tmpStr) {
268           o["trackNumber"] = picojson::value(static_cast<double>(std::atoi(tmpStr)));
269           free(tmpStr);
270           tmpStr = NULL;
271         } else {
272           o["trackNumber"] = picojson::value();
273         }
274       }
275       if (MEDIA_CONTENT_ERROR_NONE == audio_meta_get_duration(audio, &tmpInt)) {
276         o["duration"] = picojson::value(static_cast<double>(tmpInt));
277       }
278     }
279   } else {
280     o["type"] = picojson::value(std::string("OTHER"));
281   }
282
283   ret = media_info_get_media_id(info, &tmpStr);
284   if (ret == MEDIA_CONTENT_ERROR_NONE) {
285     if (tmpStr) {
286       o["id"] = picojson::value(std::string(tmpStr));
287       free(tmpStr);
288       tmpStr = NULL;
289     }
290   }
291   ret = media_info_get_display_name(info, &tmpStr);
292   if (ret == MEDIA_CONTENT_ERROR_NONE) {
293     if (tmpStr) {
294       o["name"] = picojson::value(std::string(tmpStr));
295       free(tmpStr);
296       tmpStr = NULL;
297     }
298   }
299
300   ret = media_info_get_mime_type(info, &tmpStr);
301   if (ret == MEDIA_CONTENT_ERROR_NONE) {
302     if (tmpStr) {
303       o["mimeType"] = picojson::value(std::string(tmpStr));
304       free(tmpStr);
305       tmpStr = NULL;
306     }
307   }
308   ret = media_info_get_title(info, &tmpStr);
309   if (ret == MEDIA_CONTENT_ERROR_NONE) {
310     if (tmpStr) {
311       o["title"] = picojson::value(std::string(tmpStr));
312       free(tmpStr);
313       tmpStr = NULL;
314     }
315   }
316   ret = media_info_get_file_path(info, &tmpStr);
317   if (ret == MEDIA_CONTENT_ERROR_NONE) {
318     if (tmpStr) {
319       o["contentURI"] = picojson::value(std::string(tmpStr));
320       free(tmpStr);
321       tmpStr = NULL;
322     }
323   }
324   ret = media_info_get_thumbnail_path(info, &tmpStr);
325   if (ret == MEDIA_CONTENT_ERROR_NONE) {
326     if (tmpStr) {
327       picojson::array thumbnails;
328       thumbnails.push_back(picojson::value(std::string(tmpStr)));
329       o["thumbnailURIs"] = picojson::value(thumbnails);
330       free(tmpStr);
331       tmpStr = NULL;
332     }
333   }
334   ret = media_info_get_description(info, &tmpStr);
335   if (ret == MEDIA_CONTENT_ERROR_NONE) {
336     if (tmpStr) {
337       o["description"] = picojson::value(std::string(tmpStr));
338       free(tmpStr);
339       tmpStr = NULL;
340     }
341   }
342   ret = media_info_get_rating(info, &tmpInt);
343   if (ret == MEDIA_CONTENT_ERROR_NONE) {
344     o["rating"] = picojson::value(static_cast<double>(tmpInt));
345   }
346   ret = media_info_get_size(info, &tmpLong);
347   if (ret == MEDIA_CONTENT_ERROR_NONE) {
348     o["size"] = picojson::value(static_cast<double>(tmpLong));
349   }
350   ret = media_info_get_favorite(info, &tmpBool);
351   if (ret == MEDIA_CONTENT_ERROR_NONE) {
352     o["isFavorite"] = picojson::value(tmpBool);
353   }
354   ret = media_info_get_modified_time(info, &tmpDate);
355   if (ret == MEDIA_CONTENT_ERROR_NONE) {
356     o["modifiedDate"] = picojson::value(static_cast<double>(tmpDate));
357   }
358 }
359
360 void ContentDirToJson(media_folder_h folder, picojson::object& o) {
361   ScopeLogger();
362   int ret;
363   char* tmpStr = NULL;
364   media_content_storage_e storage_type;
365
366   // id
367   ret = media_folder_get_folder_id(folder, &tmpStr);
368   if (ret == MEDIA_CONTENT_ERROR_NONE) {
369     if (tmpStr) {
370       o["id"] = picojson::value(std::string(tmpStr));
371       free(tmpStr);
372       tmpStr = NULL;
373     }
374   }
375
376   // directoryURI
377   std::string folder_path;
378   ret = media_folder_get_path(folder, &tmpStr);
379   if (ret == MEDIA_CONTENT_ERROR_NONE) {
380     if (tmpStr) {
381       o["directoryURI"] = picojson::value(std::string(tmpStr));
382       // folder_path value kept for modifiedDate property gathering
383       folder_path = tmpStr;
384       free(tmpStr);
385       tmpStr = NULL;
386     }
387   }
388
389   // title
390   ret = media_folder_get_name(folder, &tmpStr);
391   if (ret == MEDIA_CONTENT_ERROR_NONE) {
392     if (tmpStr) {
393       o["title"] = picojson::value(std::string(tmpStr));
394       free(tmpStr);
395       tmpStr = NULL;
396     }
397   }
398
399   // storageType
400   ret = media_folder_get_storage_type(folder, &storage_type);
401   if (ret == MEDIA_CONTENT_ERROR_NONE) {
402     if (storage_type == MEDIA_CONTENT_STORAGE_INTERNAL) {
403       o["storageType"] = picojson::value(std::string("INTERNAL"));
404     } else {
405       LoggerD("storageType = %d, assuming EXTERNAL as storage type", storage_type);
406       o["storageType"] = picojson::value(std::string("EXTERNAL"));
407     }
408   }
409
410   // modifiedDate
411   struct stat stat_res;
412   if (stat(folder_path.c_str(), &stat_res) == 0) {
413     auto mod_time = stat_res.st_mtime;
414     o["modifiedDate"] = picojson::value(static_cast<double>(mod_time));
415   }
416 }
417
418 static int setContent(media_info_h media, const picojson::value& content) {
419   ScopeLogger();
420
421   int ret;
422   std::string name = content.get("name").to_str();
423   std::string description = content.get("description").to_str();
424   int rating = std::stoi(content.get("rating").to_str());
425   bool is_fav = content.get("isFavorite").get<bool>();
426
427   if (NULL == media) {
428     LoggerE("MEDIA_CONTENT_ERROR_DB_FAILED");
429     return MEDIA_CONTENT_ERROR_DB_FAILED;
430   }
431
432   media_content_type_e type;
433   ret = media_info_get_media_type(media, &type);
434   if (ret != MEDIA_CONTENT_ERROR_NONE) {
435     LoggerE("Failed: media_info_get_media_type()");
436     return ret;
437   }
438
439   ret = media_info_set_display_name(media, name.c_str());
440   if (ret != MEDIA_CONTENT_ERROR_NONE) {
441     LoggerE("Updating name failed.");
442   }
443
444   ret = media_info_set_description(media, description.c_str());
445   if (ret != MEDIA_CONTENT_ERROR_NONE) {
446     LoggerE("Updating description failed.");
447   }
448
449   ret = media_info_set_rating(media, rating);
450   if (ret != MEDIA_CONTENT_ERROR_NONE) {
451     LoggerE("Updating rating failed.");
452   }
453
454   ret = media_info_set_favorite(media, is_fav);
455   if (ret != MEDIA_CONTENT_ERROR_NONE) {
456     LoggerE("Updating isFavorite failed.");
457   }
458
459   if (ret != MEDIA_CONTENT_ERROR_NONE) {
460     LoggerE("Updating favorite failed.");
461   }
462
463   if (type == MEDIA_CONTENT_TYPE_IMAGE) {
464     std::string orientation = content.get("orientation").to_str();
465     auto orientationToSet = orientationMap.find(orientation);
466
467     if (orientationToSet != orientationMap.end()) {
468       image_meta_h img;
469       if (MEDIA_CONTENT_ERROR_NONE == media_info_get_image(media, &img) &&
470           MEDIA_CONTENT_ERROR_NONE == image_meta_set_orientation(img, orientationToSet->second) &&
471           MEDIA_CONTENT_ERROR_NONE == image_meta_update_to_db(img)) {
472         LoggerD("orientation update was successful");
473       } else {
474         LoggerE("orientation update failed");
475       }
476       image_meta_destroy(img);
477     }
478   }
479
480   if (type == MEDIA_CONTENT_TYPE_IMAGE || type == MEDIA_CONTENT_TYPE_VIDEO) {
481     picojson::value geo = content.get("geolocation");
482     if (geo.evaluate_as_boolean()) {
483       LoggerD("geolocation is not null");
484       double latitude = atof(geo.get("latitude").to_str().c_str());
485       double longitude = atof(geo.get("longitude").to_str().c_str());
486       ret = media_info_set_latitude(media, latitude);
487       if (ret != MEDIA_CONTENT_ERROR_NONE) {
488         LoggerE("Updating geolocation is failed.");
489       }
490       ret = media_info_set_longitude(media, longitude);
491       if (ret != MEDIA_CONTENT_ERROR_NONE) {
492         LoggerD("Updating geolocation is failed.");
493       }
494     } else {
495       LoggerD("geolocation is null");
496     }
497   }
498
499   return MEDIA_CONTENT_ERROR_NONE;
500 }
501
502 static void FolderToJson(media_folder_h folder, picojson::object* out) {
503   ScopeLogger();
504
505   char* name = NULL;
506   char* id = NULL;
507   char* path = NULL;
508   time_t date;
509   media_content_storage_e storageType;
510
511   int ret;
512
513   ret = media_folder_get_folder_id(folder, &id);
514   if (ret != MEDIA_CONTENT_ERROR_NONE) {
515     LogAndReportError(ContentManager::convertError(ret), out,
516                       ("Failed: media_folder_get_folder_id"));
517     return;
518   }
519   std::unique_ptr<char, decltype(&free)> id_ptr(id, &free);
520
521   ret = media_folder_get_name(folder, &name);
522   if (ret != MEDIA_CONTENT_ERROR_NONE) {
523     LogAndReportError(ContentManager::convertError(ret), out, ("Failed: media_folder_get_name"));
524     return;
525   }
526   std::unique_ptr<char, decltype(&free)> name_ptr(name, &free);
527
528   ret = media_folder_get_path(folder, &path);
529   if (ret != MEDIA_CONTENT_ERROR_NONE) {
530     LogAndReportError(ContentManager::convertError(ret), out, ("Failed: media_folder_get_path"));
531     return;
532   }
533   std::unique_ptr<char, decltype(&free)> path_ptr(path, &free);
534
535   struct stat stat_res;
536   ret = stat(path, &stat_res);
537   if (0 != ret) {
538     LogAndReportError(ContentManager::convertError(errno), out, ("Failed: stat"));
539     return;
540   }
541   date = stat_res.st_mtime;
542
543   ret = media_folder_get_storage_type(folder, &storageType);
544   if (ret != MEDIA_CONTENT_ERROR_NONE) {
545     LogAndReportError(ContentManager::convertError(ret), out,
546                       ("Failed: media_folder_get_storage_type"));
547     return;
548   }
549
550   (*out)["id"] = picojson::value(std::string(id));
551   (*out)["directoryURI"] = picojson::value(std::string(path));
552   (*out)["title"] = picojson::value(std::string(name));
553
554   if (storageType == MEDIA_CONTENT_STORAGE_INTERNAL) {
555     (*out)["storageType"] = picojson::value(std::string("INTERNAL"));
556   } else if (storageType == MEDIA_CONTENT_STORAGE_EXTERNAL) {
557     (*out)["storageType"] = picojson::value(std::string("EXTERNAL"));
558   }
559
560   (*out)["modifiedDate"] = picojson::value(static_cast<double>(date));
561 }
562
563 static bool media_foreach_directory_cb(media_folder_h folder, void* user_data) {
564   ScopeLogger();
565   picojson::array* array = static_cast<picojson::array*>(user_data);
566   picojson::object json;
567   FolderToJson(folder, &json);
568   array->push_back(picojson::value(json));
569   return true;
570 }
571
572 static bool media_foreach_content_cb(media_info_h media, void* user_data) {
573   ScopeLogger();
574   picojson::value::array* contents = static_cast<picojson::value::array*>(user_data);
575   picojson::value::object o;
576   ContentToJson(media, o);
577   contents->push_back(picojson::value(o));
578   return true;
579 }
580
581 static bool playlist_foreach_cb(media_playlist_h playlist, void* user_data) {
582   ScopeLogger();
583   picojson::value::array* playlists = static_cast<picojson::value::array*>(user_data);
584   picojson::value::object o;
585   if (playlist != NULL) {
586     int id, cnt;
587     char* thumb_path = NULL;
588     char* name = NULL;
589     filter_h filter = NULL;
590     if (media_playlist_get_playlist_id(playlist, &id) == MEDIA_CONTENT_ERROR_NONE) {
591       std::stringstream str_id;
592       str_id << id;
593       o["id"] = picojson::value(std::to_string(id));
594     } else {
595       LoggerD("Invalid ID for playlist.");
596     }
597     if (media_playlist_get_thumbnail_path(playlist, &thumb_path) == MEDIA_CONTENT_ERROR_NONE) {
598       if (thumb_path != NULL) {
599         std::string thumbnail_uri(thumb_path);
600         if (thumbnail_uri != " ") {
601           thumbnail_uri = uri_prefix + thumbnail_uri;
602         }
603         o["thumbnailURI"] = picojson::value(thumbnail_uri);
604         free(thumb_path);
605       } else {
606         o["thumbnailURI"] = picojson::value();  // picojson::value(std::string(""));
607       }
608     } else {
609       LoggerD("Invalid thumbnail path for playlist.");
610     }
611     if (media_playlist_get_name(playlist, &name) == MEDIA_CONTENT_ERROR_NONE) {
612       o["name"] = picojson::value(std::string(name));
613       free(name);
614     } else {
615       LoggerD("Invalid name for playlist.");
616     }
617
618     media_filter_create(&filter);
619     std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
620         filter, &media_filter_destroy);  // automatically release the memory
621     if (media_playlist_get_media_count_from_db(id, filter, &cnt) == MEDIA_CONTENT_ERROR_NONE) {
622       o["numberOfTracks"] = picojson::value(static_cast<double>(cnt));
623     } else {
624       LoggerE("Invalid count for playlist.");
625     }
626     playlists->push_back(picojson::value(o));
627   }
628   return true;
629 }
630
631 static bool playlist_content_member_cb(int playlist_member_id, media_info_h media,
632                                        void* user_data) {
633   ScopeLogger();
634   picojson::value::array* contents = static_cast<picojson::value::array*>(user_data);
635   picojson::value::object o;
636
637   o["playlist_member_id"] = picojson::value(static_cast<double>(playlist_member_id));
638   ContentToJson(media, o);
639   contents->push_back(picojson::value(o));
640   return true;
641 }
642
643 ContentManager::ContentManager() {
644   ScopeLogger("ContentManager called");
645   if (media_content_connect() == MEDIA_CONTENT_ERROR_NONE) {
646     m_dbConnected = true;
647   } else {
648     m_dbConnected = false;
649   }
650   m_contentInstance = nullptr;
651 }
652
653 ContentManager::~ContentManager() {
654   ScopeLogger();
655   if (m_dbConnected) {
656     if (media_content_disconnect() == MEDIA_CONTENT_ERROR_NONE) {
657       m_dbConnected = false;
658     }
659   }
660 }
661
662 ContentManager* ContentManager::getInstance() {
663   ScopeLogger();
664   static ContentManager instance;
665   return &instance;
666 }
667
668 ContentInstance* ContentManager::getContentInstance() {
669   ScopeLogger();
670   return m_contentInstance;
671 }
672
673 void ContentManager::setContentInstance(ContentInstance* const content_instance) {
674   ScopeLogger();
675   m_contentInstance = content_instance;
676 }
677
678 bool ContentManager::isConnected() {
679   ScopeLogger();
680   return m_dbConnected;
681 }
682
683 void ContentManager::getDirectories(const std::shared_ptr<ReplyCallbackData>& user_data) {
684   ScopeLogger();
685   int ret;
686   filter_h filter = NULL;
687   ret = media_filter_create(&filter);
688   if (ret != MEDIA_CONTENT_ERROR_NONE) {
689     LoggerE("Failed: media_filter_create failed");
690     return;
691   }
692
693   SCOPE_EXIT {
694     media_filter_destroy(filter);
695   };
696
697   std::string condition = "(FOLDER_STORAGE_TYPE = 0 OR FOLDER_STORAGE_TYPE = 1)";
698   media_filter_set_condition(filter, condition.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
699
700   picojson::array pico_dirs;
701   ret = media_folder_foreach_folder_from_db(filter, media_foreach_directory_cb, &pico_dirs);
702   if (ret != MEDIA_CONTENT_ERROR_NONE) {
703     PlatformResult err = LogAndCreateResult(
704         ErrorCode::UNKNOWN_ERR, "Getting the directories failed.",
705         ("Failed: Getting the directories failed %d (%s)", ret, get_error_message(ret)));
706     user_data->isSuccess = err;
707     return;
708   }
709
710   user_data->result = picojson::value(pico_dirs);
711 }
712
713 void ContentManager::find(const std::shared_ptr<ReplyCallbackData>& user_data) {
714   ScopeLogger();
715
716   int ret;
717   int count, offset;
718   std::string dirId;
719
720   picojson::value::array arrayContent;
721   filter_h filter = nullptr;
722   media_filter_create(&filter);
723   SCOPE_EXIT {
724     if (filter) {
725       media_filter_destroy(filter);
726     }
727   };
728
729   if (!IsNull(user_data->args.get("filter"))) {
730     ContentFilter filterMechanism;
731     std::string query;
732     picojson::object argsObject = JsonCast<picojson::object>(user_data->args);
733     if (filterMechanism.BuildQuery(FromJson<picojson::object>(argsObject, "filter"), &query)) {
734       LoggerD("Filter query: %s", query.c_str());
735       ret = media_filter_set_condition(filter, query.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
736       if (MEDIA_CONTENT_ERROR_NONE != ret) {
737         LoggerE("Platform filter setting failed, error %d", ret);
738       }
739     }
740   }
741
742   if (user_data->args.contains("sortMode")) {
743     picojson::value vSortMode = user_data->args.get("sortMode");
744
745     if (vSortMode.is<picojson::object>()) {
746       std::string sortModeName, sortModeOrder;
747
748       ContentFilter::MapField(vSortMode.get("attributeName").to_str(), &sortModeName);
749
750       sortModeOrder = vSortMode.get("order").to_str();
751       if (!sortModeOrder.empty()) {
752         media_content_order_e order = MEDIA_CONTENT_ORDER_ASC;
753
754         if (sortModeOrder == "ASC") {
755           order = MEDIA_CONTENT_ORDER_ASC;
756         } else if (sortModeOrder == "DESC") {
757           order = MEDIA_CONTENT_ORDER_DESC;
758         }
759
760         ret = media_filter_set_order(filter, order, sortModeName.c_str(),
761                                      MEDIA_CONTENT_COLLATE_DEFAULT);
762         if (MEDIA_CONTENT_ERROR_NONE != ret) {
763           LoggerE("Platform SortMode setting failed, error: %d", ret);
764         }
765       }
766     }
767   }
768
769   if (!IsNull(user_data->args.get("count"))) {
770     count = static_cast<int>(user_data->args.get("count").get<double>());
771   } else {
772     count = -1;
773   }
774   if (!IsNull(user_data->args.get("offset"))) {
775     offset = static_cast<int>(user_data->args.get("offset").get<double>());
776   } else {
777     offset = -1;
778   }
779   ret = media_filter_set_offset(filter, offset, count);
780   if (MEDIA_CONTENT_ERROR_NONE != ret) {
781     LoggerE("A platform error occurs in media_filter_set_offset: %d", ret);
782   }
783   if (!IsNull(user_data->args.get("directoryId"))) {
784     dirId = user_data->args.get("directoryId").get<std::string>();
785     ret = media_folder_foreach_media_from_db(dirId.c_str(), filter, media_foreach_content_cb,
786                                              static_cast<void*>(&arrayContent));
787   } else {
788     ret = media_info_foreach_media_from_db(filter, media_foreach_content_cb,
789                                            static_cast<void*>(&arrayContent));
790   }
791
792   if (ret == MEDIA_CONTENT_ERROR_NONE) {
793     user_data->result = picojson::value(arrayContent);
794   } else {
795     PlatformResult err = LogAndCreateResult(
796         ErrorCode::UNKNOWN_ERR, "The iteration failed in platform",
797         ("The iteration failed in platform: %d (%s)", ret, get_error_message(ret)));
798     user_data->isSuccess = err;
799   }
800 }
801
802 int ContentManager::scanFile(std::string& uri) {
803   ScopeLogger();
804   return media_content_scan_file(uri.c_str());
805 }
806
807 PlatformResult ContentManager::scanDirectory(media_scan_completed_cb callback,
808                                              ReplyCallbackData* cbData) {
809   ScopeLogger();
810   const std::string& contentDirURI = cbData->args.get("contentDirURI").get<std::string>();
811   std::string real_path = common::FilesystemProvider::Create().GetRealPath(contentDirURI);
812   const bool recursive = cbData->args.get("recursive").get<bool>();
813
814   int ret = media_content_scan_folder(real_path.c_str(), recursive, callback, (void*)cbData);
815
816   if (ret != MEDIA_CONTENT_ERROR_NONE) {
817     if (MEDIA_CONTENT_ERROR_INVALID_PARAMETER == ret) {
818       return LogAndCreateResult(
819           ErrorCode::INVALID_VALUES_ERR, "Scanning content directory failed",
820           ("Scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
821
822     } else {
823       return LogAndCreateResult(
824           ErrorCode::UNKNOWN_ERR, "Scanning content directory failed",
825           ("Scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
826     }
827   }
828   return PlatformResult(ErrorCode::NO_ERROR);
829 }
830
831 PlatformResult ContentManager::cancelScanDirectory(const std::string& content_dir_uri) {
832   ScopeLogger();
833
834   int ret = media_content_cancel_scan_folder(content_dir_uri.c_str());
835   if (ret != MEDIA_CONTENT_ERROR_NONE) {
836     return LogAndCreateResult(
837         ErrorCode::UNKNOWN_ERR, "Cancel scan content directory failed",
838         ("Cancel scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
839   }
840   return PlatformResult(ErrorCode::NO_ERROR);
841 }
842
843 PlatformResult ContentManager::addChangeListener(media_content_noti_h* noti_handle,
844                                                  media_content_db_update_cb callback,
845                                                  void* user_data) {
846   ScopeLogger();
847
848   int ret = media_content_add_db_updated_cb(callback, user_data, noti_handle);
849
850   if (MEDIA_CONTENT_ERROR_NONE != ret) {
851     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Failed to add the listener.",
852                               ("Failed to add the listener: %d (%s)", ret, get_error_message(ret)));
853   }
854
855   return PlatformResult(ErrorCode::NO_ERROR);
856 }
857
858 PlatformResult ContentManager::removeChangeListener(media_content_noti_h noti_handle) {
859   ScopeLogger();
860
861   int ret = media_content_remove_db_updated_cb(noti_handle);
862
863   switch (ret) {
864     case MEDIA_CONTENT_ERROR_NONE:
865       return PlatformResult(ErrorCode::NO_ERROR);
866     case MEDIA_CONTENT_ERROR_INVALID_PARAMETER:
867       // Trying to remove non-existent listener, ignoring
868       LoggerI("Failed to remove the listener: %d (%s)", ret, get_error_message(ret));
869       return PlatformResult(ErrorCode::NO_ERROR);
870     default:
871       return LogAndCreateResult(
872           ErrorCode::ABORT_ERR, "Failed to remove the listener.",
873           ("Failed to remove the listener: %d (%s)", ret, get_error_message(ret)));
874   }
875 }
876
877 void ContentManager::createPlaylist(std::string name,
878                                     const std::shared_ptr<ReplyCallbackData>& user_data) {
879   ScopeLogger();
880   media_playlist_h playlist = NULL;
881
882   int ret = media_playlist_insert_to_db(name.c_str(), &playlist);
883   std::unique_ptr<std::remove_pointer<media_playlist_h>::type, int (*)(media_playlist_h)>
884       playlist_ptr(playlist, &media_playlist_destroy);  // automatically release the memory
885   if (ret != MEDIA_CONTENT_ERROR_NONE) {
886     // MEDIA_CONTENT_ERROR_DB_FAILED means that playlist probably already exists
887     PlatformResult err = LogAndCreateResult(
888         MEDIA_CONTENT_ERROR_DB_FAILED == ret ? ErrorCode::INVALID_VALUES_ERR
889                                              : ErrorCode::UNKNOWN_ERR,
890         "Creation of playlist has failed.",
891         ("Failed: creation of playlist is failed: %d (%s)", ret, get_error_message(ret)));
892     user_data->isSuccess = err;
893     return;
894   }
895   picojson::value::object o;
896
897   if (playlist != NULL) {
898     int id, cnt;
899     char* thumb_path = NULL;
900     char* name_playlist = NULL;
901     filter_h filter = NULL;
902     if (media_playlist_get_playlist_id(playlist, &id) == MEDIA_CONTENT_ERROR_NONE) {
903       o["id"] = picojson::value(std::to_string(id));
904     } else {
905       PlatformResult err =
906           LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "loading of playlist is failed.");
907       user_data->isSuccess = err;
908       return;
909     }
910     if (media_playlist_get_thumbnail_path(playlist, &thumb_path) == MEDIA_CONTENT_ERROR_NONE) {
911       if (thumb_path != NULL) {
912         o["thumbnailURI"] = picojson::value(std::string(thumb_path));
913         free(thumb_path);
914       } else {
915         o["thumbnailURI"] = picojson::value();
916       }
917     } else {
918       LoggerE("Invalid thumbnail path for playlist.");
919     }
920     if (media_playlist_get_name(playlist, &name_playlist) == MEDIA_CONTENT_ERROR_NONE) {
921       o["name"] = picojson::value(std::string(name_playlist));
922       free(name_playlist);
923     } else {
924       LoggerE("Invalid name for playlist.");
925     }
926     media_filter_create(&filter);
927     std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
928         filter, &media_filter_destroy);  // automatically release the memory
929
930     if (media_playlist_get_media_count_from_db(id, filter, &cnt) == MEDIA_CONTENT_ERROR_NONE) {
931       o["numberOfTracks"] = picojson::value(static_cast<double>(cnt));
932     } else {
933       LoggerE("Invalid count for playlist.");
934     }
935   }
936
937   user_data->result = picojson::value(o);
938 }
939
940 void ContentManager::getPlaylists(const std::shared_ptr<ReplyCallbackData>& user_data) {
941   ScopeLogger();
942   int ret;
943   filter_h filter = nullptr;
944   media_filter_create(&filter);
945   std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
946       filter, &media_filter_destroy);  // automatically release the memory
947   picojson::value::array playlists;
948
949   ret = media_playlist_foreach_playlist_from_db(filter, playlist_foreach_cb,
950                                                 static_cast<void*>(&playlists));
951
952   if (ret != MEDIA_CONTENT_ERROR_NONE) {
953     PlatformResult err = LogAndCreateResult(
954         ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
955         ("Failed: Getting playlist is failed %d (%s)", ret, get_error_message(ret)));
956     user_data->isSuccess = err;
957   }
958
959   user_data->result = picojson::value(playlists);
960 }
961
962 void ContentManager::removePlaylist(std::string playlistId,
963                                     const std::shared_ptr<ReplyCallbackData>& user_data) {
964   ScopeLogger();
965   int id = std::atoi(playlistId.c_str());
966   if (id == 0) {
967     PlatformResult err = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "PlaylistId is wrong.");
968     user_data->isSuccess = err;
969     return;
970   }
971
972   int ret = media_playlist_delete_from_db(id);
973   if (ret != MEDIA_CONTENT_ERROR_NONE) {
974     PlatformResult err =
975         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removal of playlist is failed.");
976     user_data->isSuccess = err;
977   }
978 }
979
980 int ContentManager::update(const picojson::value& args) {
981   ScopeLogger();
982
983   int ret;
984   picojson::value content = args.get("content");
985   std::string id = content.get("id").to_str();
986
987   media_info_h media = NULL;
988   ret = media_info_get_media_from_db(id.c_str(), &media);
989   if (ret == MEDIA_CONTENT_ERROR_NONE) {
990     setContent(media, content);
991     ret = media_info_update_to_db(media);
992     media_info_destroy(media);
993   }
994
995   return ret;
996 }
997
998 int ContentManager::updateBatch(const picojson::value& args) {
999   ScopeLogger();
1000   int ret = 0;
1001   std::vector<picojson::value> contents = args.get("contents").get<picojson::array>();
1002
1003   for (picojson::value::array::iterator it = contents.begin(); it != contents.end(); ++it) {
1004     picojson::value content = *it;
1005     std::string id = content.get("id").to_str();
1006     media_info_h media = NULL;
1007     ret = media_info_get_media_from_db(id.c_str(), &media);
1008     if (media != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1009       ret = setContent(media, content);
1010       if (ret != MEDIA_CONTENT_ERROR_NONE) {
1011         LoggerE("setContent failed");
1012         return ret;
1013       }
1014
1015       ret = media_info_update_to_db(media);
1016       if (ret != MEDIA_CONTENT_ERROR_NONE) {
1017         LoggerE("update to db failed");
1018       }
1019       media_info_destroy(media);
1020     } else {
1021       return ret;
1022     }
1023   }
1024   return ret;
1025 }
1026
1027 int ContentManager::playlistAdd(std::string playlist_id, std::string content_id) {
1028   ScopeLogger();
1029
1030   media_playlist_h playlist = NULL;
1031   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1032
1033   if (playlist != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1034     ret = media_playlist_add_media(playlist, content_id.c_str());
1035     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1036       LoggerE("The content(id:%s) can't add to playlist", content_id.c_str());
1037     }
1038
1039     ret = media_playlist_update_to_db(playlist);
1040     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1041       LoggerE("The content(id:%s) can't add to playlist", content_id.c_str());
1042     }
1043   } else {
1044     LoggerE("Playlist(id:%s) is not exist", playlist_id.c_str());
1045   }
1046
1047   media_playlist_destroy(playlist);
1048   return ret;
1049 }
1050
1051 int ContentManager::playlistRemove(std::string playlist_id, int member_id) {
1052   ScopeLogger();
1053
1054   media_playlist_h playlist = NULL;
1055   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1056   if (playlist != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1057     ret = media_playlist_remove_media(playlist, member_id);
1058     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1059       LoggerE("The content can't remove to playlist");
1060     }
1061
1062     ret = media_playlist_update_to_db(playlist);
1063     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1064       LoggerE("The content can't remove to playlist");
1065     }
1066   } else {
1067     LoggerE("Playlist(id:%s) is not exist", playlist_id.c_str());
1068   }
1069   media_playlist_destroy(playlist);
1070
1071   return ret;
1072 }
1073
1074 void ContentManager::playlistAddbatch(const std::shared_ptr<ReplyCallbackData>& user_data) {
1075   ScopeLogger();
1076   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1077
1078   media_playlist_h playlist = NULL;
1079   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1080
1081   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1082     PlatformResult err =
1083         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1084                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1085     user_data->isSuccess = err;
1086     return;
1087   }
1088
1089   std::vector<picojson::value> contents = user_data->args.get("contents").get<picojson::array>();
1090   for (picojson::value::array::iterator it = contents.begin(); it != contents.end(); ++it) {
1091     picojson::value content = *it;
1092     std::string id = content.get("id").to_str();
1093     ret = media_playlist_add_media(playlist, id.c_str());
1094     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1095       LoggerE("Adding Content(id:%s) is failed.", id.c_str());
1096     }
1097   }
1098
1099   ret = media_playlist_update_to_db(playlist);
1100   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1101     PlatformResult err =
1102         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Adding playlist is failed.",
1103                            ("Adding playlist is failed: %d (%s)", ret, get_error_message(ret)));
1104     user_data->isSuccess = err;
1105   }
1106   media_playlist_destroy(playlist);
1107 }
1108
1109 void ContentManager::playlistGet(const std::shared_ptr<ReplyCallbackData>& user_data) {
1110   ScopeLogger();
1111   media_playlist_h playlist = NULL;
1112   media_content_order_e order = MEDIA_CONTENT_ORDER_ASC;
1113   const std::string playOrder("play_order");
1114
1115   SCOPE_EXIT {
1116     if (playlist) {
1117       media_playlist_destroy(playlist);
1118     }
1119   };
1120
1121   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1122   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1123   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1124     PlatformResult err =
1125         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1126                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1127     user_data->isSuccess = err;
1128     return;
1129   }
1130
1131   filter_h filter = NULL;
1132   ret = media_filter_create(&filter);
1133   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1134     PlatformResult err =
1135         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Creating a filter is failed.",
1136                            ("Creating a filter is failed: %d (%s)", ret, get_error_message(ret)));
1137     user_data->isSuccess = err;
1138     return;
1139   }
1140
1141   int count = user_data->args.get("count").get<double>();
1142   int offset = user_data->args.get("offset").get<double>();
1143   ret = media_filter_set_offset(filter, offset, count);
1144   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1145     LoggerD("Setting a offset/count is failed.");
1146   }
1147   ret = media_filter_set_order(filter, order, playOrder.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
1148   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1149     LoggerD("Setting a offset/count is failed.");
1150   }
1151
1152   picojson::value::array arrayContent;
1153   ret = media_playlist_foreach_media_from_db(std::stoi(playlist_id), filter,
1154                                              playlist_content_member_cb,
1155                                              static_cast<void*>(&arrayContent));
1156
1157   media_filter_destroy(filter);
1158   if (ret == MEDIA_CONTENT_ERROR_NONE) {
1159     user_data->result = picojson::value(arrayContent);
1160   } else {
1161     PlatformResult err =
1162         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Creating a filter is failed.",
1163                            ("Creating a filter is failed: %d (%s)", ret, get_error_message(ret)));
1164     user_data->isSuccess = err;
1165   }
1166 }
1167
1168 void ContentManager::playlistRemovebatch(const std::shared_ptr<ReplyCallbackData>& user_data) {
1169   ScopeLogger();
1170   media_playlist_h playlist = NULL;
1171
1172   SCOPE_EXIT {
1173     if (playlist) {
1174       media_playlist_destroy(playlist);
1175     }
1176   };
1177
1178   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1179   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1180   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1181     PlatformResult err =
1182         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1183                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1184     user_data->isSuccess = err;
1185     return;
1186   }
1187
1188   std::vector<picojson::value> members = user_data->args.get("members").get<picojson::array>();
1189   std::size_t members_size = members.size();
1190   for (std::size_t i = 0; i < members_size; ++i) {
1191     int member_id = static_cast<int>(members.at(i).get<double>());
1192     ret = media_playlist_remove_media(playlist, member_id);
1193
1194     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1195       LoggerD("Removing a content is failed.");
1196     }
1197   }
1198
1199   ret = media_playlist_update_to_db(playlist);
1200   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1201     PlatformResult err = LogAndCreateResult(
1202         ErrorCode::UNKNOWN_ERR, "Removing the contents is failed.",
1203         ("Removing the contents is failed: %d (%s)", ret, get_error_message(ret)));
1204     user_data->isSuccess = err;
1205   }
1206 }
1207
1208 void ContentManager::playlistSetOrder(const std::shared_ptr<ReplyCallbackData>& user_data) {
1209   ScopeLogger();
1210   media_playlist_h playlist = NULL;
1211
1212   SCOPE_EXIT {
1213     if (playlist) {
1214       media_playlist_destroy(playlist);
1215     }
1216   };
1217
1218   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1219   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1220   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1221     PlatformResult err =
1222         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1223                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1224     user_data->isSuccess = err;
1225     return;
1226   }
1227
1228   int cnt;
1229   std::vector<picojson::value> members = user_data->args.get("members").get<picojson::array>();
1230
1231   ret = media_playlist_get_media_count_from_db(std::stoi(playlist_id), NULL, &cnt);
1232   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1233     LoggerE("Failed: media_playlist_get_media_count_from_db");
1234     PlatformResult err = convertError(ret);
1235     user_data->isSuccess = err;
1236     return;
1237   }
1238   std::size_t members_size = members.size();
1239   if (cnt < 0 || static_cast<size_t>(cnt) != members_size) {
1240     PlatformResult err = LogAndCreateResult(
1241         ErrorCode::INVALID_VALUES_ERR,
1242         "The items array does not contain all items from the playlist.",
1243         ("Failed: The items array does not contain all items from the playlist: %d (%s)", ret,
1244          get_error_message(ret)));
1245     user_data->isSuccess = err;
1246     return;
1247   }
1248
1249   for (std::size_t i = 0; i < members_size; ++i) {
1250     int member_id = static_cast<int>(members.at(i).get<double>());
1251     ret = media_playlist_set_play_order(playlist, member_id, i);
1252     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1253       LoggerD("Removing a content is failed.");
1254     }
1255   }
1256
1257   ret = media_playlist_update_to_db(playlist);
1258   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1259     PlatformResult err = LogAndCreateResult(
1260         ErrorCode::UNKNOWN_ERR, "Removing the contents is failed.",
1261         ("Removing the contents is failed: %d (%s)", ret, get_error_message(ret)));
1262     user_data->isSuccess = err;
1263   }
1264 }
1265
1266 void ContentManager::playlistMove(const std::shared_ptr<ReplyCallbackData>& user_data) {
1267   ScopeLogger();
1268   media_playlist_h playlist = NULL;
1269
1270   SCOPE_EXIT {
1271     if (playlist) {
1272       media_playlist_destroy(playlist);
1273     }
1274   };
1275
1276   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1277   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1278   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1279     PlatformResult err =
1280         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1281                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1282     user_data->isSuccess = err;
1283     return;
1284   }
1285   int old_order;
1286   double member_id = user_data->args.get("memberId").get<double>();
1287   double delta = user_data->args.get("delta").get<double>();
1288   ret = media_playlist_get_play_order(playlist, static_cast<int>(member_id), &old_order);
1289   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1290     PlatformResult err = LogAndCreateResult(
1291         ErrorCode::UNKNOWN_ERR, "The content can't find form playlist.",
1292         ("The content can't find form playlist: %d (%s)", ret, get_error_message(ret)));
1293     user_data->isSuccess = err;
1294     return;
1295   }
1296   int new_order = static_cast<int>(old_order) + static_cast<int>(delta);
1297   ret = media_playlist_set_play_order(playlist, static_cast<int>(member_id), new_order);
1298   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1299     PlatformResult err = LogAndCreateResult(
1300         ErrorCode::UNKNOWN_ERR, "The content can't update play_order.",
1301         ("The content can't update play_order: %d (%s)", ret, get_error_message(ret)));
1302     user_data->isSuccess = err;
1303     return;
1304   }
1305   ret = media_playlist_update_to_db(playlist);
1306   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1307     PlatformResult err = LogAndCreateResult(
1308         ErrorCode::UNKNOWN_ERR, "Updateing play_order is failed.",
1309         ("Updateing play_order is failed: %d (%s)", ret, get_error_message(ret)));
1310     user_data->isSuccess = err;
1311   }
1312 }
1313
1314 int ContentManager::getLyrics(const picojson::value& args, picojson::object& result) {
1315   ScopeLogger();
1316
1317   int ret = METADATA_EXTRACTOR_ERROR_NONE;
1318   const std::string& contentURI = args.get("contentURI").to_str();
1319   if (contentURI.empty()) {
1320     LoggerE("contentURI empty - skipping media extractor");
1321     return -1;
1322   }
1323
1324   metadata_extractor_h extractor;
1325   ret = metadata_extractor_create(&extractor);
1326   if (METADATA_EXTRACTOR_ERROR_NONE != ret) {
1327     LoggerE("metadata_extractor_create failed, error: %d", ret);
1328   }
1329   std::unique_ptr<std::remove_pointer<metadata_extractor_h>::type, int (*)(metadata_extractor_h)>
1330       extractor_ptr(extractor, &metadata_extractor_destroy);  // automatically release the memory
1331
1332   ret = metadata_extractor_set_path(extractor, contentURI.c_str());
1333   if (ret != METADATA_EXTRACTOR_ERROR_NONE) {
1334     LoggerE("metadata_extractor_set_path failed, error: %d", ret);
1335     return ret;
1336   }
1337   picojson::array timestamps;
1338   picojson::array texts = picojson::array();
1339   char* strSyncTextNum = NULL;
1340
1341   ret = metadata_extractor_get_metadata(extractor, METADATA_SYNCLYRICS_NUM, &strSyncTextNum);
1342   if (ret != METADATA_EXTRACTOR_ERROR_NONE) {
1343     LoggerE("Media extractor error %d", ret);
1344     return ret;
1345   }
1346
1347   int nSyncTextNum = 0;
1348   if (strSyncTextNum) {
1349     nSyncTextNum = atoi(strSyncTextNum);
1350     free(strSyncTextNum);
1351     strSyncTextNum = NULL;
1352   }
1353   if (nSyncTextNum > 0) {
1354     result["type"] = picojson::value(std::string("SYNCHRONIZED"));
1355     for (int i = 0; i < nSyncTextNum; i++) {
1356       unsigned long time_info = 0;
1357       char* lyrics = NULL;
1358       ret = metadata_extractor_get_synclyrics(extractor, i, &time_info, &lyrics);
1359       if (ret == METADATA_EXTRACTOR_ERROR_NONE) {
1360         timestamps.push_back(picojson::value(static_cast<double>(time_info)));
1361         texts.push_back(picojson::value(std::string(lyrics)));
1362         free(lyrics);
1363       }
1364     }
1365     result["texts"] = picojson::value(texts);
1366     result["timestamps"] = picojson::value(timestamps);
1367     ret = METADATA_EXTRACTOR_ERROR_NONE;
1368   } else {
1369     char* unSyncText = nullptr;
1370     ret = metadata_extractor_get_metadata(extractor, METADATA_UNSYNCLYRICS, &unSyncText);
1371     if (ret == METADATA_EXTRACTOR_ERROR_NONE) {
1372       result["type"] = picojson::value(std::string("UNSYNCHRONIZED"));
1373       if (nullptr == unSyncText) {
1374         LoggerE("Unsynchronized lyrics text is NULL");
1375       }
1376       texts.push_back(picojson::value(unSyncText ? unSyncText : ""));
1377       result["texts"] = picojson::value(texts);
1378       free(unSyncText);
1379     }
1380   }
1381
1382   return ret;
1383 }
1384
1385 media_playlist_h getPlaylistHandle(int id) {
1386   ScopeLogger();
1387   media_playlist_h playlist_handle = nullptr;
1388   int ret_code = media_playlist_get_playlist_from_db(id, &playlist_handle);
1389   if (MEDIA_CONTENT_ERROR_NONE != ret_code || playlist_handle == nullptr) {
1390     LoggerE("could not get playlist handle for id: %d", id);
1391     return nullptr;
1392   }
1393
1394   return playlist_handle;
1395 }
1396
1397 void destroyMediaPlaylistHandle(media_playlist_h& playlist_handle) {
1398   ScopeLogger();
1399   if (playlist_handle) {
1400     int ret_code = media_playlist_destroy(playlist_handle);
1401     playlist_handle = nullptr;
1402
1403     if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1404       LoggerE("media_playlist_destroy failed");
1405     }
1406   }
1407 }
1408
1409 int ContentManager::getPlaylistName(int id, std::string* result) {
1410   ScopeLogger();
1411   media_playlist_h playlist_handle = getPlaylistHandle(id);
1412   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1413
1414   char* tmp_playlist_name = nullptr;
1415   const int ret_code = media_playlist_get_name(playlist_handle, &tmp_playlist_name);
1416
1417   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1418     LoggerE("media_playlist_get_name failed");
1419     return TIZEN_ERROR_UNKNOWN;
1420   }
1421
1422   std::string playlist_name;
1423   if (tmp_playlist_name) {
1424     playlist_name = tmp_playlist_name;
1425     free(tmp_playlist_name);
1426     tmp_playlist_name = nullptr;
1427   }
1428
1429   *result = playlist_name;
1430   return MEDIA_CONTENT_ERROR_NONE;
1431 }
1432
1433 int updatePlaylistInDB(media_playlist_h playlist_handle) {
1434   ScopeLogger();
1435   int ret_code = media_playlist_update_to_db(playlist_handle);
1436   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1437     LoggerE("media_playlist_update_to_db failed");
1438     return ret_code;
1439   }
1440   return MEDIA_CONTENT_ERROR_NONE;
1441 }
1442
1443 int ContentManager::setPlaylistName(int id, const std::string& name) {
1444   ScopeLogger();
1445   if (name.empty()) {
1446     LoggerE("Cannot set empty playlist name!");
1447     return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1448   }
1449
1450   media_playlist_h playlist_handle = getPlaylistHandle(id);
1451   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1452
1453   const int ret_code = media_playlist_set_name(playlist_handle, name.c_str());
1454   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1455     LoggerE("media_playlist_set_name failed");
1456     // Setting name that is used by other playlist does not return bad error code here.
1457     // MEDIA_CONTENT_ERROR_INVALID_OPERATION is being returned in updatePlaylistInDB
1458     return TIZEN_ERROR_UNKNOWN;
1459   }
1460
1461   int ret = updatePlaylistInDB(playlist_handle);
1462   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1463     LoggerE("Error while updating playlist: %d", ret);
1464     if (MEDIA_CONTENT_ERROR_DB_FAILED == ret) {
1465       // We could fetch list of playlists and check if other playlist is using this
1466       // name, but that seems to be to much work in synchronous method
1467       LoggerE("Playlist name: %s is probably already used", name.c_str());
1468       return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1469     }
1470     return ret;
1471   }
1472   return MEDIA_CONTENT_ERROR_NONE;
1473 }
1474
1475 int ContentManager::getThumbnailUri(int id, std::string* result) {
1476   ScopeLogger();
1477   media_playlist_h playlist_handle = getPlaylistHandle(id);
1478   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1479
1480   char* tmp_playlist_thb_path = nullptr;
1481   const int ret_code = media_playlist_get_thumbnail_path(playlist_handle, &tmp_playlist_thb_path);
1482
1483   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1484     LoggerE("media_playlist_get_name failed");
1485     return TIZEN_ERROR_UNKNOWN;
1486   }
1487
1488   std::string playlist_thb_path;
1489   if (tmp_playlist_thb_path) {
1490     playlist_thb_path = tmp_playlist_thb_path;
1491     free(tmp_playlist_thb_path);
1492     tmp_playlist_thb_path = nullptr;
1493   }
1494
1495   if (playlist_thb_path != " ") {
1496     playlist_thb_path = uri_prefix + playlist_thb_path;
1497   }
1498
1499   *result = playlist_thb_path;
1500   return MEDIA_CONTENT_ERROR_NONE;
1501 }
1502
1503 int ContentManager::setThumbnailUri(int id, const std::string& thb_uri) {
1504   ScopeLogger();
1505
1506   // Allow setting empty URI, unfortunately Core API does not allow to set empty
1507   // path so we need to set one empty space. This is probably issue of Core API.
1508   if (!thb_uri.empty() && " " != thb_uri) {
1509     if (thb_uri.find(uri_absolute_prefix) != 0) {
1510       LoggerE("thumbnail URI is not valid: [%s]", thb_uri.c_str());
1511       return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1512     }
1513   }
1514
1515   media_playlist_h playlist_handle = getPlaylistHandle(id);
1516   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1517
1518   std::string real_path = common::FilesystemProvider::Create().GetRealPath(thb_uri);
1519   const int ret_code = media_playlist_set_thumbnail_path(playlist_handle, real_path.c_str());
1520   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1521     LoggerE("media_playlist_set_thumbnail_path failed");
1522     return TIZEN_ERROR_UNKNOWN;
1523   }
1524
1525   int ret = updatePlaylistInDB(playlist_handle);
1526   return ret;
1527 }
1528
1529 int ContentManager::getNumberOfTracks(int id, int* result) {
1530   ScopeLogger();
1531
1532   int count = 0;
1533   const int ret_code = media_playlist_get_media_count_from_db(id, nullptr, &count);
1534
1535   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1536     LoggerE("media_playlist_get_media_count_from_db failed");
1537     return TIZEN_ERROR_UNKNOWN;
1538   }
1539
1540   *result = count;
1541   return MEDIA_CONTENT_ERROR_NONE;
1542 }
1543
1544 common::PlatformResult ContentManager::createThumbnail(const std::string& id,
1545                                                        picojson::object* obj) {
1546   ScopeLogger();
1547
1548   media_info_h media_h = nullptr;
1549   int ret = media_info_get_media_from_db(id.c_str(), &media_h);
1550   if (MEDIA_CONTENT_ERROR_NONE != ret || nullptr == media_h) {
1551     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Getting media failed.",
1552                               ("Getting media failed: %d (%s)", ret, get_error_message(ret)));
1553   }
1554   SCOPE_EXIT {
1555     media_info_destroy(media_h);
1556   };
1557
1558   ret = media_info_generate_thumbnail(media_h);
1559   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1560     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Creating thumbnail failed.",
1561                               ("Creating thumbnail failed: %d (%s)", ret, get_error_message(ret)));
1562   }
1563
1564   char* path = nullptr;
1565   ret = media_info_get_thumbnail_path(media_h, &path);
1566   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1567     return LogAndCreateResult(
1568         ErrorCode::ABORT_ERR, "Creating thumbnail succeeded, but failed to get thumbnail path.",
1569         ("Getting thumbnail path failed: %d (%s)", ret, get_error_message(ret)));
1570   }
1571   obj->emplace("result", picojson::value(path));
1572   std::unique_ptr<char[], decltype(&free)>(path, free);
1573   return PlatformResult(ErrorCode::NO_ERROR);
1574 }
1575
1576 PlatformResult ContentManager::convertError(int err) {
1577   char* error_msg = get_error_message(err);
1578   switch (err) {
1579     case MEDIA_CONTENT_ERROR_INVALID_PARAMETER:
1580       return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, error_msg);
1581     case MEDIA_CONTENT_ERROR_OUT_OF_MEMORY:
1582       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1583     case MEDIA_CONTENT_ERROR_INVALID_OPERATION:
1584       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1585     case MEDIA_CONTENT_FILE_NO_SPACE_ON_DEVICE:
1586       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1587     case MEDIA_CONTENT_ERROR_PERMISSION_DENIED:
1588       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1589     case MEDIA_CONTENT_ERROR_DB_FAILED:
1590       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1591     case MEDIA_CONTENT_ERROR_DB_BUSY:
1592       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1593     case MEDIA_CONTENT_ERROR_NETWORK:
1594       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1595     case MEDIA_CONTENT_ERROR_UNSUPPORTED_CONTENT:
1596       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1597     default:
1598       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error.");
1599   }
1600 }
1601
1602 }  // namespace content
1603 }  // namespace extension