Rollback removePicture in ogg
[platform/core/api/metadata-editor.git] / src / metadata_editor.cpp
1 /*
2 * Copyright (c) 2013 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 <metadata_editor.h>
18 #include <metadata_editor_private.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <glib.h>
24
25 #include <taglib/tag.h>
26 #include <taglib/fileref.h>
27 #include <taglib/tpropertymap.h>
28 #include <taglib/mpegfile.h>
29 #include <taglib/mp4file.h>
30 #include <taglib/flacfile.h>
31 #include <taglib/oggflacfile.h>
32 #include <taglib/vorbisfile.h>
33 #include <taglib/wavfile.h>
34 #include <taglib/attachedpictureframe.h>
35 #include <taglib/tfilestream.h>
36
37 using namespace TagLib;
38
39 #define MIME_TYPE_JPEG "image/jpeg"
40 #define MIME_TYPE_PNG "image/png"
41
42 static String __get_file_ext(const char *file_path)
43 {
44         String s = String(file_path, String::UTF8);
45         const int pos = s.rfind(".");
46         if (pos != -1)
47                 return s.substr(pos + 1).upper();
48         return "";
49 }
50
51 static const char * __get_picture_type(const char *path)
52 {
53         String ext = __get_file_ext(path);
54         if (ext == "JPG" || ext == "JPEG")
55                 return MIME_TYPE_JPEG;
56         if (ext == "PNG")
57                 return MIME_TYPE_PNG;
58
59         return "";
60 }
61
62 class PictureFrame
63 {
64 public:
65         explicit PictureFrame(const char *path)
66                 : __stream(path, true) {}
67         virtual ~PictureFrame() = default;
68
69         ByteVector data() {
70                 return __stream.readBlock(__stream.length());
71         }
72
73         String mime() const {
74                 return __get_picture_type(__stream.name());
75         }
76
77         bool opened() const {
78                 return __stream.isOpen();
79         }
80
81         MP4::CoverArt::Format mp4format() const {
82                 if (mime() == MIME_TYPE_JPEG)
83                         return MP4::CoverArt::JPEG;
84                 if (mime() == MIME_TYPE_PNG)
85                         return MP4::CoverArt::PNG;
86                 return MP4::CoverArt::Unknown;
87         }
88
89 private:
90         FileStream __stream;
91 };
92
93 template <class T>
94 static bool __is_valid_index(const List<T>& lst, int index)
95 {
96         ME_RETVM_IF(lst.isEmpty(), false, "No picture");
97         ME_RETVM_IF((index < 0) || (lst.size() <= static_cast<uint>(index)),
98                                 false, "Out of range:size[%d] index[%d]", lst.size(), index);
99         return true;
100 }
101
102 static int __get_APIC(ID3v2::Tag *tag, int index, void **picture, int *size, char **mime_type)
103 {
104         ME_RETVM_IF(!tag, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Tag does not exist");
105
106         auto lst = tag->frameListMap()["APIC"];
107         ME_RETV_IF(!__is_valid_index(lst, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
108
109         auto pictureFrame = static_cast<ID3v2::AttachedPictureFrame*>(lst[index]);
110
111         uint pictureSize = pictureFrame->picture().size();
112         ME_RETVM_IF(pictureSize == 0, METADATA_EDITOR_ERROR_OPERATION_FAILED, "Size of picture is 0");
113
114         *picture = g_memdup2(pictureFrame->picture().data(), pictureSize);
115         *size = pictureSize;
116         *mime_type = g_strdup(pictureFrame->mimeType().toCString());
117
118         return METADATA_EDITOR_ERROR_NONE;
119 }
120
121 static int __get_flac_picture(List<FLAC::Picture *> lst, int index, void **picture, int *size, char **mime_type)
122 {
123         ME_RETV_IF(!__is_valid_index(lst, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
124
125         auto pictureFrame = static_cast<FLAC::Picture*>(lst[index]);
126         int pictureSize = pictureFrame->data().size();
127         ME_RETVM_IF(pictureSize == 0, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Size of picture is 0");
128
129         *picture = g_memdup2(pictureFrame->data().data(), pictureSize);
130         *size = pictureSize;
131         *mime_type = g_strdup(pictureFrame->mimeType().toCString());
132
133         return METADATA_EDITOR_ERROR_NONE;
134 }
135
136 static int __append_APIC(ID3v2::Tag *tag, PictureFrame& pic)
137 {
138         ME_RETVM_IF(!tag, METADATA_EDITOR_ERROR_METADATA_UPDATE_NOT_POSSIBLE, "Invalid ID3v2");
139
140         auto pictureFrame = new ID3v2::AttachedPictureFrame();
141         pictureFrame->setPicture(pic.data());
142         pictureFrame->setMimeType(pic.mime());
143
144         tag->addFrame(pictureFrame);
145
146         return METADATA_EDITOR_ERROR_NONE;
147 }
148
149 static int __append_ogg_picture(Ogg::XiphComment *xtag, PictureFrame& pic)
150 {
151         ME_RETVM_IF(!xtag, METADATA_EDITOR_ERROR_METADATA_UPDATE_NOT_POSSIBLE, "Invalid XiphComment");
152
153         auto frontCover = new FLAC::Picture;
154         frontCover->setData(pic.data());
155         frontCover->setMimeType(pic.mime());
156
157         xtag->addPicture(frontCover);
158
159         return METADATA_EDITOR_ERROR_NONE;
160 }
161
162 static int __remove_APIC(ID3v2::Tag *tag, int index)
163 {
164         ME_RETVM_IF(!tag, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid ID3v2");
165
166         auto lst = tag->frameListMap()["APIC"];
167         ME_RETV_IF(!__is_valid_index(lst, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
168
169         tag->removeFrame(lst[index]);
170
171         return METADATA_EDITOR_ERROR_NONE;
172 }
173
174 static int __remove_ogg_picture(Ogg::XiphComment *xtag, int index)
175 {
176         ME_RETVM_IF(!xtag, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid XiphComment");
177         ME_RETV_IF(!__is_valid_index(xtag->pictureList(), index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
178
179         /* xiphComment::removePicture works abnormally. Do not modify this fuction.
180                 - Use xtag->pictureList()[index] : crashed
181                 - Use copied xtag->pictureList()[index] : crashed
182                 - Use iterator with std::next() : heap-use-after-free occured
183         */
184         List<FLAC::Picture *>::Iterator it = xtag->pictureList().begin();
185         std::advance(it, index);
186
187         /* No need to set it to 'true'. taglib sets auto-delete. */
188         xtag->removePicture(*it, false);
189
190         return METADATA_EDITOR_ERROR_NONE;
191 }
192
193 class IAlbumArt
194 {
195 public:
196         virtual ~IAlbumArt() = default;
197
198         virtual int append(PictureFrame &pic) = 0;
199         virtual int remove(int index) = 0;
200         virtual int read(int index, void **picture, int *size, char **mime_type) = 0;
201         virtual uint count() = 0;
202 };
203
204 class Mp3AlbumArt : public IAlbumArt {
205 public:
206         explicit Mp3AlbumArt(FileRef *fileref)
207                 : __file(dynamic_cast<MPEG::File *>(fileref->file())) {}
208         ~Mp3AlbumArt() override = default;
209
210         int append(PictureFrame &pic) override {
211                 return __append_APIC(__file->ID3v2Tag(true), pic);
212         }
213
214         int remove(int index) override {
215                 return __remove_APIC(__file->ID3v2Tag(), index);
216         }
217
218         int read(int index, void **picture, int *size, char **mime_type) override {
219                 return __get_APIC(__file->ID3v2Tag(), index, picture, size, mime_type);
220         }
221
222         uint count() override {
223                 return __file->ID3v2Tag() ? __file->ID3v2Tag()->frameListMap()["APIC"].size() : 0;
224         }
225
226 private:
227         MPEG::File *__file;
228 };
229
230 class Mp4AlbumArt : public IAlbumArt {
231 public:
232         explicit Mp4AlbumArt(FileRef *fileref)
233                 : __file(dynamic_cast<MP4::File *>(fileref->file())) {
234                         if (__file && __file->tag()->contains("covr"))
235                                 __coverList = __file->tag()->item("covr").toCoverArtList();
236                 }
237         ~Mp4AlbumArt() override = default;
238
239         int append(PictureFrame &pic) override {
240                 __coverList.append(MP4::CoverArt(pic.mp4format(), pic.data()));
241                 __file->tag()->setItem("covr", __coverList);
242
243                 return METADATA_EDITOR_ERROR_NONE;
244         }
245
246         int remove(int index) override {
247                 ME_RETV_IF(!__is_valid_index(__coverList, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
248
249                 __coverList.erase(std::next(__coverList.begin(), index));
250                 __file->tag()->setItem("covr", __coverList);
251
252                 return METADATA_EDITOR_ERROR_NONE;
253         }
254
255         int read(int index, void **picture, int *size, char **mime_type) override {
256                 ME_RETV_IF(!__is_valid_index(__coverList, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
257
258                 auto coverArt = static_cast<MP4::CoverArt>(__coverList[index]);
259                 int coverArtSize = coverArt.data().size();
260                 ME_RETVM_IF(coverArtSize == 0, METADATA_EDITOR_ERROR_OPERATION_FAILED, "Size of picture is 0");
261
262                 *picture = g_memdup2(coverArt.data().data(), coverArtSize);
263                 *size = coverArtSize;
264                 if (coverArt.format() == MP4::CoverArt::JPEG)
265                         *mime_type = g_strdup(MIME_TYPE_JPEG);
266                 else if (coverArt.format() == MP4::CoverArt::PNG)
267                         *mime_type = g_strdup(MIME_TYPE_PNG);
268
269                 return METADATA_EDITOR_ERROR_NONE;
270         }
271
272         uint count() override {
273                 return __coverList.size();
274         }
275
276 private:
277         MP4::File *__file;
278         MP4::CoverArtList __coverList;
279 };
280
281 class FlacAlbumArt : public IAlbumArt {
282 public:
283         explicit FlacAlbumArt(FileRef *fileref)
284                 : __file(dynamic_cast<FLAC::File *>(fileref->file())) {}
285         ~FlacAlbumArt() override = default;
286
287         int append(PictureFrame &pic) override {
288                 auto frontCover = new FLAC::Picture;
289                 frontCover->setData(pic.data());
290                 frontCover->setMimeType(pic.mime());
291                 __file->addPicture(frontCover);
292                 return METADATA_EDITOR_ERROR_NONE;
293         }
294
295         int remove(int index) override {
296                 auto lst = __file->pictureList();
297                 ME_RETV_IF(!__is_valid_index(lst, index), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
298
299                 /* No need to set it to 'true'. taglib sets auto-delete. */
300                 __file->removePicture(lst[index], false);
301                 return METADATA_EDITOR_ERROR_NONE;
302         }
303
304         int read(int index, void **picture, int *size, char **mime_type) override {
305                 return __get_flac_picture(__file->pictureList(), index, picture, size, mime_type);
306         }
307
308         uint count() override {
309                 return __file->pictureList().size();
310         }
311
312 private:
313         FLAC::File *__file;
314 };
315
316 class OggVorbisAlbumArt : public IAlbumArt {
317 public:
318         explicit OggVorbisAlbumArt(FileRef *fileref)
319                 : __file(dynamic_cast<Ogg::Vorbis::File *>(fileref->file())) {}
320         ~OggVorbisAlbumArt() override = default;
321
322         int append(PictureFrame &pic) override {
323                 return __append_ogg_picture(__file->tag(), pic);
324         }
325
326         int remove(int index) override {
327                 return __remove_ogg_picture(__file->tag(), index);
328         }
329
330         int read(int index, void **picture, int *size, char **mime_type) override {
331                 ME_RETVM_IF(!__file->tag(), METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid XiphComment");
332                 return __get_flac_picture(__file->tag()->pictureList(), index, picture, size, mime_type);
333         }
334
335         uint count() override {
336                 return __file->tag() ? __file->tag()->pictureList().size() : 0;
337         }
338
339 private:
340         Ogg::Vorbis::File *__file;
341 };
342
343 class OggFlacAlbumArt : public IAlbumArt {
344 public:
345         explicit OggFlacAlbumArt(FileRef *fileref)
346                 : __file(dynamic_cast<Ogg::FLAC::File *>(fileref->file())) {}
347         ~OggFlacAlbumArt() override = default;
348
349         int append(PictureFrame &pic) override {
350                 return __append_ogg_picture(__file->tag(), pic);
351         }
352
353         int remove(int index) override {
354                 return __remove_ogg_picture(__file->tag(), index);
355         }
356
357         int read(int index, void **picture, int *size, char **mime_type) override {
358                 ME_RETVM_IF(!__file->tag(), METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid XiphComment");
359                 return __get_flac_picture(__file->tag()->pictureList(), index, picture, size, mime_type);
360         }
361
362         uint count() override {
363                 return __file->tag() ? __file->tag()->pictureList().size() : 0;
364         }
365
366 private:
367         Ogg::FLAC::File *__file;
368 };
369
370 class WavAlbumArt : public IAlbumArt {
371 public:
372         explicit WavAlbumArt(FileRef *fileref)
373                 : __file(dynamic_cast<RIFF::WAV::File *>(fileref->file())) {}
374         ~WavAlbumArt() override = default;
375
376         int append(PictureFrame &pic) override {
377                 return __append_APIC(__file->tag(), pic);
378         }
379
380         int remove(int index) override {
381                 return __remove_APIC(__file->tag(), index);
382         }
383
384         int read(int index, void **picture, int *size, char **mime_type) override {
385                 return __get_APIC(__file->tag(), index, picture, size, mime_type);
386         }
387
388         uint count() override {
389                 return __file->tag() ? __file->tag()->frameListMap()["APIC"].size() : 0;
390         }
391
392 private:
393         RIFF::WAV::File *__file;
394 };
395
396 class AlbumArtFactory {
397 public:
398         static IAlbumArt* create(FileRef *fref) {
399                 if (fref == nullptr)
400                         return nullptr;
401
402                 if (dynamic_cast<MPEG::File *>(fref->file()))
403                         return new Mp3AlbumArt(fref);
404                 if (dynamic_cast<MP4::File *>(fref->file()))
405                         return new Mp4AlbumArt(fref);
406                 if (dynamic_cast<FLAC::File *>(fref->file()))
407                         return new FlacAlbumArt(fref);
408                 if (dynamic_cast<RIFF::WAV::File *>(fref->file()))
409                         return new WavAlbumArt(fref);
410                 if (dynamic_cast<Ogg::Vorbis::File *>(fref->file()))
411                         return new OggVorbisAlbumArt(fref);
412                 if (dynamic_cast<Ogg::FLAC::File *>(fref->file()))
413                         return new OggFlacAlbumArt(fref);
414
415                 return nullptr;
416         }
417 };
418
419 typedef struct _metadata_editor_s {
420         FileRef *fref;
421         IAlbumArt *ifart;
422 } metadata_editor_s;
423
424 static int __check_file_validity(const char *path)
425 {
426         ME_RETVM_IF(!path, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid path");
427         ME_SDBG("path : [%s]", path);
428
429         if (access(path, R_OK) < 0) {
430                 if (errno == EACCES || errno == EPERM) {
431                         ME_ERR("Permission denied");
432                         return METADATA_EDITOR_ERROR_PERMISSION_DENIED;
433                 }
434
435                 ME_ERR("Fail to open path");
436                 return METADATA_EDITOR_ERROR_FILE_EXISTS;
437         }
438
439         return METADATA_EDITOR_ERROR_NONE;
440 }
441
442 static int __is_valid_handle(metadata_editor_s *metadata, bool isWritable)
443 {
444         ME_RETVM_IF(!metadata, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid metadata");
445         ME_RETVM_IF(!metadata->fref, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "File loading fail");
446         ME_RETVM_IF(isWritable && metadata->fref->file()->readOnly(), METADATA_EDITOR_ERROR_PERMISSION_DENIED, "Readonly error");
447         ME_RETVM_IF(!metadata->ifart, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid tag");
448         return METADATA_EDITOR_ERROR_NONE;
449 }
450
451 static int __set_to_property_map(File *file, String key, const char *value)
452 {
453         ME_RETVM_IF(!file, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid file");
454         ME_RETVM_IF(key.isEmpty(), METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid key");
455
456         PropertyMap tags = file->properties();
457
458         if (value == NULL || strlen(value) == 0)
459                 tags.erase(key);
460         else
461                 tags.replace(key, StringList(String(value, String::UTF8)));
462
463         file->setProperties(tags);
464
465         return METADATA_EDITOR_ERROR_NONE;
466 }
467
468 static int __get_from_property_map(PropertyMap tags, String key, char **value)
469 {
470         ME_RETVM_IF(key.isEmpty(), METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid field_name");
471         ME_RETVM_IF(!value, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid value");
472
473         ME_RETV_IF(tags.isEmpty(), METADATA_EDITOR_ERROR_NONE);
474
475         PropertyMap::ConstIterator found = tags.find(key);
476
477         if (found != tags.end())
478                 *value = g_strdup(found->second[0].toCString(true));
479
480         return METADATA_EDITOR_ERROR_NONE;
481 }
482
483 class OggFileTypeResolver : public FileRef::FileTypeResolver {
484         File *createFile(FileName fileName, bool, AudioProperties::ReadStyle) const {
485                 String ext = __get_file_ext(fileName);
486                 if (ext == "OGG") {
487                         File *file = new Ogg::FLAC::File(fileName);
488                         if (file->isValid())
489                                 return file;
490                         delete file;
491                         return new Ogg::Vorbis::File(fileName);
492                 }
493                 return 0;
494         }
495 };
496
497 extern "C" int metadata_editor_create(metadata_editor_h *metadata)
498 {
499         ME_RETVM_IF(!metadata, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid metadata");
500
501         metadata_editor_s* _metadata = (metadata_editor_s*)malloc(sizeof(metadata_editor_s));
502         memset(_metadata, 0, sizeof(metadata_editor_s));
503
504         *metadata = (metadata_editor_h)_metadata;
505
506         return METADATA_EDITOR_ERROR_NONE;
507 }
508
509 extern "C" int metadata_editor_destroy(metadata_editor_h metadata)
510 {
511         auto _metadata = static_cast<metadata_editor_s*>(metadata);
512         ME_RETVM_IF(!_metadata, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid metadata");
513
514         delete _metadata->fref;
515         delete _metadata->ifart;
516         free(_metadata);
517
518         return METADATA_EDITOR_ERROR_NONE;
519 }
520
521 extern "C" int metadata_editor_set_path(metadata_editor_h metadata, const char *path)
522 {
523         int ret = METADATA_EDITOR_ERROR_NONE;
524         auto _metadata = static_cast<metadata_editor_s*>(metadata);
525         ME_RETVM_IF(!_metadata, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid metadata");
526
527         ret = __check_file_validity(path);
528         ME_RETVM_IF(ret != METADATA_EDITOR_ERROR_NONE, ret, "Invalid path");
529
530         if (_metadata->fref) {
531                 ME_INFO("file free [%p]", _metadata->fref);
532                 delete _metadata->fref;
533                 delete _metadata->ifart;
534         }
535
536         try {
537                 //FileTypeResolver is not deleted until terminated. So, it should be called only once.
538                 static auto oggResolver = FileRef::addFileTypeResolver(new OggFileTypeResolver);
539                 ME_INFO("%p", oggResolver);
540                 _metadata->fref = new FileRef(path);
541         } catch (const std::bad_alloc &ex) {
542                 ME_RETVM_IF(!_metadata->fref, METADATA_EDITOR_ERROR_OUT_OF_MEMORY, "OUT_OF_MEMORY");
543         }
544
545         if (!_metadata->fref->file()->isOpen()) {
546                 delete _metadata->fref;
547                 _metadata->fref = nullptr;
548                 return METADATA_EDITOR_ERROR_PERMISSION_DENIED;
549         }
550
551         _metadata->ifart = AlbumArtFactory::create(_metadata->fref);
552
553         return METADATA_EDITOR_ERROR_NONE;
554 }
555
556 extern "C" int metadata_editor_append_picture(metadata_editor_h metadata, const char *path)
557 {
558         int ret = METADATA_EDITOR_ERROR_NONE;
559         auto _metadata = static_cast<metadata_editor_s*>(metadata);
560         ME_RETV_IF(__is_valid_handle(_metadata, true), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
561
562         ret = __check_file_validity(path);
563         ME_RETVM_IF(ret != METADATA_EDITOR_ERROR_NONE, ret, "Invalid path");
564
565         PictureFrame pic(path);
566         ME_RETVM_IF(!pic.opened(), METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid path");
567
568         return _metadata->ifart->append(pic);
569 }
570
571 extern "C" int metadata_editor_remove_picture(metadata_editor_h metadata, int index)
572 {
573         auto _metadata = static_cast<metadata_editor_s*>(metadata);
574         ME_RETV_IF(__is_valid_handle(_metadata, true), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
575
576         return _metadata->ifart->remove(index);
577 }
578
579 extern "C" int metadata_editor_get_picture(metadata_editor_h metadata, int index, void **picture, int *size, char **mime_type)
580 {
581         auto _metadata = static_cast<metadata_editor_s*>(metadata);
582         ME_RETV_IF(__is_valid_handle(_metadata, false), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
583         ME_RETVM_IF(!picture, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid picture");
584         ME_RETVM_IF(!size, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid size");
585         ME_RETVM_IF(!mime_type, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid mime_type");
586
587         return _metadata->ifart->read(index, picture, size, mime_type);
588 }
589
590 static const char * __get_attr_str(metadata_editor_attr_e attribute)
591 {
592         switch (attribute) {
593         case METADATA_EDITOR_ATTR_ARTIST:
594                 return "ARTIST";
595         case METADATA_EDITOR_ATTR_TITLE:
596                 return "TITLE";
597         case METADATA_EDITOR_ATTR_ALBUM:
598                 return "ALBUM";
599         case METADATA_EDITOR_ATTR_GENRE:
600                 return "GENRE";
601         case METADATA_EDITOR_ATTR_AUTHOR:
602                 return "COMPOSER";
603         case METADATA_EDITOR_ATTR_COPYRIGHT:
604                 return "COPYRIGHT";
605         case METADATA_EDITOR_ATTR_DATE:
606                 return "DATE";
607         case METADATA_EDITOR_ATTR_DESCRIPTION:
608                 return "DESCRIPTION";
609         case METADATA_EDITOR_ATTR_COMMENT:
610                 return "COMMENT";
611         case METADATA_EDITOR_ATTR_TRACK_NUM:
612                 return "TRACKNUMBER";
613         case METADATA_EDITOR_ATTR_CONDUCTOR:
614                 return "CONDUCTOR";
615         case METADATA_EDITOR_ATTR_UNSYNCLYRICS:
616                 return "LYRICS";
617         default:
618                 return "";
619         }
620 }
621
622 extern "C" int metadata_editor_set_metadata(metadata_editor_h metadata, metadata_editor_attr_e attribute, const char *value)
623 {
624         auto _metadata = static_cast<metadata_editor_s*>(metadata);
625         ME_RETV_IF(__is_valid_handle(_metadata, true), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
626
627         return __set_to_property_map(_metadata->fref->file(), __get_attr_str(attribute), value);
628 }
629
630 extern "C" int metadata_editor_get_metadata(metadata_editor_h metadata, metadata_editor_attr_e attribute, char **value)
631 {
632         auto _metadata = static_cast<metadata_editor_s*>(metadata);
633         ME_RETV_IF(__is_valid_handle(_metadata, false), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
634         ME_RETVM_IF(!value, METADATA_EDITOR_ERROR_INVALID_PARAMETER, "Invalid value");
635
636         /* exceptional case */
637         if (attribute == METADATA_EDITOR_ATTR_PICTURE_NUM) {
638                 *value = g_strdup_printf("%u", _metadata->ifart->count());
639                 return METADATA_EDITOR_ERROR_NONE;
640         }
641
642         return __get_from_property_map(_metadata->fref->file()->properties(), __get_attr_str(attribute), value);
643 }
644
645 extern "C" int metadata_editor_update_metadata(metadata_editor_h metadata)
646 {
647         auto _metadata = static_cast<metadata_editor_s*>(metadata);
648         ME_RETV_IF(__is_valid_handle(_metadata, true), METADATA_EDITOR_ERROR_INVALID_PARAMETER);
649
650         if (!_metadata->fref->file()->save())
651                 return METADATA_EDITOR_ERROR_OPERATION_FAILED;
652
653         return METADATA_EDITOR_ERROR_NONE;
654 }