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