xref: /MusicPlayer2/MusicPlayer2/TagLibHelper.cpp (revision 443d2d2511be730d1b1dd3181942b7fa6539aa1a)
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中的字符串转换成std::wstring类型。
50 //由于taglib将所有非unicode编码全部作为Latin编码处理,因此无法正确处理本地代码页
51 //这里将Latin编码的字符串按本地代码页处理
52 static std::wstring TagStringToWstring(const String& str, bool to_local)
53 {
54     std::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(std::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     unsigned int length = static_cast<unsigned int>(file.tellg());
120     file.seekg(0, file.beg);
121 
122     data.clear();
123     data.resize(length);
124 
125     file.read(data.data(), length);
126 
127     file.close();
128 }
129 
130 int GetPicType(const std::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                 std::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 std::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         std::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 std::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         std::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 std::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         std::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             std::wstring img_type = pic->mimeType().toCWString();
292             type = GetPicType(img_type);
293         }
294     }
295 }
296 
297 static std::wstring GetId3v2Lyric(ID3v2::Tag* id3v2)
298 {
299     std::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 std::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<std::wstring, std::wstring>& property_map)
336 {
337     if (tag != nullptr)
338     {
339         auto properties = tag->properties();
340         for (const auto& prop : properties)
341         {
342             std::wstring key = prop.first.toWString();
343             std::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 static std::wstring GetMapValue(const std::wstring& key, const std::map<std::wstring, std::wstring>& property_map)
354 {
355     auto iter = property_map.find(key);
356     if (iter != property_map.end())
357         return iter->second;
358     return std::wstring();
359 }
360 
361 //将标签属性中额外的信息保存到SongInfo中
362 static void OtherPropertyToSongInfo(SongInfo& song_info, const std::map<std::wstring, std::wstring>& property_map)
363 {
364     //获取唱片集艺术家
365     song_info.album_artist = GetMapValue(L"ALBUMARTIST", property_map);
366 
367     //获取音轨总数
368     std::wstring track_number = GetMapValue(L"TRACKNUMBER", property_map);
369     size_t index = track_number.find(L'/');
370     if (index != std::wstring::npos)
371     {
372         song_info.total_tracks = static_cast<BYTE>(_wtoi(track_number.substr(index + 1).c_str()));
373     }
374 
375     //获取CD序号和CD总数
376     std::wstring disc_number = GetMapValue(L"DISCNUMBER", property_map);
377     index = disc_number.find(L'/');
378     if (index != std::wstring::npos)
379     {
380         song_info.total_discs = static_cast<BYTE>(_wtoi(disc_number.substr(index + 1).c_str()));
381     }
382     song_info.disc_num = static_cast<BYTE>(_wtoi(disc_number.substr(0, index).c_str()));
383 }
384 
385 template<class T>
386 static void WriteOtherProperties(const SongInfo& song_info, T& file)
387 {
388     TagLib::PropertyMap properties = file.properties();
389     properties["ALBUMARTIST"].clear();
390     properties["DISCNUMBER"].clear();
391     if (!song_info.album_artist.empty())
392         properties["ALBUMARTIST"].append(song_info.album_artist);
393     if (song_info.disc_num != 0)
394         properties["DISCNUMBER"].append(std::to_wstring(static_cast<int>(song_info.disc_num)));
395     file.setProperties(properties);
396 }
397 
398 //解析Windows资源管理器设置的分级信息
399 static int ParseAudioRating(int rate_raw)
400 {
401     //使用Windows资源管理器设置了分级后,POPM字段的内容为以下格式:
402     //Windows Media Player 9 Series rating=196 counter=0
403     //其中rating后面的数字为分级,rating与分级的对应关系如下所示
404     /*
405       rating   |    分级
406       ---------|------------
407         255    |     5
408         196    |     4
409         128    |     3
410         64     |     2
411         1      |     1
412     */
413 
414     //根据分级转换成1~5星
415     if (rate_raw == 1)
416         return 1;
417     else if (rate_raw <= 64)
418         return 2;
419     else if (rate_raw <= 128)
420         return 3;
421     else if (rate_raw <= 196)
422         return 4;
423     else if (rate_raw <= 255)
424         return 5;
425     return 0;
426 }
427 
428 static int GenerateAudioRating(int rate)
429 {
430     switch (rate)
431     {
432     case 1:
433         return 1;
434     case 2:
435         return 64;
436     case 3:
437         return 128;
438     case 4:
439         return 196;
440     case 5:
441         return 255;
442     default:
443         return 0;
444     }
445 }
446 
447 static int GetId3v2Rating(ID3v2::Tag* id3v2)
448 {
449     if (id3v2 != nullptr)
450     {
451         auto frame_list_map = id3v2->frameListMap();
452         auto rate_frame = frame_list_map[STR_ID3V2_RATEING_TAG];
453         if (!rate_frame.isEmpty())
454         {
455             ID3v2::PopularimeterFrame* pFrame = dynamic_cast<ID3v2::PopularimeterFrame*>(rate_frame.front());
456             if (pFrame != nullptr)
457             {
458                 int rate_raw = pFrame->rating();
459                 return ParseAudioRating(rate_raw);
460             }
461         }
462     }
463     return 0;
464 }
465 
466 static void WriteId3v2Rating(ID3v2::Tag* id3v2, int rate)
467 {
468     if (id3v2 != nullptr)
469     {
470         auto frameListMap = id3v2->frameListMap();
471 
472         //先删除POPM帧
473         auto rate_frame_list = id3v2->frameListMap()[STR_ID3V2_RATEING_TAG];
474         if (!rate_frame_list.isEmpty())
475         {
476             for (auto frame : rate_frame_list)
477                 id3v2->removeFrame(frame);
478         }
479 
480         if (rate >= 1 && rate <= 5)
481         {
482             ID3v2::PopularimeterFrame* rate_frame = new ID3v2::PopularimeterFrame();
483             int rate_raw = GenerateAudioRating(rate);
484             rate_frame->setRating(rate_raw);
485             id3v2->addFrame(rate_frame);
486         }
487     }
488 }
489 
490 static void getFlacPropertyMap(FLAC::File& file, std::map<std::wstring, std::wstring>& property_map)
491 {
492     if (file.hasXiphComment())
493         GetTagPropertyMap(file.xiphComment(), property_map);
494     if (file.hasID3v2Tag())
495         GetTagPropertyMap(file.ID3v2Tag(), property_map);
496     if (file.hasID3v1Tag())
497         GetTagPropertyMap(file.ID3v1Tag(), property_map);
498 }
499 
500 static void getM4aPropertyMap(MP4::File& file, std::map<std::wstring, std::wstring>& property_map)
501 {
502     auto tag = file.tag();
503     GetTagPropertyMap(tag, property_map);
504 }
505 
506 static void getMpegPropertyMap(MPEG::File& file, std::map<std::wstring, std::wstring>& property_map)
507 {
508     if (file.hasID3v2Tag())
509         GetTagPropertyMap(file.ID3v2Tag(), property_map);
510     if (file.hasAPETag())
511         GetTagPropertyMap(file.APETag(), property_map);
512     if (file.hasID3v1Tag())
513         GetTagPropertyMap(file.ID3v1Tag(), property_map);
514 }
515 
516 static void getAsfPropertyMap(ASF::File& file, std::map<std::wstring, std::wstring>& property_map)
517 {
518     auto tag = file.tag();
519     GetTagPropertyMap(tag, property_map);
520 }
521 
522 static void getApePropertyMap(APE::File& file, std::map<std::wstring, std::wstring>& property_map)
523 {
524     if (file.hasAPETag())
525         GetTagPropertyMap(file.APETag(), property_map);
526     if (file.hasID3v1Tag())
527         GetTagPropertyMap(file.ID3v1Tag(), property_map);
528 }
529 
530 static void getWavPropertyMap(RIFF::WAV::File& file, std::map<std::wstring, std::wstring>& property_map)
531 {
532     if (file.hasID3v2Tag())
533         GetTagPropertyMap(file.ID3v2Tag(), property_map);
534     if (file.hasInfoTag())
535         GetTagPropertyMap(file.InfoTag(), property_map);
536 }
537 
538 static void getOggPropertyMap(Vorbis::File& file, std::map<std::wstring, std::wstring>& property_map)
539 {
540     auto tag = file.tag();
541     GetTagPropertyMap(tag, property_map);
542 }
543 
544 static void getMpcPropertyMap(MPC::File& file, std::map<std::wstring, std::wstring>& property_map)
545 {
546     if (file.hasAPETag())
547         GetTagPropertyMap(file.APETag(), property_map);
548     if (file.hasID3v1Tag())
549         GetTagPropertyMap(file.ID3v1Tag(), property_map);
550 }
551 
552 static void getOpusPropertyMap(Ogg::Opus::File& file, std::map<std::wstring, std::wstring>& property_map)
553 {
554     auto tag = file.tag();
555     GetTagPropertyMap(tag, property_map);
556 }
557 
558 static void getWavPackPropertyMap(WavPack::File& file, std::map<std::wstring, std::wstring>& property_map)
559 {
560     if (file.hasAPETag())
561         GetTagPropertyMap(file.APETag(), property_map);
562     if (file.hasID3v1Tag())
563         GetTagPropertyMap(file.ID3v1Tag(), property_map);
564 }
565 
566 static void getTtaPropertyMap(TrueAudio::File& file, std::map<std::wstring, std::wstring>& property_map)
567 {
568     if (file.hasID3v2Tag())
569         GetTagPropertyMap(file.ID3v2Tag(), property_map);
570     if (file.hasID3v1Tag())
571         GetTagPropertyMap(file.ID3v1Tag(), property_map);
572 }
573 
574 static void getAiffPropertyMap(RIFF::AIFF::File& file, std::map<std::wstring, std::wstring>& property_map)
575 {
576     auto tag = file.tag();
577     GetTagPropertyMap(tag, property_map);
578 }
579 
580 static void getSpxPropertyMap(Ogg::Speex::File& file, std::map<std::wstring, std::wstring>& property_map)
581 {
582     auto tag = file.tag();
583     GetTagPropertyMap(tag, property_map);
584 }
585 
586 static void getAnyFilePropertyMap(FileRef& file, std::map<std::wstring, std::wstring>& property_map)
587 {
588     auto tag = file.tag();
589     GetTagPropertyMap(tag, property_map);
590 }
591 
592 
593 ///////////////////////////////////////////////////////////////////////////////////
594 ///////////////////////////////////////////////////////////////////////////////////
595 ///////////////////////////////////////////////////////////////////////////////////
596 
597 bool CTagLibHelper::m_write_id3v2_3{ false };
598 
599 CTagLibHelper::CTagLibHelper()
600 {
601 }
602 
603 CTagLibHelper::~CTagLibHelper()
604 {
605 }
606 
607 void CTagLibHelper::SetWriteId3V2_3(bool write_id3v2_3)
608 {
609     m_write_id3v2_3 = write_id3v2_3;
610 }
611 
612 string CTagLibHelper::GetM4aAlbumCover(const std::wstring& file_path, int& type)
613 {
614     string cover_contents;
615     MP4::File file(file_path.c_str());
616     auto tag = file.tag();
617     if (tag != nullptr)
618     {
619         auto cover_item = tag->item(STR_MP4_COVER_TAG).toCoverArtList();
620         if (!cover_item.isEmpty())
621         {
622             const auto& pic_data = cover_item.front().data();
623             //获取专辑封面
624             cover_contents.assign(pic_data.data(), pic_data.size());
625 
626             //获取封面格式
627             switch (cover_item.front().format())
628             {
629             case MP4::CoverArt::JPEG:
630                 type = 0;
631                 break;
632             case MP4::CoverArt::PNG:
633                 type = 1;
634                 break;
635             case MP4::CoverArt::BMP:
636                 type = 3;
637                 break;
638             case MP4::CoverArt::GIF:
639                 type = 2;
640                 break;
641             default:
642                 type = -1;
643                 break;
644             }
645         }
646     }
647     return cover_contents;
648 }
649 
650 string CTagLibHelper::GetFlacAlbumCover(const std::wstring& file_path, int& type)
651 {
652     string cover_contents;
653     FLAC::File file(file_path.c_str());
654     const auto& cover_list = file.pictureList();
655     if (!cover_list.isEmpty())
656     {
657         auto pic = cover_list.front();
658         if (pic != nullptr)
659         {
660             const auto& pic_data = pic->data();
661             //获取专辑封面
662             cover_contents.assign(pic_data.data(), pic_data.size());
663 
664             std::wstring img_type = pic->mimeType().toCWString();
665             type = GetPicType(img_type);
666         }
667     }
668     return cover_contents;
669 }
670 
671 string CTagLibHelper::GetMp3AlbumCover(const std::wstring& file_path, int& type)
672 {
673     string cover_contents;
674     MPEG::File file(file_path.c_str());
675     auto id3v2 = file.ID3v2Tag();
676     GetId3v2AlbumCover(id3v2, cover_contents, type);
677     return cover_contents;
678 }
679 
680 string CTagLibHelper::GetAsfAlbumCover(const std::wstring& file_path, int& type)
681 {
682     string cover_contents;
683     ASF::File file(file_path.c_str());
684     auto tag = file.tag();
685     if (tag != nullptr)
686     {
687         ASF::AttributeList attr = tag->attribute("WM/Picture");
688         if (!attr.isEmpty())
689         {
690             ASF::Picture picture = attr.front().toPicture();
691             auto pic_data = picture.picture();
692             cover_contents.assign(pic_data.data(), pic_data.size());
693             std::wstring img_type = picture.mimeType().toCWString();
694             type = GetPicType(img_type);
695         }
696     }
697     return cover_contents;
698 }
699 
700 string CTagLibHelper::GetWavAlbumCover(const std::wstring& file_path, int& type)
701 {
702     string cover_contents;
703     RIFF::WAV::File file(file_path.c_str());
704     auto id3v2 = file.ID3v2Tag();
705     GetId3v2AlbumCover(id3v2, cover_contents, type);
706     return cover_contents;
707 }
708 
709 string CTagLibHelper::GetTtaAlbumCover(const std::wstring& file_path, int& type)
710 {
711     string cover_contents;
712     TrueAudio::File file(file_path.c_str());
713     auto id3v2 = file.ID3v2Tag();
714     GetId3v2AlbumCover(id3v2, cover_contents, type);
715     return cover_contents;
716 }
717 
718 string CTagLibHelper::GetApeAlbumCover(const std::wstring& file_path, int& type)
719 {
720     string cover_contents;
721     APE::File file(file_path.c_str());
722     auto tag = file.APETag();
723     GetApeTagAlbumCover(tag, cover_contents, type);
724     return cover_contents;
725 }
726 
727 
728 string CTagLibHelper::GetOggAlbumCover(const std::wstring& file_path, int& type)
729 {
730     string cover_contents;
731     Ogg::Vorbis::File file(file_path.c_str());
732     auto tag = file.tag();
733     if (tag != nullptr)
734     {
735         GetXiphCommentAlbumCover(tag, cover_contents, type);
736     }
737     return cover_contents;
738 }
739 
740 string CTagLibHelper::GetOpusAlbumCover(const std::wstring& file_path, int& type)
741 {
742     string cover_contents;
743     Ogg::Opus::File file(file_path.c_str());
744     auto tag = file.tag();
745     if (tag != nullptr)
746     {
747         GetXiphCommentAlbumCover(tag, cover_contents, type);
748     }
749     return cover_contents;
750 }
751 
752 string CTagLibHelper::GetSpxAlbumCover(const std::wstring& file_path, int& type)
753 {
754     string cover_contents;
755     Ogg::Speex::File file(file_path.c_str());
756     auto tag = file.tag();
757     if (tag != nullptr)
758     {
759         GetXiphCommentAlbumCover(tag, cover_contents, type);
760     }
761     return cover_contents;
762 }
763 
764 string CTagLibHelper::GetAiffAlbumCover(const std::wstring& file_path, int& type)
765 {
766     string cover_contents;
767     RIFF::AIFF::File file(file_path.c_str());
768     auto id3v2 = file.tag();
769     GetId3v2AlbumCover(id3v2, cover_contents, type);
770     return cover_contents;
771 
772 }
773 
774 string CTagLibHelper::GetMpcAlbumCover(const std::wstring& file_path, int& type)
775 {
776     string cover_contents;
777     MPC::File file(file_path.c_str());
778     auto ape_tag = file.APETag();
779     GetApeTagAlbumCover(ape_tag, cover_contents, type);
780     return cover_contents;
781 }
782 
783 string CTagLibHelper::GetWavePackAlbumCover(const std::wstring& file_path, int& type)
784 {
785     string cover_contents;
786     WavPack::File file(file_path.c_str());
787     auto ape_tag = file.APETag();
788     GetApeTagAlbumCover(ape_tag, cover_contents, type);
789     return cover_contents;
790 }
791 
792 void CTagLibHelper::GetFlacTagInfo(SongInfo& song_info)
793 {
794     FLAC::File file(song_info.file_path.c_str());
795     if (file.hasID3v1Tag())
796         song_info.tag_type |= T_ID3V1;
797     if (file.hasID3v2Tag())
798         song_info.tag_type |= T_ID3V2;
799     auto tag = file.tag();
800     if (tag != nullptr)
801     {
802         TagToSongInfo(song_info, tag, true);
803     }
804     std::map<std::wstring, std::wstring> property_map;
805     getFlacPropertyMap(file, property_map);
806     OtherPropertyToSongInfo(song_info, property_map);
807 }
808 
809 void CTagLibHelper::GetM4aTagInfo(SongInfo& song_info)
810 {
811     MP4::File file(song_info.file_path.c_str());
812     if (file.hasMP4Tag())
813         song_info.tag_type |= T_MP4;
814     auto tag = file.tag();
815     if (tag != nullptr)
816     {
817         TagToSongInfo(song_info, tag, false);
818     }
819     std::map<std::wstring, std::wstring> property_map;
820     getM4aPropertyMap(file, property_map);
821     OtherPropertyToSongInfo(song_info, property_map);
822 }
823 
824 void CTagLibHelper::GetMpegTagInfo(SongInfo& song_info)
825 {
826     MPEG::File file(song_info.file_path.c_str());
827     if (file.hasID3v1Tag())
828         song_info.tag_type |= T_ID3V1;
829     if (file.hasID3v2Tag())
830         song_info.tag_type |= T_ID3V2;
831     if (file.hasAPETag())
832         song_info.tag_type |= T_APE;
833 
834     auto tag = file.tag();
835     if (tag != nullptr)
836     {
837         TagToSongInfo(song_info, tag, true);
838     }
839     std::map<std::wstring, std::wstring> property_map;
840     getMpegPropertyMap(file, property_map);
841     OtherPropertyToSongInfo(song_info, property_map);
842 }
843 
844 void CTagLibHelper::GetAsfTagInfo(SongInfo& song_info)
845 {
846     ASF::File file(song_info.file_path.c_str());
847     auto tag = file.tag();
848     if (tag != nullptr)
849     {
850         TagToSongInfo(song_info, tag, false);
851     }
852     std::map<std::wstring, std::wstring> property_map;
853     getAsfPropertyMap(file, property_map);
854     OtherPropertyToSongInfo(song_info, property_map);
855 }
856 
857 void CTagLibHelper::GetApeTagInfo(SongInfo& song_info)
858 {
859     APE::File file(song_info.file_path.c_str());
860     if (file.hasID3v1Tag())
861         song_info.tag_type |= T_ID3V1;
862     if (file.hasAPETag())
863         song_info.tag_type |= T_APE;
864 
865     auto tag = file.tag();
866     if (tag != nullptr)
867     {
868         TagToSongInfo(song_info, tag, true);
869     }
870     std::map<std::wstring, std::wstring> property_map;
871     getApePropertyMap(file, property_map);
872     OtherPropertyToSongInfo(song_info, property_map);
873 }
874 
875 void CTagLibHelper::GetWavTagInfo(SongInfo& song_info)
876 {
877     RIFF::WAV::File file(song_info.file_path.c_str());
878     if (file.hasID3v2Tag())
879         song_info.tag_type |= T_ID3V2;
880     if (file.hasInfoTag())
881         song_info.tag_type |= T_RIFF;
882     auto tag = file.tag();
883     if (tag != nullptr)
884     {
885         TagToSongInfo(song_info, tag, false);
886     }
887     std::map<std::wstring, std::wstring> property_map;
888     getWavPropertyMap(file, property_map);
889     OtherPropertyToSongInfo(song_info, property_map);
890 }
891 
892 void CTagLibHelper::GetOggTagInfo(SongInfo& song_info)
893 {
894     Vorbis::File file(song_info.file_path.c_str());
895     auto tag = file.tag();
896     if (tag != nullptr)
897     {
898         TagToSongInfo(song_info, tag, false);
899     }
900     std::map<std::wstring, std::wstring> property_map;
901     getOggPropertyMap(file, property_map);
902     OtherPropertyToSongInfo(song_info, property_map);
903 }
904 
905 void CTagLibHelper::GetMpcTagInfo(SongInfo& song_info)
906 {
907     MPC::File file(song_info.file_path.c_str());
908     if (file.hasAPETag())
909         song_info.tag_type |= T_APE;
910     if (file.hasID3v1Tag())
911         song_info.tag_type |= T_ID3V1;
912     auto tag = file.tag();
913     if (tag != nullptr)
914     {
915         TagToSongInfo(song_info, tag, true);
916     }
917     std::map<std::wstring, std::wstring> property_map;
918     getMpcPropertyMap(file, property_map);
919     OtherPropertyToSongInfo(song_info, property_map);
920 }
921 
922 void CTagLibHelper::GetOpusTagInfo(SongInfo& song_info)
923 {
924     Ogg::Opus::File file(song_info.file_path.c_str());
925     auto tag = file.tag();
926     if (tag != nullptr)
927     {
928         TagToSongInfo(song_info, tag, false);
929     }
930     std::map<std::wstring, std::wstring> property_map;
931     getOpusPropertyMap(file, property_map);
932     OtherPropertyToSongInfo(song_info, property_map);
933 }
934 
935 void CTagLibHelper::GetWavPackTagInfo(SongInfo& song_info)
936 {
937     WavPack::File file(song_info.file_path.c_str());
938     if (file.hasAPETag())
939         song_info.tag_type |= T_APE;
940     if (file.hasID3v1Tag())
941         song_info.tag_type |= T_ID3V1;
942     auto tag = file.tag();
943     if (tag != nullptr)
944     {
945         TagToSongInfo(song_info, tag, true);
946     }
947     std::map<std::wstring, std::wstring> property_map;
948     getWavPackPropertyMap(file, property_map);
949     OtherPropertyToSongInfo(song_info, property_map);
950 }
951 
952 void CTagLibHelper::GetTtaTagInfo(SongInfo& song_info)
953 {
954     TrueAudio::File file(song_info.file_path.c_str());
955     if (file.hasID3v1Tag())
956         song_info.tag_type |= T_ID3V1;
957     if (file.hasID3v2Tag())
958         song_info.tag_type |= T_ID3V2;
959     auto tag = file.tag();
960     if (tag != nullptr)
961     {
962         TagToSongInfo(song_info, tag, true);
963     }
964     std::map<std::wstring, std::wstring> property_map;
965     getTtaPropertyMap(file, property_map);
966     OtherPropertyToSongInfo(song_info, property_map);
967 }
968 
969 void CTagLibHelper::GetAiffTagInfo(SongInfo& song_info)
970 {
971     RIFF::AIFF::File file(song_info.file_path.c_str());
972     if (file.hasID3v2Tag())
973         song_info.tag_type |= T_ID3V2;
974     auto tag = file.tag();
975     if (tag != nullptr)
976     {
977         TagToSongInfo(song_info, tag, false);
978     }
979     std::map<std::wstring, std::wstring> property_map;
980     getAiffPropertyMap(file, property_map);
981     OtherPropertyToSongInfo(song_info, property_map);
982 }
983 
984 void CTagLibHelper::GetSpxTagInfo(SongInfo& song_info)
985 {
986     Ogg::Speex::File file(song_info.file_path.c_str());
987     auto tag = file.tag();
988     if (tag != nullptr)
989     {
990         TagToSongInfo(song_info, tag, false);
991     }
992     std::map<std::wstring, std::wstring> property_map;
993     getSpxPropertyMap(file, property_map);
994     OtherPropertyToSongInfo(song_info, property_map);
995 }
996 
997 void CTagLibHelper::GetAnyFileTagInfo(SongInfo& song_info)
998 {
999     FileRef file(song_info.file_path.c_str());
1000     auto tag = file.tag();
1001     if (tag != nullptr)
1002     {
1003         TagToSongInfo(song_info, tag, false);
1004     }
1005     std::map<std::wstring, std::wstring> property_map;
1006     getAnyFilePropertyMap(file, property_map);
1007     OtherPropertyToSongInfo(song_info, property_map);
1008 }
1009 
1010 void CTagLibHelper::GetFlacPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1011 {
1012     FLAC::File file(file_path.c_str());
1013     getFlacPropertyMap(file, property_map);
1014 }
1015 
1016 void CTagLibHelper::GetM4aPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1017 {
1018     MP4::File file(file_path.c_str());
1019     getM4aPropertyMap(file, property_map);
1020 }
1021 
1022 void CTagLibHelper::GetMpegPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1023 {
1024     MPEG::File file(file_path.c_str());
1025     getMpegPropertyMap(file, property_map);
1026 }
1027 
1028 void CTagLibHelper::GetAsfPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1029 {
1030     ASF::File file(file_path.c_str());
1031     getAsfPropertyMap(file, property_map);
1032 }
1033 
1034 void CTagLibHelper::GetApePropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1035 {
1036     APE::File file(file_path.c_str());
1037     getApePropertyMap(file, property_map);
1038 }
1039 
1040 void CTagLibHelper::GetWavPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1041 {
1042     RIFF::WAV::File file(file_path.c_str());
1043     getWavPropertyMap(file, property_map);
1044 }
1045 
1046 void CTagLibHelper::GetOggPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1047 {
1048     Vorbis::File file(file_path.c_str());
1049     getOggPropertyMap(file, property_map);
1050 }
1051 
1052 void CTagLibHelper::GetMpcPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1053 {
1054     MPC::File file(file_path.c_str());
1055     getMpcPropertyMap(file, property_map);
1056 }
1057 
1058 void CTagLibHelper::GetOpusPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1059 {
1060     Ogg::Opus::File file(file_path.c_str());
1061     getOpusPropertyMap(file, property_map);
1062 }
1063 
1064 void CTagLibHelper::GetWavPackPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1065 {
1066     WavPack::File file(file_path.c_str());
1067     getWavPackPropertyMap(file, property_map);
1068 }
1069 
1070 void CTagLibHelper::GetTtaPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1071 {
1072     TrueAudio::File file(file_path.c_str());
1073     getTtaPropertyMap(file, property_map);
1074 }
1075 
1076 void CTagLibHelper::GetAiffPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1077 {
1078     RIFF::AIFF::File file(file_path.c_str());
1079     getAiffPropertyMap(file, property_map);
1080 }
1081 
1082 void CTagLibHelper::GetSpxPropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1083 {
1084     Ogg::Speex::File file(file_path.c_str());
1085     getSpxPropertyMap(file, property_map);
1086 }
1087 
1088 void CTagLibHelper::GetAnyFilePropertyMap(const std::wstring& file_path, std::map<std::wstring, std::wstring>& property_map)
1089 {
1090     FileRef file(file_path.c_str());
1091     getAnyFilePropertyMap(file, property_map);
1092 }
1093 
1094 std::wstring CTagLibHelper::GetMpegLyric(const std::wstring& file_path)
1095 {
1096     MPEG::File file(file_path.c_str());
1097     auto id3v2 = file.ID3v2Tag();
1098     return GetId3v2Lyric(id3v2);
1099 }
1100 
1101 std::wstring CTagLibHelper::GetM4aLyric(const std::wstring& file_path)
1102 {
1103     std::wstring lyrics;
1104     MP4::File file(file_path.c_str());
1105     auto tag = file.tag();
1106     if (tag != nullptr)
1107     {
1108         auto item_map = file.tag()->itemMap();
1109         auto lyric_item = item_map[STR_MP4_LYRICS_TAG].toStringList();;
1110         if (!lyric_item.isEmpty())
1111             lyrics = lyric_item.front().toWString();
1112     }
1113     return lyrics;
1114 }
1115 
1116 std::wstring CTagLibHelper::GetFlacLyric(const std::wstring& file_path)
1117 {
1118     std::wstring lyrics;
1119     FLAC::File file(file_path.c_str());
1120     auto properties = file.properties();
1121     if (file.isValid())
1122     {
1123         auto lyric_item = properties[STR_FLAC_LYRIC_TAG];
1124         if (!lyric_item.isEmpty())
1125         {
1126             lyrics = lyric_item.front().toWString();
1127         }
1128     }
1129     return lyrics;
1130 }
1131 
1132 std::wstring CTagLibHelper::GetAsfLyric(const std::wstring& file_path)
1133 {
1134     std::wstring lyrics;
1135     ASF::File file(file_path.c_str());
1136     if (file.isValid())
1137     {
1138         auto properties = file.properties();
1139         auto lyric_item = properties[STR_ASF_LYRIC_TAG];
1140         if (!lyric_item.isEmpty())
1141         {
1142             lyrics = lyric_item.front().toWString();
1143         }
1144     }
1145     return lyrics;
1146 }
1147 
1148 
1149 std::wstring CTagLibHelper::GetWavLyric(const std::wstring& file_path)
1150 {
1151     RIFF::WAV::File file(file_path.c_str());
1152     auto id3v2 = file.ID3v2Tag();
1153     return GetId3v2Lyric(id3v2);
1154 
1155 }
1156 
1157 bool CTagLibHelper::WriteMpegLyric(const std::wstring& file_path, const std::wstring& lyric_contents)
1158 {
1159     std::wstring lyrics;
1160     MPEG::File file(file_path.c_str());
1161     auto id3v2 = file.ID3v2Tag();
1162     WriteId3v2Lyric(id3v2, lyric_contents);
1163     int tags = MPEG::File::ID3v2;
1164     if (file.hasAPETag())
1165         tags |= MPEG::File::APE;
1166     bool saved = file.save(tags, File::StripOthers, GetWriteId3v2Version());
1167     return saved;
1168 }
1169 
1170 bool CTagLibHelper::WriteFlacLyric(const std::wstring& file_path, const std::wstring& lyric_contents)
1171 {
1172     FLAC::File file(file_path.c_str());
1173     if (file.isValid())
1174     {
1175         auto properties = file.properties();
1176         if (lyric_contents.empty())
1177         {
1178             properties.erase(STR_FLAC_LYRIC_TAG);
1179         }
1180         else
1181         {
1182             StringList lyric_item;
1183             lyric_item.append(lyric_contents);
1184             properties[STR_FLAC_LYRIC_TAG] = lyric_item;
1185         }
1186         file.setProperties(properties);
1187         bool saved = file.save();
1188         return saved;
1189     }
1190     return false;
1191 }
1192 
1193 bool CTagLibHelper::WriteM4aLyric(const std::wstring& file_path, const std::wstring& lyric_contents)
1194 {
1195     MP4::File file(file_path.c_str());
1196     auto tag = file.tag();
1197     if (tag != nullptr)
1198     {
1199         if (lyric_contents.empty())
1200         {
1201             tag->removeItem(STR_MP4_LYRICS_TAG);
1202         }
1203         else
1204         {
1205             StringList lyric_list;
1206             lyric_list.append(lyric_contents);
1207             MP4::Item lyrics_item(lyric_list);
1208             tag->setItem(STR_MP4_LYRICS_TAG, lyrics_item);
1209         }
1210         bool saved = file.save();
1211         return saved;
1212     }
1213     return false;
1214 }
1215 
1216 bool CTagLibHelper::WriteAsfLyric(const std::wstring& file_path, const std::wstring& lyric_contents)
1217 {
1218     ASF::File file(file_path.c_str());
1219     if (file.isValid())
1220     {
1221         auto properties = file.properties();
1222         if (lyric_contents.empty())
1223         {
1224             properties.erase(STR_ASF_LYRIC_TAG);
1225         }
1226         else
1227         {
1228             StringList lyric_item;
1229             lyric_item.append(lyric_contents);
1230             properties[STR_ASF_LYRIC_TAG] = lyric_item;
1231         }
1232         file.setProperties(properties);
1233         bool saved = file.save();
1234         return saved;
1235     }
1236     return false;
1237 }
1238 
1239 bool CTagLibHelper::WriteWavLyric(const std::wstring& file_path, const std::wstring& lyric_contents)
1240 {
1241     std::wstring lyrics;
1242     RIFF::WAV::File file(file_path.c_str());
1243     auto id3v2 = file.ID3v2Tag();
1244     WriteId3v2Lyric(id3v2, lyric_contents);
1245     bool saved = file.save(RIFF::WAV::File::TagTypes::AllTags, File::StripOthers, GetWriteId3v2Version());
1246     return saved;
1247 }
1248 
1249 bool CTagLibHelper::WriteMp3AlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist)
1250 {
1251     MPEG::File file(file_path.c_str());
1252     if (!file.isValid())
1253         return false;
1254 
1255     //先删除专辑封面
1256     if (remove_exist)
1257     {
1258         auto id3v2tag = file.ID3v2Tag();
1259         DeleteId3v2AlbumCover(id3v2tag);
1260     }
1261     if (!album_cover_path.empty())
1262     {
1263         auto id3v2tag = file.ID3v2Tag(true);
1264         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1265     }
1266     int tags = MPEG::File::ID3v2;
1267     if (file.hasAPETag())
1268         tags |= MPEG::File::APE;
1269     bool saved = file.save(tags, File::StripOthers, GetWriteId3v2Version());
1270     return saved;
1271 }
1272 
1273 bool CTagLibHelper::WriteFlacAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist)
1274 {
1275     FLAC::File file(file_path.c_str());
1276     if (!file.isValid())
1277         return false;
1278 
1279     //先删除专辑封面
1280     if (remove_exist)
1281     {
1282         file.removePictures();
1283     }
1284 
1285     if (!album_cover_path.empty())
1286     {
1287         ByteVector pic_data;
1288         FileToByteVector(pic_data, album_cover_path);
1289         FLAC::Picture* newpic = new FLAC::Picture();
1290         newpic->setType(FLAC::Picture::FrontCover);
1291         newpic->setData(pic_data);
1292         std::wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
1293         newpic->setMimeType(L"image/" + ext);
1294         file.addPicture(newpic);
1295     }
1296     bool saved = file.save();
1297     return saved;
1298 }
1299 
1300 bool CTagLibHelper::WriteM4aAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1301 {
1302     MP4::File file(file_path.c_str());
1303     if (!file.isValid())
1304         return false;
1305 
1306     auto tag = file.tag();
1307     if (tag == nullptr)
1308         return false;
1309 
1310     if (remove_exist)
1311     {
1312         if (tag->contains(STR_MP4_COVER_TAG))
1313         {
1314             tag->removeItem(STR_MP4_COVER_TAG);
1315         }
1316     }
1317 
1318     if (!album_cover_path.empty())
1319     {
1320         ByteVector pic_data;
1321         FileToByteVector(pic_data, album_cover_path);
1322         MP4::CoverArt::Format format = MP4::CoverArt::Format::Unknown;
1323         std::wstring ext = CFilePathHelper(album_cover_path).GetFileExtension();
1324         if (ext == L"jpg" || ext == L"jpeg")
1325             format = MP4::CoverArt::Format::JPEG;
1326         else if (ext == L"png")
1327             format = MP4::CoverArt::Format::PNG;
1328         else if (ext == L"gif")
1329             format = MP4::CoverArt::Format::GIF;
1330         else if (ext == L"bmp")
1331             format = MP4::CoverArt::Format::BMP;
1332         MP4::CoverArt cover_item(format, pic_data);
1333 
1334         auto cover_item_list = tag->item(STR_MP4_COVER_TAG).toCoverArtList();
1335         cover_item_list.append(cover_item);
1336         tag->setItem(STR_MP4_COVER_TAG, cover_item_list);
1337     }
1338     bool saved = file.save();
1339     return saved;
1340 }
1341 
1342 bool CTagLibHelper::WriteAsfAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1343 {
1344     ASF::File file(file_path.c_str());
1345     if (!file.isValid())
1346         return false;
1347 
1348     auto tag = file.tag();
1349     if (tag == nullptr)
1350         return false;
1351 
1352     if (remove_exist)
1353     {
1354         if (tag->contains(STR_ASF_COVER_TAG))
1355         {
1356             tag->removeItem(STR_ASF_COVER_TAG);
1357         }
1358     }
1359 
1360     if (!album_cover_path.empty())
1361     {
1362         ByteVector pic_data;
1363         FileToByteVector(pic_data, album_cover_path);
1364 
1365         ASF::Picture picture;
1366         std::wstring str_mine_type = L"image/" + CFilePathHelper(album_cover_path).GetFileExtension();
1367         picture.setMimeType(str_mine_type);
1368         picture.setType(ASF::Picture::FrontCover);
1369         picture.setPicture(pic_data);
1370         tag->setAttribute(STR_ASF_COVER_TAG, picture);
1371     }
1372     bool saved = file.save();
1373     return saved;
1374 }
1375 
1376 bool CTagLibHelper::WriteWavAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1377 {
1378     RIFF::WAV::File file(file_path.c_str());
1379     if (!file.isValid())
1380         return false;
1381 
1382     //先删除专辑封面
1383     auto id3v2tag = file.ID3v2Tag();
1384     if (remove_exist)
1385     {
1386         DeleteId3v2AlbumCover(id3v2tag);
1387     }
1388     if (!album_cover_path.empty())
1389     {
1390         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1391     }
1392     bool saved = file.save(RIFF::WAV::File::AllTags, File::StripOthers, GetWriteId3v2Version());
1393     return saved;
1394 }
1395 
1396 bool CTagLibHelper::WriteTtaAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1397 {
1398     TrueAudio::File file(file_path.c_str());
1399     if (!file.isValid())
1400         return false;
1401 
1402     if (remove_exist)
1403     {
1404         auto id3v2tag = file.ID3v2Tag();
1405         DeleteId3v2AlbumCover(id3v2tag);
1406     }
1407     if (!album_cover_path.empty())
1408     {
1409         auto id3v2tag = file.ID3v2Tag(true);
1410         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1411     }
1412     bool saved = file.save();
1413     return saved;
1414     return false;
1415 }
1416 
1417 bool CTagLibHelper::WriteApeAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1418 {
1419     APE::File file(file_path.c_str());
1420     if (!file.isValid())
1421         return false;
1422 
1423     auto tag = file.APETag(true);
1424     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1425     bool saved = file.save();
1426     return saved;
1427 }
1428 
1429 bool CTagLibHelper::WriteOggAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1430 {
1431     Ogg::Vorbis::File file(file_path.c_str());
1432     if (!file.isValid())
1433         return false;
1434 
1435     auto tag = file.tag();
1436     if (tag != nullptr)
1437     {
1438         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1439         bool saved = file.save();
1440         return saved;
1441     }
1442     return false;
1443 }
1444 
1445 bool CTagLibHelper::WriteOpusAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist)
1446 {
1447     Ogg::Opus::File file(file_path.c_str());
1448     if (!file.isValid())
1449         return false;
1450 
1451     auto tag = file.tag();
1452     if (tag != nullptr)
1453     {
1454         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1455         bool saved = file.save();
1456         return saved;
1457     }
1458     return false;
1459 }
1460 
1461 bool CTagLibHelper::WriteSpxAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1462 {
1463     Ogg::Speex::File file(file_path.c_str());
1464     if (!file.isValid())
1465         return false;
1466 
1467     auto tag = file.tag();
1468     if (tag != nullptr)
1469     {
1470         WriteXiphCommentAlbumCover(tag, album_cover_path, remove_exist);
1471         bool saved = file.save();
1472         return saved;
1473     }
1474     return false;
1475 }
1476 
1477 bool CTagLibHelper::WriteAiffAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1478 {
1479     RIFF::AIFF::File file(file_path.c_str());
1480     if (!file.isValid())
1481         return false;
1482 
1483     //先删除专辑封面
1484     auto id3v2tag = file.tag();
1485     if (remove_exist)
1486     {
1487         DeleteId3v2AlbumCover(id3v2tag);
1488     }
1489     if (!album_cover_path.empty())
1490     {
1491         WriteId3v2AlbumCover(id3v2tag, album_cover_path);
1492     }
1493     bool saved = file.save(GetWriteId3v2Version());
1494     return saved;
1495 }
1496 
1497 bool CTagLibHelper::WriteMpcAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1498 {
1499     MPC::File file(file_path.c_str());
1500     if (!file.isValid())
1501         return false;
1502 
1503     auto tag = file.APETag(true);
1504     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1505     bool saved = file.save();
1506     return saved;
1507 }
1508 
1509 bool CTagLibHelper::WriteWavePackAlbumCover(const std::wstring& file_path, const std::wstring& album_cover_path, bool remove_exist /*= true*/)
1510 {
1511     WavPack::File file(file_path.c_str());
1512     if (!file.isValid())
1513         return false;
1514 
1515     auto tag = file.APETag(true);
1516     WriteApeTagAlbumCover(tag, album_cover_path, remove_exist);
1517     bool saved = file.save();
1518     return saved;
1519 }
1520 
1521 bool CTagLibHelper::WriteMpegTag(const SongInfo& song_info)
1522 {
1523     MPEG::File file(song_info.file_path.c_str());
1524     auto tag = file.tag();
1525     SongInfoToTag(song_info, tag);
1526     int tags = MPEG::File::ID3v2;
1527     //if (file.hasID3v1Tag())
1528     //    tags |= MPEG::File::ID3v1;
1529     if (file.hasAPETag())
1530         tags |= MPEG::File::APE;
1531     WriteOtherProperties(song_info, file);
1532     bool saved = file.save(tags, File::StripOthers, GetWriteId3v2Version());
1533     return saved;
1534 }
1535 
1536 bool CTagLibHelper::WriteFlacTag(const SongInfo& song_info)
1537 {
1538     FLAC::File file(song_info.file_path.c_str());
1539     auto tag = file.tag();
1540     SongInfoToTag(song_info, tag);
1541     WriteOtherProperties(song_info, file);
1542     bool saved = file.save();
1543     return saved;
1544 }
1545 
1546 bool CTagLibHelper::WriteM4aTag(const SongInfo& song_info)
1547 {
1548     MP4::File file(song_info.file_path.c_str());
1549     auto tag = file.tag();
1550     SongInfoToTag(song_info, tag);
1551     bool saved = file.save();
1552     return saved;
1553 }
1554 
1555 bool CTagLibHelper::WriteWavTag(const SongInfo& song_info)
1556 {
1557     RIFF::WAV::File file(song_info.file_path.c_str());
1558     auto tag = file.tag();
1559     SongInfoToTag(song_info, tag);
1560     WriteOtherProperties(song_info, file);
1561     bool saved = file.save(RIFF::WAV::File::AllTags, File::StripOthers, GetWriteId3v2Version());
1562     return saved;
1563 }
1564 
1565 bool CTagLibHelper::WriteOggTag(const SongInfo& song_info)
1566 {
1567     Vorbis::File file(song_info.file_path.c_str());
1568     auto tag = file.tag();
1569     SongInfoToTag(song_info, tag);
1570     WriteOtherProperties(song_info, file);
1571     bool saved = file.save();
1572     return saved;
1573 }
1574 
1575 bool CTagLibHelper::WriteApeTag(const SongInfo& song_info)
1576 {
1577     APE::File file(song_info.file_path.c_str());
1578     auto tag = file.tag();
1579     SongInfoToTag(song_info, tag);
1580     WriteOtherProperties(song_info, file);
1581     bool saved = file.save();
1582     return saved;
1583 }
1584 
1585 bool CTagLibHelper::WriteMpcTag(const SongInfo& song_info)
1586 {
1587     MPC::File file(song_info.file_path.c_str());
1588     auto tag = file.tag();
1589     SongInfoToTag(song_info, tag);
1590     WriteOtherProperties(song_info, file);
1591     bool saved = file.save();
1592     return saved;
1593 }
1594 
1595 bool CTagLibHelper::WriteOpusTag(const SongInfo& song_info)
1596 {
1597     Ogg::Opus::File file(song_info.file_path.c_str());
1598     auto tag = file.tag();
1599     SongInfoToTag(song_info, tag);
1600     WriteOtherProperties(song_info, file);
1601     bool saved = file.save();
1602     return saved;
1603 }
1604 
1605 bool CTagLibHelper::WriteWavPackTag(const SongInfo& song_info)
1606 {
1607     WavPack::File file(song_info.file_path.c_str());
1608     auto tag = file.tag();
1609     SongInfoToTag(song_info, tag);
1610     WriteOtherProperties(song_info, file);
1611     bool saved = file.save();
1612     return saved;
1613 }
1614 
1615 bool CTagLibHelper::WriteTtaTag(const SongInfo& song_info)
1616 {
1617     TrueAudio::File file(song_info.file_path.c_str());
1618     auto tag = file.tag();
1619     SongInfoToTag(song_info, tag);
1620     WriteOtherProperties(song_info, file);
1621     bool saved = file.save();
1622     return saved;
1623 }
1624 
1625 bool CTagLibHelper::WriteAiffTag(const SongInfo& song_info)
1626 {
1627     RIFF::AIFF::File file(song_info.file_path.c_str());
1628     auto tag = file.tag();
1629     SongInfoToTag(song_info, tag);
1630     WriteOtherProperties(song_info, file);
1631     bool saved = file.save(GetWriteId3v2Version());
1632     return saved;
1633 }
1634 
1635 bool CTagLibHelper::WriteAsfTag(const SongInfo& song_info)
1636 {
1637     ASF::File file(song_info.file_path.c_str());
1638     auto tag = file.tag();
1639     SongInfoToTag(song_info, tag);
1640     WriteOtherProperties(song_info, file);
1641     bool saved = file.save();
1642     return saved;
1643 }
1644 
1645 bool CTagLibHelper::WriteSpxTag(const SongInfo& song_info)
1646 {
1647     Ogg::Speex::File file(song_info.file_path.c_str());
1648     auto tag = file.tag();
1649     SongInfoToTag(song_info, tag);
1650     WriteOtherProperties(song_info, file);
1651     bool saved = file.save();
1652     return saved;
1653 }
1654 
1655 std::wstring CTagLibHelper::GetApeCue(const std::wstring& file_path)
1656 {
1657     std::wstring cue_contents;
1658     APE::File file(file_path.c_str());
1659     auto tag = file.APETag();
1660     if (tag != nullptr)
1661     {
1662         auto item_list_map = tag->itemListMap();
1663         auto cue_item = item_list_map[STR_APE_CUE_TAG];
1664         cue_contents = cue_item.toString().toWString();
1665     }
1666     return cue_contents;
1667 }
1668 
1669 int CTagLibHelper::GetMepgRating(const std::wstring& file_path)
1670 {
1671     MPEG::File file(file_path.c_str());
1672     auto id3v2 = file.ID3v2Tag();
1673     return GetId3v2Rating(id3v2);
1674 }
1675 
1676 int CTagLibHelper::GetFlacRating(const std::wstring& file_path)
1677 {
1678     FLAC::File file(file_path.c_str());
1679     auto properties = file.properties();
1680     if (file.isValid())
1681     {
1682         auto rating_item = properties[STR_FLAC_RATING_TAG];
1683         if (!rating_item.isEmpty())
1684         {
1685             int rating = _wtoi(rating_item.front().toWString().c_str());
1686             return rating;
1687         }
1688     }
1689     return 0;
1690 
1691 }
1692 
1693 int CTagLibHelper::GetWmaRating(const std::wstring& file_path)
1694 {
1695     int rate{};
1696     ASF::File file(file_path.c_str());
1697     if (file.isValid())
1698     {
1699         ASF::Tag* tag = file.tag();
1700         auto rating_str = tag->rating();
1701         rate = _wtoi(rating_str.toWString().c_str());
1702     }
1703     return rate;
1704 }
1705 
1706 bool CTagLibHelper::WriteMpegRating(const std::wstring& file_path, int rate)
1707 {
1708     MPEG::File file(file_path.c_str());
1709     auto id3v2 = file.ID3v2Tag();
1710     WriteId3v2Rating(id3v2, rate);
1711     int tags = MPEG::File::ID3v2;
1712     if (file.hasAPETag())
1713         tags |= MPEG::File::APE;
1714     bool saved = file.save(tags, File::StripOthers, GetWriteId3v2Version());
1715     return saved;
1716 }
1717 
1718 bool CTagLibHelper::WriteFlacRating(const std::wstring& file_path, int rate)
1719 {
1720     FLAC::File file(file_path.c_str());
1721     if (file.isValid())
1722     {
1723         auto properties = file.properties();
1724         properties[STR_FLAC_RATING_TAG].clear();
1725         properties[STR_FLAC_RATING_TAG].append(std::to_wstring(rate).c_str());
1726         file.setProperties(properties);
1727         bool saved = file.save();
1728         return saved;
1729     }
1730     return false;
1731 }
1732 
1733 bool CTagLibHelper::WriteWmaRating(const std::wstring& file_path, int rate)
1734 {
1735     ASF::File file(file_path.c_str());
1736     if (file.isValid())
1737     {
1738         auto tag = file.tag();
1739         tag->setRating(std::to_wstring(rate));
1740         bool saved = file.save();
1741         return saved;
1742     }
1743     return false;
1744 }
1745 
1746 TagLib::ID3v2::Version CTagLibHelper::GetWriteId3v2Version()
1747 {
1748     return (m_write_id3v2_3 ? ID3v2::Version::v3 : ID3v2::Version::v4);
1749 }
1750