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