Merge branch 'tizen_5.0' into tizen_5.5
[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   // TODO: The function media_folder_get_storage_type is marked as deprecated since 5.0.
402   // As an alternative, it is recommended to use storage_get_type_dev function. However,
403   // this function does not work with internal storages. The media_folder_get_storage_type
404   // function should be kept or moved to internal header by Native API.
405   if (ret == MEDIA_CONTENT_ERROR_NONE) {
406     if (storage_type == MEDIA_CONTENT_STORAGE_INTERNAL) {
407       o["storageType"] = picojson::value(std::string("INTERNAL"));
408     } else {
409       LoggerD("storageType = %d, assuming EXTERNAL as storage type", storage_type);
410       o["storageType"] = picojson::value(std::string("EXTERNAL"));
411     }
412   }
413
414   // modifiedDate
415   struct stat stat_res;
416   if (stat(folder_path.c_str(), &stat_res) == 0) {
417     auto mod_time = stat_res.st_mtime;
418     o["modifiedDate"] = picojson::value(static_cast<double>(mod_time));
419   }
420 }
421
422 static int setContent(media_info_h media, const picojson::value& content) {
423   ScopeLogger();
424
425   int ret;
426   std::string name = content.get("name").to_str();
427   std::string description = content.get("description").to_str();
428   int rating = std::stoi(content.get("rating").to_str());
429   bool is_fav = content.get("isFavorite").get<bool>();
430
431   if (NULL == media) {
432     LoggerE("MEDIA_CONTENT_ERROR_DB_FAILED");
433     return MEDIA_CONTENT_ERROR_DB_FAILED;
434   }
435
436   media_content_type_e type;
437   ret = media_info_get_media_type(media, &type);
438   if (ret != MEDIA_CONTENT_ERROR_NONE) {
439     LoggerE("Failed: media_info_get_media_type()");
440     return ret;
441   }
442
443   ret = media_info_set_display_name(media, name.c_str());
444   if (ret != MEDIA_CONTENT_ERROR_NONE) {
445     LoggerE("Updating name failed.");
446   }
447
448   ret = media_info_set_description(media, description.c_str());
449   if (ret != MEDIA_CONTENT_ERROR_NONE) {
450     LoggerE("Updating description failed.");
451   }
452
453   ret = media_info_set_rating(media, rating);
454   if (ret != MEDIA_CONTENT_ERROR_NONE) {
455     LoggerE("Updating rating failed.");
456   }
457
458   ret = media_info_set_favorite(media, is_fav);
459   if (ret != MEDIA_CONTENT_ERROR_NONE) {
460     LoggerE("Updating isFavorite failed.");
461   }
462
463   if (ret != MEDIA_CONTENT_ERROR_NONE) {
464     LoggerE("Updating favorite failed.");
465   }
466
467   if (type == MEDIA_CONTENT_TYPE_IMAGE) {
468     std::string orientation = content.get("orientation").to_str();
469     auto orientationToSet = orientationMap.find(orientation);
470
471     if (orientationToSet != orientationMap.end()) {
472       image_meta_h img;
473       if (MEDIA_CONTENT_ERROR_NONE == media_info_get_image(media, &img) &&
474           MEDIA_CONTENT_ERROR_NONE == image_meta_set_orientation(img, orientationToSet->second) &&
475           MEDIA_CONTENT_ERROR_NONE == image_meta_update_to_db(img)) {
476         LoggerD("orientation update was successful");
477       } else {
478         LoggerE("orientation update failed");
479       }
480       image_meta_destroy(img);
481     }
482   }
483
484   if (type == MEDIA_CONTENT_TYPE_IMAGE || type == MEDIA_CONTENT_TYPE_VIDEO) {
485     picojson::value geo = content.get("geolocation");
486     if (geo.evaluate_as_boolean()) {
487       LoggerD("geolocation is not null");
488       double latitude = atof(geo.get("latitude").to_str().c_str());
489       double longitude = atof(geo.get("longitude").to_str().c_str());
490       ret = media_info_set_latitude(media, latitude);
491       if (ret != MEDIA_CONTENT_ERROR_NONE) {
492         LoggerE("Updating geolocation is failed.");
493       }
494       ret = media_info_set_longitude(media, longitude);
495       if (ret != MEDIA_CONTENT_ERROR_NONE) {
496         LoggerD("Updating geolocation is failed.");
497       }
498     } else {
499       LoggerD("geolocation is null");
500     }
501   }
502
503   return MEDIA_CONTENT_ERROR_NONE;
504 }
505
506 static void FolderToJson(media_folder_h folder, picojson::object* out) {
507   ScopeLogger();
508
509   char* name = NULL;
510   char* id = NULL;
511   char* path = NULL;
512   time_t date;
513   media_content_storage_e storageType;
514
515   int ret;
516
517   ret = media_folder_get_folder_id(folder, &id);
518   if (ret != MEDIA_CONTENT_ERROR_NONE) {
519     LogAndReportError(ContentManager::convertError(ret), out,
520                       ("Failed: media_folder_get_folder_id"));
521     return;
522   }
523   std::unique_ptr<char, decltype(&free)> id_ptr(id, &free);
524
525   ret = media_folder_get_name(folder, &name);
526   if (ret != MEDIA_CONTENT_ERROR_NONE) {
527     LogAndReportError(ContentManager::convertError(ret), out, ("Failed: media_folder_get_name"));
528     return;
529   }
530   std::unique_ptr<char, decltype(&free)> name_ptr(name, &free);
531
532   ret = media_folder_get_path(folder, &path);
533   if (ret != MEDIA_CONTENT_ERROR_NONE) {
534     LogAndReportError(ContentManager::convertError(ret), out, ("Failed: media_folder_get_path"));
535     return;
536   }
537   std::unique_ptr<char, decltype(&free)> path_ptr(path, &free);
538
539   struct stat stat_res;
540   ret = stat(path, &stat_res);
541   if (0 != ret) {
542     LogAndReportError(ContentManager::convertError(errno), out, ("Failed: stat"));
543     return;
544   }
545   date = stat_res.st_mtime;
546
547   ret = media_folder_get_storage_type(folder, &storageType);
548   // TODO: The function media_folder_get_storage_type is marked as deprecated since 5.0.
549   // As an alternative, it is recommended to use storage_get_type_dev function. However,
550   // this function does not work with internal storages. The media_folder_get_storage_type
551   // function should be kept or moved to internal header by Native API.
552   if (ret != MEDIA_CONTENT_ERROR_NONE) {
553     LogAndReportError(ContentManager::convertError(ret), out,
554                       ("Failed: media_folder_get_storage_type"));
555     return;
556   }
557
558   (*out)["id"] = picojson::value(std::string(id));
559   (*out)["directoryURI"] = picojson::value(std::string(path));
560   (*out)["title"] = picojson::value(std::string(name));
561
562   if (storageType == MEDIA_CONTENT_STORAGE_INTERNAL) {
563     (*out)["storageType"] = picojson::value(std::string("INTERNAL"));
564   } else if (storageType == MEDIA_CONTENT_STORAGE_EXTERNAL) {
565     (*out)["storageType"] = picojson::value(std::string("EXTERNAL"));
566   }
567
568   (*out)["modifiedDate"] = picojson::value(static_cast<double>(date));
569 }
570
571 static bool media_foreach_directory_cb(media_folder_h folder, void* user_data) {
572   ScopeLogger();
573   picojson::array* array = static_cast<picojson::array*>(user_data);
574   picojson::object json;
575   FolderToJson(folder, &json);
576   array->push_back(picojson::value(json));
577   return true;
578 }
579
580 static bool media_foreach_content_cb(media_info_h media, void* user_data) {
581   ScopeLogger();
582   picojson::value::array* contents = static_cast<picojson::value::array*>(user_data);
583   picojson::value::object o;
584   ContentToJson(media, o);
585   contents->push_back(picojson::value(o));
586   return true;
587 }
588
589 static bool playlist_foreach_cb(media_playlist_h playlist, void* user_data) {
590   ScopeLogger();
591   picojson::value::array* playlists = static_cast<picojson::value::array*>(user_data);
592   picojson::value::object o;
593   if (playlist != NULL) {
594     int id, cnt;
595     char* thumb_path = NULL;
596     char* name = NULL;
597     filter_h filter = NULL;
598     if (media_playlist_get_playlist_id(playlist, &id) == MEDIA_CONTENT_ERROR_NONE) {
599       std::stringstream str_id;
600       str_id << id;
601       o["id"] = picojson::value(std::to_string(id));
602     } else {
603       LoggerD("Invalid ID for playlist.");
604     }
605     if (media_playlist_get_thumbnail_path(playlist, &thumb_path) == MEDIA_CONTENT_ERROR_NONE) {
606       if (thumb_path != NULL) {
607         std::string thumbnail_uri(thumb_path);
608         if (thumbnail_uri != " ") {
609           thumbnail_uri = uri_prefix + thumbnail_uri;
610         }
611         o["thumbnailURI"] = picojson::value(thumbnail_uri);
612         free(thumb_path);
613       } else {
614         o["thumbnailURI"] = picojson::value();  // picojson::value(std::string(""));
615       }
616     } else {
617       LoggerD("Invalid thumbnail path for playlist.");
618     }
619     if (media_playlist_get_name(playlist, &name) == MEDIA_CONTENT_ERROR_NONE) {
620       o["name"] = picojson::value(std::string(name));
621       free(name);
622     } else {
623       LoggerD("Invalid name for playlist.");
624     }
625
626     media_filter_create(&filter);
627     std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
628         filter, &media_filter_destroy);  // automatically release the memory
629     if (media_playlist_get_media_count_from_db(id, filter, &cnt) == MEDIA_CONTENT_ERROR_NONE) {
630       o["numberOfTracks"] = picojson::value(static_cast<double>(cnt));
631     } else {
632       LoggerE("Invalid count for playlist.");
633     }
634     playlists->push_back(picojson::value(o));
635   }
636   return true;
637 }
638
639 static bool playlist_content_member_cb(int playlist_member_id, media_info_h media,
640                                        void* user_data) {
641   ScopeLogger();
642   picojson::value::array* contents = static_cast<picojson::value::array*>(user_data);
643   picojson::value::object o;
644
645   o["playlist_member_id"] = picojson::value(static_cast<double>(playlist_member_id));
646   ContentToJson(media, o);
647   contents->push_back(picojson::value(o));
648   return true;
649 }
650
651 ContentManager::ContentManager() {
652   ScopeLogger("ContentManager called");
653   if (media_content_connect() == MEDIA_CONTENT_ERROR_NONE) {
654     m_dbConnected = true;
655   } else {
656     m_dbConnected = false;
657   }
658   m_contentInstance = nullptr;
659 }
660
661 ContentManager::~ContentManager() {
662   ScopeLogger();
663   if (m_dbConnected) {
664     if (media_content_disconnect() == MEDIA_CONTENT_ERROR_NONE) {
665       m_dbConnected = false;
666     }
667   }
668 }
669
670 ContentManager* ContentManager::getInstance() {
671   ScopeLogger();
672   static ContentManager instance;
673   return &instance;
674 }
675
676 ContentInstance* ContentManager::getContentInstance() {
677   ScopeLogger();
678   return m_contentInstance;
679 }
680
681 void ContentManager::setContentInstance(ContentInstance* const content_instance) {
682   ScopeLogger();
683   m_contentInstance = content_instance;
684 }
685
686 bool ContentManager::isConnected() {
687   ScopeLogger();
688   return m_dbConnected;
689 }
690
691 void ContentManager::getDirectories(const std::shared_ptr<ReplyCallbackData>& user_data) {
692   ScopeLogger();
693   int ret;
694   filter_h filter = NULL;
695   ret = media_filter_create(&filter);
696   if (ret != MEDIA_CONTENT_ERROR_NONE) {
697     LoggerE("Failed: media_filter_create failed");
698     return;
699   }
700
701   SCOPE_EXIT {
702     media_filter_destroy(filter);
703   };
704
705   std::string condition = "(FOLDER_STORAGE_TYPE = 0 OR FOLDER_STORAGE_TYPE = 1)";
706   media_filter_set_condition(filter, condition.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
707
708   picojson::array pico_dirs;
709   ret = media_folder_foreach_folder_from_db(filter, media_foreach_directory_cb, &pico_dirs);
710   if (ret != MEDIA_CONTENT_ERROR_NONE) {
711     PlatformResult err = LogAndCreateResult(
712         ErrorCode::UNKNOWN_ERR, "Getting the directories failed.",
713         ("Failed: Getting the directories failed %d (%s)", ret, get_error_message(ret)));
714     user_data->isSuccess = err;
715     return;
716   }
717
718   user_data->result = picojson::value(pico_dirs);
719 }
720
721 void ContentManager::find(const std::shared_ptr<ReplyCallbackData>& user_data) {
722   ScopeLogger();
723
724   int ret;
725   int count, offset;
726   std::string dirId;
727
728   picojson::value::array arrayContent;
729   filter_h filter = nullptr;
730   media_filter_create(&filter);
731   SCOPE_EXIT {
732     if (filter) {
733       media_filter_destroy(filter);
734     }
735   };
736
737   if (!IsNull(user_data->args.get("filter"))) {
738     ContentFilter filterMechanism;
739     std::string query;
740     picojson::object argsObject = JsonCast<picojson::object>(user_data->args);
741     if (filterMechanism.BuildQuery(FromJson<picojson::object>(argsObject, "filter"), &query)) {
742       LoggerD("Filter query: %s", query.c_str());
743       ret = media_filter_set_condition(filter, query.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
744       if (MEDIA_CONTENT_ERROR_NONE != ret) {
745         LoggerE("Platform filter setting failed, error %d", ret);
746       }
747     }
748   }
749
750   if (user_data->args.contains("sortMode")) {
751     picojson::value vSortMode = user_data->args.get("sortMode");
752
753     if (vSortMode.is<picojson::object>()) {
754       std::string sortModeName, sortModeOrder;
755
756       ContentFilter::MapField(vSortMode.get("attributeName").to_str(), &sortModeName);
757
758       sortModeOrder = vSortMode.get("order").to_str();
759       if (!sortModeOrder.empty()) {
760         media_content_order_e order = MEDIA_CONTENT_ORDER_ASC;
761
762         if (sortModeOrder == "ASC") {
763           order = MEDIA_CONTENT_ORDER_ASC;
764         } else if (sortModeOrder == "DESC") {
765           order = MEDIA_CONTENT_ORDER_DESC;
766         }
767
768         ret = media_filter_set_order(filter, order, sortModeName.c_str(),
769                                      MEDIA_CONTENT_COLLATE_DEFAULT);
770         if (MEDIA_CONTENT_ERROR_NONE != ret) {
771           LoggerE("Platform SortMode setting failed, error: %d", ret);
772         }
773       }
774     }
775   }
776
777   if (!IsNull(user_data->args.get("count"))) {
778     count = static_cast<int>(user_data->args.get("count").get<double>());
779   } else {
780     count = -1;
781   }
782   if (!IsNull(user_data->args.get("offset"))) {
783     offset = static_cast<int>(user_data->args.get("offset").get<double>());
784   } else {
785     offset = -1;
786   }
787   ret = media_filter_set_offset(filter, offset, count);
788   if (MEDIA_CONTENT_ERROR_NONE != ret) {
789     LoggerE("A platform error occurs in media_filter_set_offset: %d", ret);
790   }
791   if (!IsNull(user_data->args.get("directoryId"))) {
792     dirId = user_data->args.get("directoryId").get<std::string>();
793     ret = media_folder_foreach_media_from_db(dirId.c_str(), filter, media_foreach_content_cb,
794                                              static_cast<void*>(&arrayContent));
795   } else {
796     ret = media_info_foreach_media_from_db(filter, media_foreach_content_cb,
797                                            static_cast<void*>(&arrayContent));
798   }
799
800   if (ret == MEDIA_CONTENT_ERROR_NONE) {
801     user_data->result = picojson::value(arrayContent);
802   } else {
803     PlatformResult err = LogAndCreateResult(
804         ErrorCode::UNKNOWN_ERR, "The iteration failed in platform",
805         ("The iteration failed in platform: %d (%s)", ret, get_error_message(ret)));
806     user_data->isSuccess = err;
807   }
808 }
809
810 int ContentManager::scanFile(std::string& uri) {
811   ScopeLogger();
812   return media_content_scan_file(uri.c_str());
813 }
814
815 PlatformResult ContentManager::scanDirectory(media_scan_completed_cb callback,
816                                              ReplyCallbackData* cbData) {
817   ScopeLogger();
818   const std::string& contentDirURI = cbData->args.get("contentDirURI").get<std::string>();
819   std::string real_path = common::FilesystemProvider::Create().GetRealPath(contentDirURI);
820   const bool recursive = cbData->args.get("recursive").get<bool>();
821
822   int ret = media_content_scan_folder(real_path.c_str(), recursive, callback, (void*)cbData);
823
824   if (ret != MEDIA_CONTENT_ERROR_NONE) {
825     if (MEDIA_CONTENT_ERROR_INVALID_PARAMETER == ret) {
826       return LogAndCreateResult(
827           ErrorCode::INVALID_VALUES_ERR, "Scanning content directory failed",
828           ("Scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
829
830     } else {
831       return LogAndCreateResult(
832           ErrorCode::UNKNOWN_ERR, "Scanning content directory failed",
833           ("Scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
834     }
835   }
836   return PlatformResult(ErrorCode::NO_ERROR);
837 }
838
839 PlatformResult ContentManager::cancelScanDirectory(const std::string& content_dir_uri) {
840   ScopeLogger();
841
842   int ret = media_content_cancel_scan_folder(content_dir_uri.c_str());
843   if (ret != MEDIA_CONTENT_ERROR_NONE) {
844     return LogAndCreateResult(
845         ErrorCode::UNKNOWN_ERR, "Cancel scan content directory failed",
846         ("Cancel scan folder failed in platform: %d (%s)", ret, get_error_message(ret)));
847   }
848   return PlatformResult(ErrorCode::NO_ERROR);
849 }
850
851 PlatformResult ContentManager::addChangeListener(media_content_noti_h* noti_handle,
852                                                  media_content_db_update_cb callback,
853                                                  void* user_data) {
854   ScopeLogger();
855
856   int ret = media_content_add_db_updated_cb(callback, user_data, noti_handle);
857
858   if (MEDIA_CONTENT_ERROR_NONE != ret) {
859     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Failed to add the listener.",
860                               ("Failed to add the listener: %d (%s)", ret, get_error_message(ret)));
861   }
862
863   return PlatformResult(ErrorCode::NO_ERROR);
864 }
865
866 PlatformResult ContentManager::removeChangeListener(media_content_noti_h noti_handle) {
867   ScopeLogger();
868
869   int ret = media_content_remove_db_updated_cb(noti_handle);
870
871   switch (ret) {
872     case MEDIA_CONTENT_ERROR_NONE:
873       return PlatformResult(ErrorCode::NO_ERROR);
874     case MEDIA_CONTENT_ERROR_INVALID_PARAMETER:
875       // Trying to remove non-existent listener, ignoring
876       LoggerI("Failed to remove the listener: %d (%s)", ret, get_error_message(ret));
877       return PlatformResult(ErrorCode::NO_ERROR);
878     default:
879       return LogAndCreateResult(
880           ErrorCode::ABORT_ERR, "Failed to remove the listener.",
881           ("Failed to remove the listener: %d (%s)", ret, get_error_message(ret)));
882   }
883 }
884
885 void ContentManager::createPlaylist(std::string name,
886                                     const std::shared_ptr<ReplyCallbackData>& user_data) {
887   ScopeLogger();
888   media_playlist_h playlist = NULL;
889
890   int ret = media_playlist_insert_to_db(name.c_str(), &playlist);
891   std::unique_ptr<std::remove_pointer<media_playlist_h>::type, int (*)(media_playlist_h)>
892       playlist_ptr(playlist, &media_playlist_destroy);  // automatically release the memory
893   if (ret != MEDIA_CONTENT_ERROR_NONE) {
894     // MEDIA_CONTENT_ERROR_DB_FAILED means that playlist probably already exists
895     PlatformResult err = LogAndCreateResult(
896         MEDIA_CONTENT_ERROR_DB_FAILED == ret ? ErrorCode::INVALID_VALUES_ERR
897                                              : ErrorCode::UNKNOWN_ERR,
898         "Creation of playlist has failed.",
899         ("Failed: creation of playlist is failed: %d (%s)", ret, get_error_message(ret)));
900     user_data->isSuccess = err;
901     return;
902   }
903   picojson::value::object o;
904
905   if (playlist != NULL) {
906     int id, cnt;
907     char* thumb_path = NULL;
908     char* name_playlist = NULL;
909     filter_h filter = NULL;
910     if (media_playlist_get_playlist_id(playlist, &id) == MEDIA_CONTENT_ERROR_NONE) {
911       o["id"] = picojson::value(std::to_string(id));
912     } else {
913       PlatformResult err =
914           LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "loading of playlist is failed.");
915       user_data->isSuccess = err;
916       return;
917     }
918     if (media_playlist_get_thumbnail_path(playlist, &thumb_path) == MEDIA_CONTENT_ERROR_NONE) {
919       if (thumb_path != NULL) {
920         o["thumbnailURI"] = picojson::value(std::string(thumb_path));
921         free(thumb_path);
922       } else {
923         o["thumbnailURI"] = picojson::value();
924       }
925     } else {
926       LoggerE("Invalid thumbnail path for playlist.");
927     }
928     if (media_playlist_get_name(playlist, &name_playlist) == MEDIA_CONTENT_ERROR_NONE) {
929       o["name"] = picojson::value(std::string(name_playlist));
930       free(name_playlist);
931     } else {
932       LoggerE("Invalid name for playlist.");
933     }
934     media_filter_create(&filter);
935     std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
936         filter, &media_filter_destroy);  // automatically release the memory
937
938     if (media_playlist_get_media_count_from_db(id, filter, &cnt) == MEDIA_CONTENT_ERROR_NONE) {
939       o["numberOfTracks"] = picojson::value(static_cast<double>(cnt));
940     } else {
941       LoggerE("Invalid count for playlist.");
942     }
943   }
944
945   user_data->result = picojson::value(o);
946 }
947
948 void ContentManager::getPlaylists(const std::shared_ptr<ReplyCallbackData>& user_data) {
949   ScopeLogger();
950   int ret;
951   filter_h filter = nullptr;
952   media_filter_create(&filter);
953   std::unique_ptr<std::remove_pointer<filter_h>::type, int (*)(filter_h)> filter_ptr(
954       filter, &media_filter_destroy);  // automatically release the memory
955   picojson::value::array playlists;
956
957   ret = media_playlist_foreach_playlist_from_db(filter, playlist_foreach_cb,
958                                                 static_cast<void*>(&playlists));
959
960   if (ret != MEDIA_CONTENT_ERROR_NONE) {
961     PlatformResult err = LogAndCreateResult(
962         ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
963         ("Failed: Getting playlist is failed %d (%s)", ret, get_error_message(ret)));
964     user_data->isSuccess = err;
965   }
966
967   user_data->result = picojson::value(playlists);
968 }
969
970 void ContentManager::removePlaylist(std::string playlistId,
971                                     const std::shared_ptr<ReplyCallbackData>& user_data) {
972   ScopeLogger();
973   int id = std::atoi(playlistId.c_str());
974   if (id == 0) {
975     PlatformResult err = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "PlaylistId is wrong.");
976     user_data->isSuccess = err;
977     return;
978   }
979
980   int ret = media_playlist_delete_from_db(id);
981   if (ret != MEDIA_CONTENT_ERROR_NONE) {
982     PlatformResult err =
983         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Removal of playlist is failed.");
984     user_data->isSuccess = err;
985   }
986 }
987
988 int ContentManager::update(const picojson::value& args) {
989   ScopeLogger();
990
991   int ret;
992   picojson::value content = args.get("content");
993   std::string id = content.get("id").to_str();
994
995   media_info_h media = NULL;
996   ret = media_info_get_media_from_db(id.c_str(), &media);
997   if (ret == MEDIA_CONTENT_ERROR_NONE) {
998     setContent(media, content);
999     ret = media_info_update_to_db(media);
1000     media_info_destroy(media);
1001   }
1002
1003   return ret;
1004 }
1005
1006 int ContentManager::updateBatch(const picojson::value& args) {
1007   ScopeLogger();
1008   int ret = 0;
1009   std::vector<picojson::value> contents = args.get("contents").get<picojson::array>();
1010
1011   for (picojson::value::array::iterator it = contents.begin(); it != contents.end(); ++it) {
1012     picojson::value content = *it;
1013     std::string id = content.get("id").to_str();
1014     media_info_h media = NULL;
1015     ret = media_info_get_media_from_db(id.c_str(), &media);
1016     if (media != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1017       ret = setContent(media, content);
1018       if (ret != MEDIA_CONTENT_ERROR_NONE) {
1019         LoggerE("setContent failed");
1020         return ret;
1021       }
1022
1023       ret = media_info_update_to_db(media);
1024       if (ret != MEDIA_CONTENT_ERROR_NONE) {
1025         LoggerE("update to db failed");
1026       }
1027       media_info_destroy(media);
1028     } else {
1029       return ret;
1030     }
1031   }
1032   return ret;
1033 }
1034
1035 int ContentManager::playlistAdd(std::string playlist_id, std::string content_id) {
1036   ScopeLogger();
1037
1038   media_playlist_h playlist = NULL;
1039   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1040
1041   if (playlist != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1042     ret = media_playlist_add_media(playlist, content_id.c_str());
1043     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1044       LoggerE("The content(id:%s) can't add to playlist", content_id.c_str());
1045     }
1046
1047     ret = media_playlist_update_to_db(playlist);
1048     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1049       LoggerE("The content(id:%s) can't add to playlist", content_id.c_str());
1050     }
1051   } else {
1052     LoggerE("Playlist(id:%s) is not exist", playlist_id.c_str());
1053   }
1054
1055   media_playlist_destroy(playlist);
1056   return ret;
1057 }
1058
1059 int ContentManager::playlistRemove(std::string playlist_id, int member_id) {
1060   ScopeLogger();
1061
1062   media_playlist_h playlist = NULL;
1063   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1064   if (playlist != NULL && ret == MEDIA_CONTENT_ERROR_NONE) {
1065     ret = media_playlist_remove_media(playlist, member_id);
1066     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1067       LoggerE("The content can't remove to playlist");
1068     }
1069
1070     ret = media_playlist_update_to_db(playlist);
1071     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1072       LoggerE("The content can't remove to playlist");
1073     }
1074   } else {
1075     LoggerE("Playlist(id:%s) is not exist", playlist_id.c_str());
1076   }
1077   media_playlist_destroy(playlist);
1078
1079   return ret;
1080 }
1081
1082 void ContentManager::playlistAddbatch(const std::shared_ptr<ReplyCallbackData>& user_data) {
1083   ScopeLogger();
1084   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1085
1086   media_playlist_h playlist = NULL;
1087   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1088
1089   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1090     PlatformResult err =
1091         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1092                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1093     user_data->isSuccess = err;
1094     return;
1095   }
1096
1097   std::vector<picojson::value> contents = user_data->args.get("contents").get<picojson::array>();
1098   for (picojson::value::array::iterator it = contents.begin(); it != contents.end(); ++it) {
1099     picojson::value content = *it;
1100     std::string id = content.get("id").to_str();
1101     ret = media_playlist_add_media(playlist, id.c_str());
1102     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1103       LoggerE("Adding Content(id:%s) is failed.", id.c_str());
1104     }
1105   }
1106
1107   ret = media_playlist_update_to_db(playlist);
1108   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1109     PlatformResult err =
1110         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Adding playlist is failed.",
1111                            ("Adding playlist is failed: %d (%s)", ret, get_error_message(ret)));
1112     user_data->isSuccess = err;
1113   }
1114   media_playlist_destroy(playlist);
1115 }
1116
1117 void ContentManager::playlistGet(const std::shared_ptr<ReplyCallbackData>& user_data) {
1118   ScopeLogger();
1119   media_playlist_h playlist = NULL;
1120   media_content_order_e order = MEDIA_CONTENT_ORDER_ASC;
1121   const std::string playOrder("playlist_member_order");
1122
1123   SCOPE_EXIT {
1124     if (playlist) {
1125       media_playlist_destroy(playlist);
1126     }
1127   };
1128
1129   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1130   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1131   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1132     PlatformResult err =
1133         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1134                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1135     user_data->isSuccess = err;
1136     return;
1137   }
1138
1139   filter_h filter = NULL;
1140   ret = media_filter_create(&filter);
1141   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1142     PlatformResult err =
1143         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Creating a filter is failed.",
1144                            ("Creating a filter is failed: %d (%s)", ret, get_error_message(ret)));
1145     user_data->isSuccess = err;
1146     return;
1147   }
1148
1149   int count = user_data->args.get("count").get<double>();
1150   int offset = user_data->args.get("offset").get<double>();
1151   ret = media_filter_set_offset(filter, offset, count);
1152   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1153     LoggerD("Setting a offset/count is failed.");
1154   }
1155   ret = media_filter_set_order(filter, order, playOrder.c_str(), MEDIA_CONTENT_COLLATE_DEFAULT);
1156   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1157     LoggerD("Setting a offset/count is failed.");
1158   }
1159
1160   picojson::value::array arrayContent;
1161   ret = media_playlist_foreach_media_from_db(std::stoi(playlist_id), filter,
1162                                              playlist_content_member_cb,
1163                                              static_cast<void*>(&arrayContent));
1164
1165   media_filter_destroy(filter);
1166   if (ret == MEDIA_CONTENT_ERROR_NONE) {
1167     user_data->result = picojson::value(arrayContent);
1168   } else {
1169     PlatformResult err =
1170         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Creating a filter is failed.",
1171                            ("Creating a filter is failed: %d (%s)", ret, get_error_message(ret)));
1172     user_data->isSuccess = err;
1173   }
1174 }
1175
1176 void ContentManager::playlistRemovebatch(const std::shared_ptr<ReplyCallbackData>& user_data) {
1177   ScopeLogger();
1178   media_playlist_h playlist = NULL;
1179
1180   SCOPE_EXIT {
1181     if (playlist) {
1182       media_playlist_destroy(playlist);
1183     }
1184   };
1185
1186   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1187   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1188   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1189     PlatformResult err =
1190         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1191                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1192     user_data->isSuccess = err;
1193     return;
1194   }
1195
1196   std::vector<picojson::value> members = user_data->args.get("members").get<picojson::array>();
1197   std::size_t members_size = members.size();
1198   for (std::size_t i = 0; i < members_size; ++i) {
1199     int member_id = static_cast<int>(members.at(i).get<double>());
1200     ret = media_playlist_remove_media(playlist, member_id);
1201
1202     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1203       LoggerD("Removing a content is failed.");
1204     }
1205   }
1206
1207   ret = media_playlist_update_to_db(playlist);
1208   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1209     PlatformResult err = LogAndCreateResult(
1210         ErrorCode::UNKNOWN_ERR, "Removing the contents is failed.",
1211         ("Removing the contents is failed: %d (%s)", ret, get_error_message(ret)));
1212     user_data->isSuccess = err;
1213   }
1214 }
1215
1216 void ContentManager::playlistSetOrder(const std::shared_ptr<ReplyCallbackData>& user_data) {
1217   ScopeLogger();
1218   media_playlist_h playlist = NULL;
1219
1220   SCOPE_EXIT {
1221     if (playlist) {
1222       media_playlist_destroy(playlist);
1223     }
1224   };
1225
1226   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1227   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1228   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1229     PlatformResult err =
1230         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1231                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1232     user_data->isSuccess = err;
1233     return;
1234   }
1235
1236   int cnt;
1237   std::vector<picojson::value> members = user_data->args.get("members").get<picojson::array>();
1238
1239   ret = media_playlist_get_media_count_from_db(std::stoi(playlist_id), NULL, &cnt);
1240   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1241     LoggerE("Failed: media_playlist_get_media_count_from_db");
1242     PlatformResult err = convertError(ret);
1243     user_data->isSuccess = err;
1244     return;
1245   }
1246   std::size_t members_size = members.size();
1247   if (cnt < 0 || static_cast<size_t>(cnt) != members_size) {
1248     PlatformResult err = LogAndCreateResult(
1249         ErrorCode::INVALID_VALUES_ERR,
1250         "The items array does not contain all items from the playlist.",
1251         ("Failed: The items array does not contain all items from the playlist: %d (%s)", ret,
1252          get_error_message(ret)));
1253     user_data->isSuccess = err;
1254     return;
1255   }
1256
1257   for (std::size_t i = 0; i < members_size; ++i) {
1258     int member_id = static_cast<int>(members.at(i).get<double>());
1259     ret = media_playlist_set_play_order(playlist, member_id, i);
1260     if (ret != MEDIA_CONTENT_ERROR_NONE) {
1261       LoggerD("Removing a content is failed.");
1262     }
1263   }
1264
1265   ret = media_playlist_update_to_db(playlist);
1266   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1267     PlatformResult err = LogAndCreateResult(
1268         ErrorCode::UNKNOWN_ERR, "Removing the contents is failed.",
1269         ("Removing the contents is failed: %d (%s)", ret, get_error_message(ret)));
1270     user_data->isSuccess = err;
1271   }
1272 }
1273
1274 void ContentManager::playlistMove(const std::shared_ptr<ReplyCallbackData>& user_data) {
1275   ScopeLogger();
1276   media_playlist_h playlist = NULL;
1277
1278   SCOPE_EXIT {
1279     if (playlist) {
1280       media_playlist_destroy(playlist);
1281     }
1282   };
1283
1284   std::string playlist_id = user_data->args.get("playlistId").get<std::string>();
1285   int ret = media_playlist_get_playlist_from_db(std::stoi(playlist_id), &playlist);
1286   if (ret != MEDIA_CONTENT_ERROR_NONE && playlist == NULL) {
1287     PlatformResult err =
1288         LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Getting playlist is failed.",
1289                            ("Getting playlist is failed: %d (%s)", ret, get_error_message(ret)));
1290     user_data->isSuccess = err;
1291     return;
1292   }
1293   int old_order;
1294   double member_id = user_data->args.get("memberId").get<double>();
1295   double delta = user_data->args.get("delta").get<double>();
1296   ret = media_playlist_get_play_order(playlist, static_cast<int>(member_id), &old_order);
1297   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1298     PlatformResult err = LogAndCreateResult(
1299         ErrorCode::UNKNOWN_ERR, "The content can't find form playlist.",
1300         ("The content can't find form playlist: %d (%s)", ret, get_error_message(ret)));
1301     user_data->isSuccess = err;
1302     return;
1303   }
1304   int new_order = static_cast<int>(old_order) + static_cast<int>(delta);
1305   ret = media_playlist_set_play_order(playlist, static_cast<int>(member_id), new_order);
1306   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1307     PlatformResult err = LogAndCreateResult(
1308         ErrorCode::UNKNOWN_ERR, "The content can't update play_order.",
1309         ("The content can't update play_order: %d (%s)", ret, get_error_message(ret)));
1310     user_data->isSuccess = err;
1311     return;
1312   }
1313   ret = media_playlist_update_to_db(playlist);
1314   if (ret != MEDIA_CONTENT_ERROR_NONE) {
1315     PlatformResult err = LogAndCreateResult(
1316         ErrorCode::UNKNOWN_ERR, "Updateing play_order is failed.",
1317         ("Updateing play_order is failed: %d (%s)", ret, get_error_message(ret)));
1318     user_data->isSuccess = err;
1319   }
1320 }
1321
1322 int ContentManager::getLyrics(const picojson::value& args, picojson::object& result) {
1323   ScopeLogger();
1324
1325   int ret = METADATA_EXTRACTOR_ERROR_NONE;
1326   const std::string& contentURI = args.get("contentURI").to_str();
1327   if (contentURI.empty()) {
1328     LoggerE("contentURI empty - skipping media extractor");
1329     return -1;
1330   }
1331
1332   metadata_extractor_h extractor;
1333   ret = metadata_extractor_create(&extractor);
1334   if (METADATA_EXTRACTOR_ERROR_NONE != ret) {
1335     LoggerE("metadata_extractor_create failed, error: %d", ret);
1336   }
1337   std::unique_ptr<std::remove_pointer<metadata_extractor_h>::type, int (*)(metadata_extractor_h)>
1338       extractor_ptr(extractor, &metadata_extractor_destroy);  // automatically release the memory
1339
1340   ret = metadata_extractor_set_path(extractor, contentURI.c_str());
1341   if (ret != METADATA_EXTRACTOR_ERROR_NONE) {
1342     LoggerE("metadata_extractor_set_path failed, error: %d", ret);
1343     return ret;
1344   }
1345   picojson::array timestamps;
1346   picojson::array texts = picojson::array();
1347   char* strSyncTextNum = NULL;
1348
1349   ret = metadata_extractor_get_metadata(extractor, METADATA_SYNCLYRICS_NUM, &strSyncTextNum);
1350   if (ret != METADATA_EXTRACTOR_ERROR_NONE) {
1351     LoggerE("Media extractor error %d", ret);
1352     return ret;
1353   }
1354
1355   int nSyncTextNum = 0;
1356   if (strSyncTextNum) {
1357     nSyncTextNum = atoi(strSyncTextNum);
1358     free(strSyncTextNum);
1359     strSyncTextNum = NULL;
1360   }
1361   if (nSyncTextNum > 0) {
1362     result["type"] = picojson::value(std::string("SYNCHRONIZED"));
1363     for (int i = 0; i < nSyncTextNum; i++) {
1364       unsigned long time_info = 0;
1365       char* lyrics = NULL;
1366       ret = metadata_extractor_get_synclyrics(extractor, i, &time_info, &lyrics);
1367       if (ret == METADATA_EXTRACTOR_ERROR_NONE) {
1368         timestamps.push_back(picojson::value(static_cast<double>(time_info)));
1369         texts.push_back(picojson::value(std::string(lyrics)));
1370         free(lyrics);
1371       }
1372     }
1373     result["texts"] = picojson::value(texts);
1374     result["timestamps"] = picojson::value(timestamps);
1375     ret = METADATA_EXTRACTOR_ERROR_NONE;
1376   } else {
1377     char* unSyncText = nullptr;
1378     ret = metadata_extractor_get_metadata(extractor, METADATA_UNSYNCLYRICS, &unSyncText);
1379     if (ret == METADATA_EXTRACTOR_ERROR_NONE) {
1380       result["type"] = picojson::value(std::string("UNSYNCHRONIZED"));
1381       if (nullptr == unSyncText) {
1382         LoggerE("Unsynchronized lyrics text is NULL");
1383       }
1384       texts.push_back(picojson::value(unSyncText ? unSyncText : ""));
1385       result["texts"] = picojson::value(texts);
1386       free(unSyncText);
1387     }
1388   }
1389
1390   return ret;
1391 }
1392
1393 media_playlist_h getPlaylistHandle(int id) {
1394   ScopeLogger();
1395   media_playlist_h playlist_handle = nullptr;
1396   int ret_code = media_playlist_get_playlist_from_db(id, &playlist_handle);
1397   if (MEDIA_CONTENT_ERROR_NONE != ret_code || playlist_handle == nullptr) {
1398     LoggerE("could not get playlist handle for id: %d", id);
1399     return nullptr;
1400   }
1401
1402   return playlist_handle;
1403 }
1404
1405 void destroyMediaPlaylistHandle(media_playlist_h& playlist_handle) {
1406   ScopeLogger();
1407   if (playlist_handle) {
1408     int ret_code = media_playlist_destroy(playlist_handle);
1409     playlist_handle = nullptr;
1410
1411     if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1412       LoggerE("media_playlist_destroy failed");
1413     }
1414   }
1415 }
1416
1417 int ContentManager::getPlaylistName(int id, std::string* result) {
1418   ScopeLogger();
1419   media_playlist_h playlist_handle = getPlaylistHandle(id);
1420   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1421
1422   char* tmp_playlist_name = nullptr;
1423   const int ret_code = media_playlist_get_name(playlist_handle, &tmp_playlist_name);
1424
1425   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1426     LoggerE("media_playlist_get_name failed");
1427     return TIZEN_ERROR_UNKNOWN;
1428   }
1429
1430   std::string playlist_name;
1431   if (tmp_playlist_name) {
1432     playlist_name = tmp_playlist_name;
1433     free(tmp_playlist_name);
1434     tmp_playlist_name = nullptr;
1435   }
1436
1437   *result = playlist_name;
1438   return MEDIA_CONTENT_ERROR_NONE;
1439 }
1440
1441 int updatePlaylistInDB(media_playlist_h playlist_handle) {
1442   ScopeLogger();
1443   int ret_code = media_playlist_update_to_db(playlist_handle);
1444   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1445     LoggerE("media_playlist_update_to_db failed");
1446     return ret_code;
1447   }
1448   return MEDIA_CONTENT_ERROR_NONE;
1449 }
1450
1451 int ContentManager::setPlaylistName(int id, const std::string& name) {
1452   ScopeLogger();
1453   if (name.empty()) {
1454     LoggerE("Cannot set empty playlist name!");
1455     return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1456   }
1457
1458   media_playlist_h playlist_handle = getPlaylistHandle(id);
1459   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1460
1461   const int ret_code = media_playlist_set_name(playlist_handle, name.c_str());
1462   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1463     LoggerE("media_playlist_set_name failed");
1464     // Setting name that is used by other playlist does not return bad error code here.
1465     // MEDIA_CONTENT_ERROR_INVALID_OPERATION is being returned in updatePlaylistInDB
1466     return TIZEN_ERROR_UNKNOWN;
1467   }
1468
1469   int ret = updatePlaylistInDB(playlist_handle);
1470   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1471     LoggerE("Error while updating playlist: %d", ret);
1472     if (MEDIA_CONTENT_ERROR_DB_FAILED == ret) {
1473       // We could fetch list of playlists and check if other playlist is using this
1474       // name, but that seems to be to much work in synchronous method
1475       LoggerE("Playlist name: %s is probably already used", name.c_str());
1476       return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1477     }
1478     return ret;
1479   }
1480   return MEDIA_CONTENT_ERROR_NONE;
1481 }
1482
1483 int ContentManager::getThumbnailUri(int id, std::string* result) {
1484   ScopeLogger();
1485   media_playlist_h playlist_handle = getPlaylistHandle(id);
1486   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1487
1488   char* tmp_playlist_thb_path = nullptr;
1489   const int ret_code = media_playlist_get_thumbnail_path(playlist_handle, &tmp_playlist_thb_path);
1490
1491   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1492     LoggerE("media_playlist_get_name failed");
1493     return TIZEN_ERROR_UNKNOWN;
1494   }
1495
1496   std::string playlist_thb_path;
1497   if (tmp_playlist_thb_path) {
1498     playlist_thb_path = tmp_playlist_thb_path;
1499     free(tmp_playlist_thb_path);
1500     tmp_playlist_thb_path = nullptr;
1501   }
1502
1503   if (playlist_thb_path != " ") {
1504     playlist_thb_path = uri_prefix + playlist_thb_path;
1505   }
1506
1507   *result = playlist_thb_path;
1508   return MEDIA_CONTENT_ERROR_NONE;
1509 }
1510
1511 int ContentManager::setThumbnailUri(int id, const std::string& thb_uri) {
1512   ScopeLogger();
1513
1514   // Allow setting empty URI, unfortunately Core API does not allow to set empty
1515   // path so we need to set one empty space. This is probably issue of Core API.
1516   if (!thb_uri.empty() && " " != thb_uri) {
1517     if (thb_uri.find(uri_absolute_prefix) != 0) {
1518       LoggerE("thumbnail URI is not valid: [%s]", thb_uri.c_str());
1519       return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
1520     }
1521   }
1522
1523   media_playlist_h playlist_handle = getPlaylistHandle(id);
1524   PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
1525
1526   std::string real_path = common::FilesystemProvider::Create().GetRealPath(thb_uri);
1527   const int ret_code = media_playlist_set_thumbnail_path(playlist_handle, real_path.c_str());
1528   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1529     LoggerE("media_playlist_set_thumbnail_path failed");
1530     return TIZEN_ERROR_UNKNOWN;
1531   }
1532
1533   int ret = updatePlaylistInDB(playlist_handle);
1534   return ret;
1535 }
1536
1537 int ContentManager::getNumberOfTracks(int id, int* result) {
1538   ScopeLogger();
1539
1540   int count = 0;
1541   const int ret_code = media_playlist_get_media_count_from_db(id, nullptr, &count);
1542
1543   if (MEDIA_CONTENT_ERROR_NONE != ret_code) {
1544     LoggerE("media_playlist_get_media_count_from_db failed");
1545     return TIZEN_ERROR_UNKNOWN;
1546   }
1547
1548   *result = count;
1549   return MEDIA_CONTENT_ERROR_NONE;
1550 }
1551
1552 common::PlatformResult ContentManager::createThumbnail(const std::string& id,
1553                                                        picojson::object* obj) {
1554   ScopeLogger();
1555
1556   media_info_h media_h = nullptr;
1557   int ret = media_info_get_media_from_db(id.c_str(), &media_h);
1558   if (MEDIA_CONTENT_ERROR_NONE != ret || nullptr == media_h) {
1559     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Getting media failed.",
1560                               ("Getting media failed: %d (%s)", ret, get_error_message(ret)));
1561   }
1562   SCOPE_EXIT {
1563     media_info_destroy(media_h);
1564   };
1565
1566   ret = media_info_generate_thumbnail(media_h);
1567   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1568     return LogAndCreateResult(ErrorCode::ABORT_ERR, "Creating thumbnail failed.",
1569                               ("Creating thumbnail failed: %d (%s)", ret, get_error_message(ret)));
1570   }
1571
1572   char* path = nullptr;
1573   ret = media_info_get_thumbnail_path(media_h, &path);
1574   if (MEDIA_CONTENT_ERROR_NONE != ret) {
1575     return LogAndCreateResult(
1576         ErrorCode::ABORT_ERR, "Creating thumbnail succeeded, but failed to get thumbnail path.",
1577         ("Getting thumbnail path failed: %d (%s)", ret, get_error_message(ret)));
1578   }
1579   obj->emplace("result", picojson::value(path));
1580   std::unique_ptr<char[], decltype(&free)>(path, free);
1581   return PlatformResult(ErrorCode::NO_ERROR);
1582 }
1583
1584 PlatformResult ContentManager::convertError(int err) {
1585   char* error_msg = get_error_message(err);
1586   switch (err) {
1587     case MEDIA_CONTENT_ERROR_INVALID_PARAMETER:
1588       return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, error_msg);
1589     case MEDIA_CONTENT_ERROR_OUT_OF_MEMORY:
1590       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1591     case MEDIA_CONTENT_ERROR_INVALID_OPERATION:
1592       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1593     case MEDIA_CONTENT_FILE_NO_SPACE_ON_DEVICE:
1594       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1595     case MEDIA_CONTENT_ERROR_PERMISSION_DENIED:
1596       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1597     case MEDIA_CONTENT_ERROR_DB_FAILED:
1598       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1599     case MEDIA_CONTENT_ERROR_DB_BUSY:
1600       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1601     case MEDIA_CONTENT_ERROR_NETWORK:
1602       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1603     case MEDIA_CONTENT_ERROR_UNSUPPORTED_CONTENT:
1604       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, error_msg);
1605     default:
1606       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error.");
1607   }
1608 }
1609
1610 }  // namespace content
1611 }  // namespace extension