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