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