xref: /MusicPlayer2/MusicPlayer2/TagLibHelper.cpp (revision 67ca1ef4c5be62e9a917ffa576a24422d15e109a)
1 #include "stdafx.h"
2 #include "TagLibHelper.h"
3 #include "taglib/mp4file.h"
4 #include "taglib/mp4coverart.h"
5 #include "taglib/flacfile.h"
6 #include "taglib/flacpicture.h"
7 #include "taglib/mpegfile.h"
8 #include "Common.h"
9 #include "FilePathHelper.h"
10 #include "taglib/attachedpictureframe.h"
11 #include "taglib/id3v2tag.h"
12 #include "taglib/id3v1tag.h"
13 #include "taglib/apefile.h"
14 #include "taglib/wavfile.h"
15 #include "taglib/mpcfile.h"
16 #include "taglib/opusfile.h"
17 #include "taglib/wavpackfile.h"
18 #include "taglib/vorbisfile.h"
19 #include "taglib/trueaudiofile.h"
20 #include "taglib/aifffile.h"
21 #include "taglib/asffile.h"
22 #include "taglib/tpropertymap.h"
23 #include "AudioCommon.h"
24 #include "taglib/apetag.h"
25 #include "taglib/fileref.h"
26 #include "taglib/speexfile.h"
27 #include "taglib/unsynchronizedlyricsframe.h"
28 
29 
30 using namespace TagLib;
31 
32 #define STR_MP4_COVER_TAG "covr"
33 #define STR_MP4_LYRICS_TAG "----:com.apple.iTunes:Lyrics"
34 #define STR_ASF_COVER_TAG "WM/Picture"
35 #define STR_APE_COVER_TAG "COVER ART (FRONT)"
36 #define STR_ID3V2_LYRIC_TAG "USLT"
37 #define STR_FLAC_LYRIC_TAG "LYRICS"
38 #define STR_ASF_LYRIC_TAG "LYRICS"
39 #define STR_APE_CUE_TAG "CUESHEET"
40 
41 //��taglib�е��ַ���ת����wstring���͡�
42 //����taglib�����з�unicode����ȫ����ΪLatin���봦������޷���ȷ�����ش���ҳ
43 //���ォLatin������ַ��������ش���ҳ����
44 static wstring TagStringToWstring(const String& str, bool to_local)
45 {
46     wstring result;
47     if (to_local && str.isLatin1())
48         result = CCommon::StrToUnicode(str.to8Bit(), CodeType::ANSI);
49     else
50         result = str.toWString();
51     return result;
52 }
53 
54 static void SongInfoToTag(const SongInfo& song_info, Tag* tag)
55 {
56     if (tag != nullptr)
57     {
58         tag->setTitle(song_info.title);
59         tag->setArtist(song_info.artist);
60         tag->setAlbum(song_info.album);
61         tag->setGenre(song_info.genre);
62         tag->setTrack(song_info.track);
63         tag->setComment(song_info.comment);
64         tag->setYear(song_info.year);
65     }
66 }
67 
68 static bool IsStringNumber(wstring str, int num)
69 {
70     if (!str.empty() && str.front() == L'(')
71         str = str.substr(1);
72     if (!str.empty() && str.back() == L')')
73         str.pop_back();
74     if (CCommon::StrIsNumber(str))
75     {
76         num = _wtoi(str.c_str());
77         return true;
78     }
79     return false;
80 }
81 
82 static void TagToSongInfo(SongInfo& song_info, Tag* tag, bool to_local)
83 {
84     if (tag != nullptr)
85     {
86         song_info.title = TagStringToWstring(tag->title(), to_local);
87         song_info.artist = TagStringToWstring(tag->artist(), to_local);
88         song_info.album = TagStringToWstring(tag->album(), to_local);
89         song_info.genre = TagStringToWstring(tag->genre(), to_local);
90         int genre_num{};
91         if (IsStringNumber(song_info.genre, genre_num))
92         {
93             song_info.genre = CAudioCommon::GetGenre(static_cast<BYTE>(genre_num));
94         }
95 
96         song_info.year = tag->year();
97         song_info.track = tag->track();
98         song_info.comment = TagStringToWstring(tag->comment(), to_local);
99     }
100 }
101 
102 //���ļ����ݶ�ȡ��ByteVector
103 static void FileToByteVector(ByteVector& data, const std::wstring& file_path)
104 {
105     std::ifstream file{ file_path, std::ios::binary | std::ios::in };
106     if (file.fail())
107         return;
108 
109     //��ȡ�ļ�����
110     file.seekg(0, file.end);
111     size_t length = file.tellg();
112     file.seekg(0, file.beg);
113 
114     data.clear();
115     data.resize(static_cast<unsigned int>(length));
116 
117     file.read(data.data(), length);
118 
119     file.close();
120 }
121 
122 int GetPicType(const wstring& mimeType)
123 {
124     int type{ -1 };
125     if (mimeType == L"image/jpeg" || mimeType == L"image/jpg")
126         type = 0;
127     else if (mimeType == L"image/png")
128         type = 1;
129     else if (mimeType == L"image/gif")
130         type = 2;
131     else if (mimeType == L"image/bmp")
132         type = 3;
133     else
134         type = -1;
135     return type;
136 }
137 
138 static void GetId3v2AlbumCover(ID3v2::Tag* id3v2, string& cover_contents, int& type)
139 {
140     if (id3v2 != nullptr)
141     {
142         auto pic_frame_list = id3v2->frameListMap()["APIC"];
143         if (!pic_frame_list.isEmpty())
144         {
145             ID3v2::AttachedPictureFrame *frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(pic_frame_list.front());
146             if (frame != nullptr)
147             {
148                 auto pic_data = frame->picture();
149                 //��ȡר������
150                 cover_contents.assign(pic_data.data(), pic_data.size());
151                 wstring img_type = frame->mimeType().toCWString();
152                 type = GetPicType(img_type);
153             }
154         }
155     }
156 }
157 
158 static void DeleteId3v2AlbumCover(ID3v2::Tag* id3v2tag)
159 {
160     if (id3v2tag != nullptr)
161     {
162         auto pic_frame_list = id3v2tag->frameListMap()["APIC"];
163         if (!pic_frame_list.isEmpty())
164         {
165             for (auto frame : pic_frame_list)
166                 id3v2tag->removeFrame(frame);
167         }
168     }
169 }
170 
171 static void WriteId3v2AlbumCover(ID3v2::Tag* id3v2tag, const wstring& album_cover_path)
172 {
173     if (id3v2tag != nullptr)
174     {
175         //��ȡͼƬ�ļ�
176         ByteVector pic_data;
177         FileToByteVector(pic_data, album_cover_path);
178         //����Ƶ�ļ�д��ͼƬ�ļ�
179         ID3v2::AttachedPictureFrame* pic_frame = new ID3v2::AttachedPictureFrame();
180         pic_frame->setPicture(pic_data);
181         pic_frame->setType(ID3v2::AttachedPictureFrame::FrontCover);
182         wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
183         pic_frame->setMimeType(L"image/" + ext);
184         id3v2tag->addFrame(pic_frame);
185     }
186 }
187 
188 
189 static void GetApeTagAlbumCover(APE::Tag* tag, string& cover_contents, int& type)
190 {
191     if (tag != nullptr)
192     {
193         auto item_list_map = tag->itemListMap();
194         auto pic_item = item_list_map[STR_APE_COVER_TAG];
195         auto pic_data = pic_item.binaryData();
196         if (!pic_data.isEmpty())
197         {
198             cover_contents.assign(pic_data.data(), pic_data.size());
199 
200             size_t index{};
201             index = cover_contents.find('\0');
202             std::string pic_desc;
203             if (index != std::string::npos)
204             {
205                 pic_desc = cover_contents.substr(0, index);
206                 cover_contents = cover_contents.substr(index + 1);
207             }
208 
209             if (!pic_desc.empty())
210             {
211                 std::string img_type;
212                 index = pic_desc.rfind('.');
213                 if (index != std::string::npos && index < pic_desc.size() - 1)
214                 {
215                     img_type = pic_desc.substr(index + 1);
216                     img_type = "image/" + img_type;
217                     type = GetPicType(CCommon::ASCIIToUnicode(img_type));
218                 }
219             }
220         }
221     }
222 }
223 
224 static void WriteApeTagAlbumCover(APE::Tag* tag, const wstring &album_cover_path, bool remove_exist)
225 {
226     if (remove_exist)
227     {
228         tag->removeItem(STR_APE_COVER_TAG);
229     }
230 
231     if (!album_cover_path.empty())
232     {
233         ByteVector pic_data;
234         FileToByteVector(pic_data, album_cover_path);
235 
236         ByteVector pic_item_data;
237         pic_item_data = "Cover Art (Front).";
238         wstring file_type = CFilePathHelper(album_cover_path).GetFileExtension();
239         for (wchar_t ch : file_type)
240             pic_item_data.append(static_cast<char>(ch));
241         pic_item_data.append('\0');
242         pic_item_data.append(pic_data);
243 
244         APE::Item pic_item(STR_APE_COVER_TAG, pic_item_data, true);
245         tag->setItem(STR_APE_COVER_TAG, pic_item);
246     }
247 }
248 
249 static void WriteXiphCommentAlbumCover(Ogg::XiphComment * tag, const wstring &album_cover_path, bool remove_exist)
250 {
251     //��ɾ��ר������
252     if (remove_exist)
253     {
254         tag->removeAllPictures();
255     }
256 
257     if (!album_cover_path.empty())
258     {
259         ByteVector pic_data;
260         FileToByteVector(pic_data, album_cover_path);
261         FLAC::Picture *newpic = new FLAC::Picture();
262         newpic->setType(FLAC::Picture::FrontCover);
263         newpic->setData(pic_data);
264         wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
265         newpic->setMimeType(L"image/" + ext);
266         tag->addPicture(newpic);
267     }
268 }
269 
270 
271 void GetXiphCommentAlbumCover(Ogg::XiphComment * tag, string &cover_contents, int& type)
272 {
273     const auto& cover_list = tag->pictureList();
274     if (!cover_list.isEmpty())
275     {
276         auto pic = cover_list.front();
277         if (pic != nullptr)
278         {
279             const auto& pic_data = pic->data();
280             //��ȡר������
281             cover_contents.assign(pic_data.data(), pic_data.size());
282 
283             wstring img_type = pic->mimeType().toCWString();
284             type = GetPicType(img_type);
285         }
286     }
287 }
288 
289 wstring GetId3v2Lyric(ID3v2::Tag* id3v2)
290 {
291     wstring lyrics;
292     if (id3v2 != nullptr)
293     {
294         auto frame_list_map = id3v2->frameListMap();
295         auto lyric_frame = frame_list_map[STR_ID3V2_LYRIC_TAG];
296         if (!lyric_frame.isEmpty())
297             lyrics = lyric_frame.front()->toString().toWString();
298     }
299     return lyrics;
300 }
301 
302 
303 void WriteId3v2Lyric(ID3v2::Tag * id3v2, const wstring &lyric_contents)
304 {
305     if (id3v2 != nullptr)
306     {
307         //��ɾ�����֡
308         auto lyric_frame_list = id3v2->frameListMap()[STR_ID3V2_LYRIC_TAG];
309         if (!lyric_frame_list.isEmpty())
310         {
311             for (auto frame : lyric_frame_list)
312                 id3v2->removeFrame(frame);
313         }
314 
315         if (!lyric_contents.empty())
316         {
317             //д����֡
318             ID3v2::UnsynchronizedLyricsFrame* lyric_frame = new ID3v2::UnsynchronizedLyricsFrame();
319             lyric_frame->setText(lyric_contents.c_str());
320             id3v2->addFrame(lyric_frame);
321         }
322     }
323 }
324 
325 
326 template<class T>
327 void GetTagPropertyMap(T* tag, std::map<wstring, wstring>& property_map)
328 {
329     if (tag != nullptr)
330     {
331         auto properties = tag->properties();
332         for (const auto& prop : properties)
333         {
334             wstring key = prop.first.toWString();
335             wstring value = TagStringToWstring(prop.second.toString(L";"), true);
336             auto iter = property_map.find(key);
337             if (iter == property_map.end())
338                 property_map[key] = value;
339             else if (iter->second.empty())
340                 iter->second = value;
341         }
342     }
343 }
344 
345 
346 
347 ///////////////////////////////////////////////////////////////////////////////////
348 ///////////////////////////////////////////////////////////////////////////////////
349 
350 CTagLibHelper::CTagLibHelper()
351 {
352 }
353 
354 CTagLibHelper::~CTagLibHelper()
355 {
356 }
357 
358 string CTagLibHelper::GetM4aAlbumCover(const wstring& file_path, int& type)
359 {
360     string cover_contents;
361     MP4::File file(file_path.c_str());
362     auto tag = file.tag();
363     if(tag != nullptr)
364     {
365         auto cover_item = tag->item(STR_MP4_COVER_TAG).toCoverArtList();
366         if (!cover_item.isEmpty())
367         {
368             const auto& pic_data = cover_item.front().data();
369             //��ȡר������
370             cover_contents.assign(pic_data.data(), pic_data.size());
371 
372             //��ȡ�����ʽ
373             switch (cover_item.front().format())
374             {
375             case MP4::CoverArt::JPEG:
376                 type = 0;
377                 break;
378             case MP4::CoverArt::PNG:
379                 type = 1;
380                 break;
381             case MP4::CoverArt::BMP:
382                 type = 3;
383                 break;
384             case MP4::CoverArt::GIF:
385                 type = 2;
386                 break;
387             default:
388                 type = -1;
389                 break;
390             }
391         }
392     }
393     return cover_contents;
394 }
395 
396 string CTagLibHelper::GetFlacAlbumCover(const wstring& file_path, int& type)
397 {
398     string cover_contents;
399     FLAC::File file(file_path.c_str());
400     const auto& cover_list = file.pictureList();
401     if (!cover_list.isEmpty())
402     {
403         auto pic = cover_list.front();
404         if (pic != nullptr)
405         {
406             const auto& pic_data = pic->data();
407             //��ȡר������
408             cover_contents.assign(pic_data.data(), pic_data.size());
409 
410             wstring img_type = pic->mimeType().toCWString();
411             type = GetPicType(img_type);
412         }
413     }
414     return cover_contents;
415 }
416 
417 string CTagLibHelper::GetMp3AlbumCover(const wstring & file_path, int & type)
418 {
419     string cover_contents;
420     MPEG::File file(file_path.c_str());
421     auto id3v2 = file.ID3v2Tag();
422     GetId3v2AlbumCover(id3v2, cover_contents, type);
423     return cover_contents;
424 }
425 
426 string CTagLibHelper::GetAsfAlbumCover(const wstring& file_path, int& type)
427 {
428     string cover_contents;
429     ASF::File file(file_path.c_str());
430     auto tag = file.tag();
431     if (tag != nullptr)
432     {
433         ASF::AttributeList attr = tag->attribute("WM/Picture");
434         if (!attr.isEmpty())
435         {
436             ASF::Picture picture = attr.front().toPicture();
437             auto pic_data = picture.picture();
438             cover_contents.assign(pic_data.data(), pic_data.size());
439             wstring img_type = picture.mimeType().toCWString();
440             type = GetPicType(img_type);
441         }
442     }
443     return cover_contents;
444 }
445 
446 string CTagLibHelper::GetWavAlbumCover(const wstring& file_path, int& type)
447 {
448     string cover_contents;
449     RIFF::WAV::File file(file_path.c_str());
450     auto id3v2 = file.ID3v2Tag();
451     GetId3v2AlbumCover(id3v2, cover_contents, type);
452     return cover_contents;
453 }
454 
455 string CTagLibHelper::GetTtaAlbumCover(const wstring& file_path, int& type)
456 {
457     string cover_contents;
458     TrueAudio::File file(file_path.c_str());
459     auto id3v2 = file.ID3v2Tag();
460     GetId3v2AlbumCover(id3v2, cover_contents, type);
461     return cover_contents;
462 }
463 
464 string CTagLibHelper::GetApeAlbumCover(const wstring& file_path, int& type)
465 {
466     string cover_contents;
467     APE::File file(file_path.c_str());
468     auto tag = file.APETag();
469     GetApeTagAlbumCover(tag, cover_contents, type);
470     return cover_contents;
471 }
472 
473 
474 string CTagLibHelper::GetOggAlbumCover(const wstring& file_path, int& type)
475 {
476     string cover_contents;
477     Ogg::Vorbis::File file(file_path.c_str());
478     auto tag = file.tag();
479     if (tag != nullptr)
480     {
481         GetXiphCommentAlbumCover(tag, cover_contents, type);
482     }
483     return cover_contents;
484 }
485 
486 string CTagLibHelper::GetOpusAlbumCover(const wstring & file_path, int & type)
487 {
488     string cover_contents;
489     Ogg::Opus::File file(file_path.c_str());
490     auto tag = file.tag();
491     if (tag != nullptr)
492     {
493         GetXiphCommentAlbumCover(tag, cover_contents, type);
494     }
495     return cover_contents;
496 }
497 
498 string CTagLibHelper::GetSpxAlbumCover(const wstring& file_path, int& type)
499 {
500     string cover_contents;
501     Ogg::Speex::File file(file_path.c_str());
502     auto tag = file.tag();
503     if (tag != nullptr)
504     {
505         GetXiphCommentAlbumCover(tag, cover_contents, type);
506     }
507     return cover_contents;
508 }
509 
510 string CTagLibHelper::GetAiffAlbumCover(const wstring& file_path, int& type)
511 {
512     string cover_contents;
513     RIFF::AIFF::File file(file_path.c_str());
514     auto id3v2 = file.tag();
515     GetId3v2AlbumCover(id3v2, cover_contents, type);
516     return cover_contents;
517 
518 }
519 
520 string CTagLibHelper::GetMpcAlbumCover(const wstring& file_path, int& type)
521 {
522     string cover_contents;
523     MPC::File file(file_path.c_str());
524     auto ape_tag = file.APETag();
525     GetApeTagAlbumCover(ape_tag, cover_contents, type);
526     return cover_contents;
527 }
528 
529 string CTagLibHelper::GetWavePackAlbumCover(const wstring& file_path, int& type)
530 {
531     string cover_contents;
532     WavPack::File file(file_path.c_str());
533     auto ape_tag = file.APETag();
534     GetApeTagAlbumCover(ape_tag, cover_contents, type);
535     return cover_contents;
536 }
537 
538 void CTagLibHelper::GetFlacTagInfo(SongInfo& song_info)
539 {
540     FLAC::File file(song_info.file_path.c_str());
541     if (file.hasID3v1Tag())
542         song_info.tag_type |= T_ID3V1;
543     if (file.hasID3v2Tag())
544         song_info.tag_type |= T_ID3V2;
545     auto tag = file.tag();
546     if (tag != nullptr)
547     {
548         TagToSongInfo(song_info, tag, true);
549     }
550 }
551 
552 void CTagLibHelper::GetM4aTagInfo(SongInfo& song_info)
553 {
554     MP4::File file(song_info.file_path.c_str());
555     if (file.hasMP4Tag())
556         song_info.tag_type |= T_MP4;
557     auto tag = file.tag();
558     if (tag != nullptr)
559     {
560         TagToSongInfo(song_info, tag, false);
561     }
562 }
563 
564 void CTagLibHelper::GetMpegTagInfo(SongInfo& song_info)
565 {
566     MPEG::File file(song_info.file_path.c_str());
567     if (file.hasID3v1Tag())
568         song_info.tag_type |= T_ID3V1;
569     if (file.hasID3v2Tag())
570         song_info.tag_type |= T_ID3V2;
571     if (file.hasAPETag())
572         song_info.tag_type |= T_APE;
573 
574     auto tag = file.tag();
575     if (tag != nullptr)
576     {
577         TagToSongInfo(song_info, tag, true);
578     }
579 }
580 
581 void CTagLibHelper::GetAsfTagInfo(SongInfo& song_info)
582 {
583     ASF::File file(song_info.file_path.c_str());
584     auto tag = file.tag();
585     if (tag != nullptr)
586     {
587         TagToSongInfo(song_info, tag, false);
588     }
589 }
590 
591 void CTagLibHelper::GetApeTagInfo(SongInfo& song_info)
592 {
593     APE::File file(song_info.file_path.c_str());
594     if (file.hasID3v1Tag())
595         song_info.tag_type |= T_ID3V1;
596     if (file.hasAPETag())
597         song_info.tag_type |= T_APE;
598 
599     auto tag = file.tag();
600     if (tag != nullptr)
601     {
602         TagToSongInfo(song_info, tag, true);
603     }
604 }
605 
606 void CTagLibHelper::GetWavTagInfo(SongInfo& song_info)
607 {
608     RIFF::WAV::File file(song_info.file_path.c_str());
609     if (file.hasID3v2Tag())
610         song_info.tag_type |= T_ID3V2;
611     if (file.hasInfoTag())
612         song_info.tag_type |= T_RIFF;
613     auto tag = file.tag();
614     if (tag != nullptr)
615     {
616         TagToSongInfo(song_info, tag, false);
617     }
618 }
619 
620 void CTagLibHelper::GetOggTagInfo(SongInfo& song_info)
621 {
622     Vorbis::File file(song_info.file_path.c_str());
623     auto tag = file.tag();
624     if (tag != nullptr)
625     {
626         TagToSongInfo(song_info, tag, false);
627     }
628 }
629 
630 void CTagLibHelper::GetMpcTagInfo(SongInfo& song_info)
631 {
632     MPC::File file(song_info.file_path.c_str());
633     if (file.hasAPETag())
634         song_info.tag_type |= T_APE;
635     if (file.hasID3v1Tag())
636         song_info.tag_type |= T_ID3V1;
637     auto tag = file.tag();
638     if (tag != nullptr)
639     {
640         TagToSongInfo(song_info, tag, true);
641     }
642 }
643 
644 void CTagLibHelper::GetOpusTagInfo(SongInfo& song_info)
645 {
646     Ogg::Opus::File file(song_info.file_path.c_str());
647     auto tag = file.tag();
648     if (tag != nullptr)
649     {
650         TagToSongInfo(song_info, tag, false);
651     }
652 }
653 
654 void CTagLibHelper::GetWavPackTagInfo(SongInfo& song_info)
655 {
656     WavPack::File file(song_info.file_path.c_str());
657     if (file.hasAPETag())
658         song_info.tag_type |= T_APE;
659     if (file.hasID3v1Tag())
660         song_info.tag_type |= T_ID3V1;
661     auto tag = file.tag();
662     if (tag != nullptr)
663     {
664         TagToSongInfo(song_info, tag, true);
665     }
666 }
667 
668 void CTagLibHelper::GetTtaTagInfo(SongInfo& song_info)
669 {
670     TrueAudio::File file(song_info.file_path.c_str());
671     if (file.hasID3v1Tag())
672         song_info.tag_type |= T_ID3V1;
673     if (file.hasID3v2Tag())
674         song_info.tag_type |= T_ID3V2;
675     auto tag = file.tag();
676     if (tag != nullptr)
677     {
678         TagToSongInfo(song_info, tag, true);
679     }
680 }
681 
682 void CTagLibHelper::GetAiffTagInfo(SongInfo& song_info)
683 {
684     RIFF::AIFF::File file(song_info.file_path.c_str());
685     if (file.hasID3v2Tag())
686         song_info.tag_type |= T_ID3V2;
687     auto tag = file.tag();
688     if (tag != nullptr)
689     {
690         TagToSongInfo(song_info, tag, false);
691     }
692 }
693 
694 void CTagLibHelper::GetSpxTagInfo(SongInfo& song_info)
695 {
696     Ogg::Speex::File file(song_info.file_path.c_str());
697     auto tag = file.tag();
698     if (tag != nullptr)
699     {
700         TagToSongInfo(song_info, tag, false);
701     }
702 }
703 
704 void CTagLibHelper::GetAnyFileTagInfo(SongInfo & song_info)
705 {
706     FileRef file(song_info.file_path.c_str());
707     auto tag = file.tag();
708     if (tag != nullptr)
709     {
710         TagToSongInfo(song_info, tag, false);
711     }
712 }
713 
714 void CTagLibHelper::GetFlacPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
715 {
716     FLAC::File file(file_path.c_str());
717     if (file.hasXiphComment())
718         GetTagPropertyMap(file.xiphComment(), property_map);
719     if (file.hasID3v2Tag())
720         GetTagPropertyMap(file.ID3v2Tag(), property_map);
721     if (file.hasID3v1Tag())
722         GetTagPropertyMap(file.ID3v1Tag(), property_map);
723 }
724 
725 void CTagLibHelper::GetM4aPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
726 {
727     MP4::File file(file_path.c_str());
728     auto tag = file.tag();
729     GetTagPropertyMap(tag, property_map);
730 }
731 
732 void CTagLibHelper::GetMpegPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
733 {
734     MPEG::File file(file_path.c_str());
735     if (file.hasID3v2Tag())
736         GetTagPropertyMap(file.ID3v2Tag(), property_map);
737     if (file.hasAPETag())
738         GetTagPropertyMap(file.APETag(), property_map);
739     if (file.hasID3v1Tag())
740         GetTagPropertyMap(file.ID3v1Tag(), property_map);
741 }
742 
743 void CTagLibHelper::GetAsfPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
744 {
745     ASF::File file(file_path.c_str());
746     auto tag = file.tag();
747     GetTagPropertyMap(tag, property_map);
748 }
749 
750 void CTagLibHelper::GetApePropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
751 {
752     APE::File file(file_path.c_str());
753     if (file.hasAPETag())
754         GetTagPropertyMap(file.APETag(), property_map);
755     if (file.hasID3v1Tag())
756         GetTagPropertyMap(file.ID3v1Tag(), property_map);
757 }
758 
759 void CTagLibHelper::GetWavPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
760 {
761     RIFF::WAV::File file(file_path.c_str());
762     auto tag = file.tag();
763     GetTagPropertyMap(tag, property_map);
764 }
765 
766 void CTagLibHelper::GetOggPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
767 {
768     Vorbis::File file(file_path.c_str());
769     auto tag = file.tag();
770     GetTagPropertyMap(tag, property_map);
771 }
772 
773 void CTagLibHelper::GetMpcPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
774 {
775     MPC::File file(file_path.c_str());
776     if (file.hasAPETag())
777         GetTagPropertyMap(file.APETag(), property_map);
778     if (file.hasID3v1Tag())
779         GetTagPropertyMap(file.ID3v1Tag(), property_map);
780 }
781 
782 void CTagLibHelper::GetOpusPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
783 {
784     Ogg::Opus::File file(file_path.c_str());
785     auto tag = file.tag();
786     GetTagPropertyMap(tag, property_map);
787 }
788 
789 void CTagLibHelper::GetWavPackPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
790 {
791     WavPack::File file(file_path.c_str());
792     if (file.hasAPETag())
793         GetTagPropertyMap(file.APETag(), property_map);
794     if (file.hasID3v1Tag())
795         GetTagPropertyMap(file.ID3v1Tag(), property_map);
796 }
797 
798 void CTagLibHelper::GetTtaPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
799 {
800     TrueAudio::File file(file_path.c_str());
801     if (file.hasID3v2Tag())
802         GetTagPropertyMap(file.ID3v2Tag(), property_map);
803     if (file.hasID3v1Tag())
804         GetTagPropertyMap(file.ID3v1Tag(), property_map);
805 }
806 
807 void CTagLibHelper::GetAiffPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
808 {
809     RIFF::AIFF::File file(file_path.c_str());
810     auto tag = file.tag();
811     GetTagPropertyMap(tag, property_map);
812 }
813 
814 void CTagLibHelper::GetSpxPropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
815 {
816     Ogg::Speex::File file(file_path.c_str());
817     auto tag = file.tag();
818     GetTagPropertyMap(tag, property_map);
819 }
820 
821 void CTagLibHelper::GetAnyFilePropertyMap(const wstring & file_path, std::map<wstring, wstring>& property_map)
822 {
823     FileRef file(file_path.c_str());
824     auto tag = file.tag();
825     GetTagPropertyMap(tag, property_map);
826 }
827 
828 wstring CTagLibHelper::GetMpegLyric(const wstring& file_path)
829 {
830     MPEG::File file(file_path.c_str());
831     auto id3v2 = file.ID3v2Tag();
832     return GetId3v2Lyric(id3v2);
833 }
834 
835 wstring CTagLibHelper::GetM4aLyric(const wstring& file_path)
836 {
837     wstring lyrics;
838     MP4::File file(file_path.c_str());
839     auto tag = file.tag();
840     if (tag != nullptr)
841     {
842         auto item_map = file.tag()->itemMap();
843         auto lyric_item = item_map[STR_MP4_LYRICS_TAG].toStringList();;
844         if (!lyric_item.isEmpty())
845             lyrics = lyric_item.front().toWString();
846     }
847     return lyrics;
848 }
849 
850 wstring CTagLibHelper::GetFlacLyric(const wstring& file_path)
851 {
852     wstring lyrics;
853     FLAC::File file(file_path.c_str());
854     auto properties = file.properties();
855     if (file.isValid())
856     {
857         auto lyric_item = properties[STR_FLAC_LYRIC_TAG];
858         if (!lyric_item.isEmpty())
859         {
860             lyrics = lyric_item.front().toWString();
861         }
862     }
863     return lyrics;
864 }
865 
866 wstring CTagLibHelper::GetAsfLyric(const wstring& file_path)
867 {
868     wstring lyrics;
869     ASF::File file(file_path.c_str());
870     if (file.isValid())
871     {
872         auto properties = file.properties();
873         auto lyric_item = properties[STR_ASF_LYRIC_TAG];
874         if (!lyric_item.isEmpty())
875         {
876             lyrics = lyric_item.front().toWString();
877         }
878     }
879     return lyrics;
880 }
881 
882 
883 wstring CTagLibHelper::GetWavLyric(const wstring& file_path)
884 {
885     RIFF::WAV::File file(file_path.c_str());
886     auto id3v2 = file.ID3v2Tag();
887     return GetId3v2Lyric(id3v2);
888 
889 }
890 
891 bool CTagLibHelper::WriteMpegLyric(const wstring& file_path, const wstring& lyric_contents)
892 {
893     wstring lyrics;
894     MPEG::File file(file_path.c_str());
895     auto id3v2 = file.ID3v2Tag();
896     WriteId3v2Lyric(id3v2, lyric_contents);
897     int tags = MPEG::File::ID3v2;
898     if (file.hasAPETag())
899         tags |= MPEG::File::APE;
900     bool saved = file.save(tags);
901     return saved;
902 }
903 
904 bool CTagLibHelper::WriteFlacLyric(const wstring& file_path, const wstring& lyric_contents)
905 {
906     FLAC::File file(file_path.c_str());
907     if (file.isValid())
908     {
909         auto properties = file.properties();
910         if (lyric_contents.empty())
911         {
912             properties.erase(STR_FLAC_LYRIC_TAG);
913         }
914         else
915         {
916             StringList lyric_item;
917             lyric_item.append(lyric_contents);
918             properties[STR_FLAC_LYRIC_TAG] = lyric_item;
919         }
920         file.setProperties(properties);
921         bool saved = file.save();
922         return saved;
923     }
924     return false;
925 }
926 
927 bool CTagLibHelper::WriteM4aLyric(const wstring& file_path, const wstring& lyric_contents)
928 {
929     MP4::File file(file_path.c_str());
930     auto tag = file.tag();
931     if (tag != nullptr)
932     {
933         if (lyric_contents.empty())
934         {
935             tag->removeItem(STR_MP4_LYRICS_TAG);
936         }
937         else
938         {
939             StringList lyric_list;
940             lyric_list.append(lyric_contents);
941             MP4::Item lyrics_item(lyric_list);
942             tag->setItem(STR_MP4_LYRICS_TAG, lyrics_item);
943         }
944         bool saved = file.save();
945         return saved;
946     }
947     return false;
948 }
949 
950 bool CTagLibHelper::WriteAsfLyric(const wstring& file_path, const wstring& lyric_contents)
951 {
952     ASF::File file(file_path.c_str());
953     if (file.isValid())
954     {
955         auto properties = file.properties();
956         if (lyric_contents.empty())
957         {
958             properties.erase(STR_ASF_LYRIC_TAG);
959         }
960         else
961         {
962             StringList lyric_item;
963             lyric_item.append(lyric_contents);
964             properties[STR_ASF_LYRIC_TAG] = lyric_item;
965         }
966         file.setProperties(properties);
967         bool saved = file.save();
968         return saved;
969     }
970     return false;
971 }
972 
973 bool CTagLibHelper::WriteWavLyric(const wstring& file_path, const wstring& lyric_contents)
974 {
975     wstring lyrics;
976     RIFF::WAV::File file(file_path.c_str());
977     auto id3v2 = file.ID3v2Tag();
978     WriteId3v2Lyric(id3v2, lyric_contents);
979     bool saved = file.save();
980     return saved;
981 }
982 
983 bool CTagLibHelper::WriteMp3AlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist)
984 {
985     MPEG::File file(file_path.c_str());
986     if (!file.isValid())
987         return false;
988 
989     //��ɾ��ר������
990     if (remove_exist)
991     {
992         auto id3v2tag = file.ID3v2Tag();
993         DeleteId3v2AlbumCover(id3v2tag);
994     }
995     if (!album_cover_path.empty())
996     {
997         auto id3v2tag = file.ID3v2Tag(true);
998         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
999     }
1000     int tags = MPEG::File::ID3v2;
1001     if (file.hasAPETag())
1002         tags |= MPEG::File::APE;
1003     bool saved = file.save(tags);
1004     return saved;
1005 }
1006 
1007 bool CTagLibHelper::WriteFlacAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist)
1008 {
1009     FLAC::File file(file_path.c_str());
1010     if (!file.isValid())
1011         return false;
1012 
1013     //��ɾ��ר������
1014     if (remove_exist)
1015     {
1016         file.removePictures();
1017     }
1018 
1019     if (!album_cover_path.empty())
1020     {
1021         ByteVector pic_data;
1022         FileToByteVector(pic_data, album_cover_path);
1023         FLAC::Picture *newpic = new FLAC::Picture();
1024         newpic->setType(FLAC::Picture::FrontCover);
1025         newpic->setData(pic_data);
1026         wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
1027         newpic->setMimeType(L"image/" + ext);
1028         file.addPicture(newpic);
1029     }
1030     bool saved = file.save();
1031     return saved;
1032 }
1033 
1034 bool CTagLibHelper::WriteM4aAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1035 {
1036     MP4::File file(file_path.c_str());
1037     if (!file.isValid())
1038         return false;
1039 
1040     auto tag = file.tag();
1041     if (tag == nullptr)
1042         return false;
1043 
1044     if (remove_exist)
1045     {
1046         if (tag->contains(STR_MP4_COVER_TAG))
1047         {
1048             tag->removeItem(STR_MP4_COVER_TAG);
1049         }
1050     }
1051 
1052     if (!album_cover_path.empty())
1053     {
1054         ByteVector pic_data;
1055         FileToByteVector(pic_data, album_cover_path);
1056         MP4::CoverArt::Format format = MP4::CoverArt::Format::Unknown;
1057         wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
1058         if (ext == L"jpg" || ext == L"jpeg")
1059             format = MP4::CoverArt::Format::JPEG;
1060         else if (ext == L"png")
1061             format = MP4::CoverArt::Format::PNG;
1062         else if (ext == L"gif")
1063             format = MP4::CoverArt::Format::GIF;
1064         else if (ext == L"bmp")
1065             format = MP4::CoverArt::Format::BMP;
1066         MP4::CoverArt cover_item(format, pic_data);
1067 
1068         auto cover_item_list = tag->item(STR_MP4_COVER_TAG).toCoverArtList();
1069         cover_item_list.append(cover_item);
1070         tag->setItem(STR_MP4_COVER_TAG, cover_item_list);
1071     }
1072     bool saved = file.save();
1073     return saved;
1074 }
1075 
1076 bool CTagLibHelper::WriteAsfAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1077 {
1078     ASF::File file(file_path.c_str());
1079     if (!file.isValid())
1080         return false;
1081 
1082     auto tag = file.tag();
1083     if (tag == nullptr)
1084         return false;
1085 
1086     if (remove_exist)
1087     {
1088         if (tag->contains(STR_ASF_COVER_TAG))
1089         {
1090             tag->removeItem(STR_ASF_COVER_TAG);
1091         }
1092     }
1093 
1094     if (!album_cover_path.empty())
1095     {
1096         ByteVector pic_data;
1097         FileToByteVector(pic_data, album_cover_path);
1098 
1099         ASF::Picture picture;
1100         wstring str_mine_type = L"image/" + CFilePathHelper(album_cover_path).GetFileExtension();
1101         picture.setMimeType(str_mine_type);
1102         picture.setType(ASF::Picture::FrontCover);
1103         picture.setPicture(pic_data);
1104         tag->setAttribute(STR_ASF_COVER_TAG, picture);
1105     }
1106     bool saved = file.save();
1107     return saved;
1108 }
1109 
1110 bool CTagLibHelper::WriteWavAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1111 {
1112     RIFF::WAV::File file(file_path.c_str());
1113     if (!file.isValid())
1114         return false;
1115 
1116     //��ɾ��ר������
1117     auto id3v2tag = file.ID3v2Tag();
1118     if (remove_exist)
1119     {
1120         DeleteId3v2AlbumCover(id3v2tag);
1121     }
1122     if (!album_cover_path.empty())
1123     {
1124         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1125     }
1126     bool saved = file.save();
1127     return saved;
1128 }
1129 
1130 bool CTagLibHelper::WriteTtaAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1131 {
1132     TrueAudio::File file(file_path.c_str());
1133     if (!file.isValid())
1134         return false;
1135 
1136     if (remove_exist)
1137     {
1138         auto id3v2tag = file.ID3v2Tag();
1139         DeleteId3v2AlbumCover(id3v2tag);
1140     }
1141     if (!album_cover_path.empty())
1142     {
1143         auto id3v2tag = file.ID3v2Tag(true);
1144         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1145     }
1146     bool saved = file.save();
1147     return saved;
1148     return false;
1149 }
1150 
1151 bool CTagLibHelper::WriteApeAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1152 {
1153     APE::File file(file_path.c_str());
1154     if (!file.isValid())
1155         return false;
1156 
1157     auto tag = file.APETag(true);
1158     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1159     bool saved = file.save();
1160     return saved;
1161 }
1162 
1163 bool CTagLibHelper::WriteOggAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1164 {
1165     Ogg::Vorbis::File file(file_path.c_str());
1166     if (!file.isValid())
1167         return false;
1168 
1169     auto tag = file.tag();
1170     if (tag != nullptr)
1171     {
1172         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1173         bool saved = file.save();
1174         return saved;
1175     }
1176     return false;
1177 }
1178 
1179 bool CTagLibHelper::WriteOpusAlbumCover(const wstring & file_path, const wstring & album_cover_path, bool remove_exist)
1180 {
1181     Ogg::Opus::File file(file_path.c_str());
1182     if (!file.isValid())
1183         return false;
1184 
1185     auto tag = file.tag();
1186     if (tag != nullptr)
1187     {
1188         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1189         bool saved = file.save();
1190         return saved;
1191     }
1192     return false;
1193 }
1194 
1195 bool CTagLibHelper::WriteSpxAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1196 {
1197     Ogg::Speex::File file(file_path.c_str());
1198     if (!file.isValid())
1199         return false;
1200 
1201     auto tag = file.tag();
1202     if (tag != nullptr)
1203     {
1204         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1205         bool saved = file.save();
1206         return saved;
1207     }
1208     return false;
1209 }
1210 
1211 bool CTagLibHelper::WriteAiffAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1212 {
1213     RIFF::AIFF::File file(file_path.c_str());
1214     if (!file.isValid())
1215         return false;
1216 
1217     //��ɾ��ר������
1218     auto id3v2tag = file.tag();
1219     if (remove_exist)
1220     {
1221         DeleteId3v2AlbumCover(id3v2tag);
1222     }
1223     if (!album_cover_path.empty())
1224     {
1225         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1226     }
1227     bool saved = file.save();
1228     return saved;
1229 }
1230 
1231 bool CTagLibHelper::WriteMpcAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1232 {
1233     MPC::File file(file_path.c_str());
1234     if (!file.isValid())
1235         return false;
1236 
1237     auto tag = file.APETag(true);
1238     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1239     bool saved = file.save();
1240     return saved;
1241 }
1242 
1243 bool CTagLibHelper::WriteWavePackAlbumCover(const wstring& file_path, const wstring& album_cover_path, bool remove_exist /*= true*/)
1244 {
1245     WavPack::File file(file_path.c_str());
1246     if (!file.isValid())
1247         return false;
1248 
1249     auto tag = file.APETag(true);
1250     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1251     bool saved = file.save();
1252     return saved;
1253 }
1254 
1255 bool CTagLibHelper::WriteMpegTag(SongInfo & song_info)
1256 {
1257     MPEG::File file(song_info.file_path.c_str());
1258     auto tag = file.tag();
1259     SongInfoToTag(song_info, tag);
1260     int tags = MPEG::File::ID3v2;
1261     //if (file.hasID3v1Tag())
1262     //    tags |= MPEG::File::ID3v1;
1263     if (file.hasAPETag())
1264         tags |= MPEG::File::APE;
1265     bool saved = file.save(tags);
1266     return saved;
1267 }
1268 
1269 bool CTagLibHelper::WriteFlacTag(SongInfo& song_info)
1270 {
1271     FLAC::File file(song_info.file_path.c_str());
1272     auto tag = file.tag();
1273     SongInfoToTag(song_info, tag);
1274     bool saved = file.save();
1275     return saved;
1276 }
1277 
1278 bool CTagLibHelper::WriteM4aTag(SongInfo & song_info)
1279 {
1280     MP4::File file(song_info.file_path.c_str());
1281     auto tag = file.tag();
1282     SongInfoToTag(song_info, tag);
1283     bool saved = file.save();
1284     return saved;
1285 }
1286 
1287 bool CTagLibHelper::WriteWavTag(SongInfo & song_info)
1288 {
1289     RIFF::WAV::File file(song_info.file_path.c_str());
1290     auto tag = file.tag();
1291     SongInfoToTag(song_info, tag);
1292     bool saved = file.save();
1293     return saved;
1294 }
1295 
1296 bool CTagLibHelper::WriteOggTag(SongInfo & song_info)
1297 {
1298     Vorbis::File file(song_info.file_path.c_str());
1299     auto tag = file.tag();
1300     SongInfoToTag(song_info, tag);
1301     bool saved = file.save();
1302     return saved;
1303 }
1304 
1305 bool CTagLibHelper::WriteApeTag(SongInfo& song_info)
1306 {
1307     APE::File file(song_info.file_path.c_str());
1308     auto tag = file.tag();
1309     SongInfoToTag(song_info, tag);
1310     bool saved = file.save();
1311     return saved;
1312 }
1313 
1314 bool CTagLibHelper::WriteMpcTag(SongInfo& song_info)
1315 {
1316     MPC::File file(song_info.file_path.c_str());
1317     auto tag = file.tag();
1318     SongInfoToTag(song_info, tag);
1319     bool saved = file.save();
1320     return saved;
1321 }
1322 
1323 bool CTagLibHelper::WriteOpusTag(SongInfo & song_info)
1324 {
1325     Ogg::Opus::File file(song_info.file_path.c_str());
1326     auto tag = file.tag();
1327     SongInfoToTag(song_info, tag);
1328     bool saved = file.save();
1329     return saved;
1330 }
1331 
1332 bool CTagLibHelper::WriteWavPackTag(SongInfo& song_info)
1333 {
1334     WavPack::File file(song_info.file_path.c_str());
1335     auto tag = file.tag();
1336     SongInfoToTag(song_info, tag);
1337     bool saved = file.save();
1338     return saved;
1339 }
1340 
1341 bool CTagLibHelper::WriteTtaTag(SongInfo& song_info)
1342 {
1343     TrueAudio::File file(song_info.file_path.c_str());
1344     auto tag = file.tag();
1345     SongInfoToTag(song_info, tag);
1346     bool saved = file.save();
1347     return saved;
1348 }
1349 
1350 bool CTagLibHelper::WriteAiffTag(SongInfo & song_info)
1351 {
1352     RIFF::AIFF::File file(song_info.file_path.c_str());
1353     auto tag = file.tag();
1354     SongInfoToTag(song_info, tag);
1355     bool saved = file.save();
1356     return saved;
1357 }
1358 
1359 bool CTagLibHelper::WriteAsfTag(SongInfo & song_info)
1360 {
1361     ASF::File file(song_info.file_path.c_str());
1362     auto tag = file.tag();
1363     SongInfoToTag(song_info, tag);
1364     bool saved = file.save();
1365     return saved;
1366 }
1367 
1368 bool CTagLibHelper::WriteSpxTag(SongInfo& song_info)
1369 {
1370     Ogg::Speex::File file(song_info.file_path.c_str());
1371     auto tag = file.tag();
1372     SongInfoToTag(song_info, tag);
1373     bool saved = file.save();
1374     return saved;
1375 }
1376 
1377 wstring CTagLibHelper::GetApeCue(const wstring& file_path)
1378 {
1379     wstring cue_contents;
1380     APE::File file(file_path.c_str());
1381     auto tag = file.APETag();
1382     if (tag != nullptr)
1383     {
1384         auto item_list_map = tag->itemListMap();
1385         auto cue_item = item_list_map[STR_APE_CUE_TAG];
1386         cue_contents = cue_item.toString().toWString();
1387     }
1388     return cue_contents;
1389 }
1390