1 /****************************************************************************************
2 * Copyright (c) 2010 Sergey Ivanov <123kash@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify it under *
5 * the terms of the GNU General Public License as published by the Free Software *
6 * Foundation; either version 2 of the License, or (at your option) any later *
7 * version. *
8 * *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU General Public License along with *
14 * this program. If not, see <http://www.gnu.org/licenses/>. *
15 ****************************************************************************************/
16
17
18 #include "ASFTagHelper.h"
19
20 #include "StringHelper.h"
21
22 #include <QBuffer>
23 #include <QImage>
24
25 #include <asfattribute.h>
26
27 using namespace Meta::Tag;
28
ASFTagHelper(TagLib::Tag * tag,TagLib::ASF::Tag * asfTag,Amarok::FileType fileType)29 ASFTagHelper::ASFTagHelper( TagLib::Tag *tag, TagLib::ASF::Tag *asfTag, Amarok::FileType fileType )
30 : TagHelper( tag, fileType )
31 , m_tag( asfTag )
32 {
33 m_fieldMap.insert( Meta::valAlbumArtist, TagLib::String( "WM/AlbumArtist" ) );
34 m_fieldMap.insert( Meta::valBpm, TagLib::String( "WM/BeatsPerMinute" ) );
35 m_fieldMap.insert( Meta::valCompilation, TagLib::String( "Amarok/Compilation" ) ); //Not standard tag
36 m_fieldMap.insert( Meta::valComposer, TagLib::String( "WM/Composer" ) );
37 m_fieldMap.insert( Meta::valDiscNr, TagLib::String( "WM/PartOfSet" ) );
38 m_fieldMap.insert( Meta::valHasCover, TagLib::String( "WM/Picture" ) );
39 m_fieldMap.insert( Meta::valPlaycount, TagLib::String( "FMPS/Playcount" ) );
40 m_fieldMap.insert( Meta::valRating, TagLib::String( "FMPS/Rating" ) );
41 m_fieldMap.insert( Meta::valScore, TagLib::String( "FMPS/Rating_Amarok_Score" ) );
42 m_fieldMap.insert( Meta::valLyrics, TagLib::String( "WM/Lyrics" ) );
43
44 m_uidFieldMap.insert( UIDAFT, TagLib::String( "Amarok/AFTv1" ) );
45 }
46
47 Meta::FieldHash
tags() const48 ASFTagHelper::tags() const
49 {
50 Meta::FieldHash data = TagHelper::tags();
51
52 TagLib::ASF::AttributeListMap map = m_tag->attributeListMap();
53 for( TagLib::ASF::AttributeListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
54 {
55 if( !it->second.size() )
56 continue;
57
58 qint64 field;
59 TagLib::ASF::Attribute value = it->second[0];
60 QString strValue = TStringToQString( value.toString() );
61 if( ( field = fieldName( it->first ) ) )
62 {
63 if( field == Meta::valBpm || field == Meta::valPlaycount )
64 data.insert( field, value.toUInt() );
65 else if( field == Meta::valRating )
66 data.insert( field, qRound( strValue.toFloat() * 10.0 ) );
67 else if( field == Meta::valScore )
68 data.insert( field, strValue.toFloat() * 100.0 );
69 else if( field == Meta::valDiscNr )
70 data.insert( field, value.toUInt() );
71 else if( field == Meta::valCompilation )
72 data.insert( field, value.toBool() );
73 else if( field == Meta::valHasCover )
74 {
75 for( TagLib::ASF::AttributeList::ConstIterator cover = it->second.begin(); cover != it->second.end(); ++cover )
76 {
77 if( cover->type() != TagLib::ASF::Attribute::BytesType )
78 continue;
79
80 TagLib::ASF::Picture pict = cover->toPicture();
81 if( ( pict.type() == TagLib::ASF::Picture::FrontCover ||
82 pict.type() == TagLib::ASF::Picture::Other ) &&
83 pict.dataSize() > MIN_COVER_SIZE )
84 {
85 data.insert( field, true );
86 break;
87 }
88 }
89 }
90 else
91 data.insert( field, strValue );
92 }
93 else if( it->first == uidFieldName( UIDAFT ) && isValidUID( strValue, UIDAFT ) )
94 data.insert( Meta::valUniqueId, strValue );
95 }
96
97 return data;
98 }
99
100 bool
setTags(const Meta::FieldHash & changes)101 ASFTagHelper::setTags( const Meta::FieldHash &changes )
102 {
103 bool modified = TagHelper::setTags( changes );
104
105 foreach( const qint64 key, changes.keys() )
106 {
107 QVariant value = changes.value( key );
108 TagLib::String field = fieldName( key );
109
110 if( !field.isNull() && !field.isEmpty() )
111 {
112 if( key == Meta::valHasCover )
113 continue;
114 // http://gitorious.org/~jefferai/xdg-specs/jefferais-xdg-specs/blobs/mediaspecs/specifications/FMPSpecs/specification.txt sais that mp4 tags should be saved as strings
115 if( key == Meta::valHasCover )
116 continue;
117 else if( key == Meta::valRating )
118 m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ) ) );
119 else if( key == Meta::valScore )
120 m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ) ) );
121 else if( key == Meta::valBpm || key == Meta::valDiscNr )
122 m_tag->setAttribute( field, TagLib::ASF::Attribute( value.toUInt() ) );
123 else if( key == Meta::valCompilation )
124 m_tag->setAttribute( field, TagLib::ASF::Attribute( value.toBool() ) );
125 else
126 m_tag->setAttribute( field, TagLib::ASF::Attribute( Qt4QStringToTString( value.toString() ) ) );
127
128 modified = true;
129 }
130 else if( key == Meta::valUniqueId )
131 {
132 QPair < UIDType, QString > uidPair = splitUID( value.toString() );
133 if( uidPair.first == UIDInvalid )
134 continue;
135
136 m_tag->setAttribute( uidFieldName( uidPair.first ),
137 TagLib::ASF::Attribute( Qt4QStringToTString( uidPair.second ) ) );
138 modified = true;
139 }
140 }
141
142 return modified;
143 }
144
145 bool
hasEmbeddedCover() const146 ASFTagHelper::hasEmbeddedCover() const
147 {
148 TagLib::ASF::AttributeListMap map = m_tag->attributeListMap();
149 TagLib::String name = fieldName( Meta::valHasCover );
150 for( TagLib::ASF::AttributeListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
151 {
152 if( it->first == name )
153 {
154 TagLib::ASF::AttributeList coverList = it->second;
155 for( TagLib::ASF::AttributeList::ConstIterator cover = coverList.begin(); cover != coverList.end(); ++cover )
156 {
157 if( cover->type() != TagLib::ASF::Attribute::BytesType )
158 continue;
159
160 TagLib::ASF::Picture pict = cover->toPicture();
161 if( ( pict.type() == TagLib::ASF::Picture::FrontCover ||
162 pict.type() == TagLib::ASF::Picture::Other ) &&
163 pict.dataSize() > MIN_COVER_SIZE )
164 {
165 return true;
166 }
167 }
168 }
169 }
170
171 return false;
172 }
173
174 QImage
embeddedCover() const175 ASFTagHelper::embeddedCover() const
176 {
177 TagLib::ASF::AttributeListMap map = m_tag->attributeListMap();
178 TagLib::String name = fieldName( Meta::valHasCover );
179
180 TagLib::ASF::Picture cover, otherCover;
181 bool hasCover = false, hasOtherCover = false;
182
183 for( TagLib::ASF::AttributeListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
184 {
185 if( it->first == name )
186 {
187 TagLib::ASF::AttributeList coverList = it->second;
188 for( TagLib::ASF::AttributeList::ConstIterator it = coverList.begin(); it != coverList.end(); ++it )
189 {
190 if( it->type() != TagLib::ASF::Attribute::BytesType )
191 continue;
192
193 TagLib::ASF::Picture pict = it->toPicture();
194
195 if( pict.dataSize() < MIN_COVER_SIZE )
196 continue;
197
198 if( pict.type() == TagLib::ASF::Picture::FrontCover )
199 {
200 cover = pict;
201 hasCover = true;
202 }
203 else if( pict.type() == TagLib::ASF::Picture::Other )
204 {
205 otherCover = pict;
206 hasOtherCover = true;
207 }
208 }
209 }
210 }
211
212 if( !hasCover && hasOtherCover )
213 {
214 cover = otherCover;
215 hasCover = true;
216 }
217
218 if( !hasCover )
219 return QImage();
220
221 return QImage::fromData( ( uchar * ) cover.picture().data(), cover.picture().size() );
222 }
223
224 bool
setEmbeddedCover(const QImage & cover)225 ASFTagHelper::setEmbeddedCover( const QImage &cover )
226 {
227 QByteArray bytes;
228 QBuffer buffer( &bytes );
229
230 buffer.open( QIODevice::WriteOnly );
231
232 if( !cover.save( &buffer, "JPEG" ) )
233 {
234 buffer.close();
235 return false;
236 }
237
238 buffer.close();
239
240 TagLib::String name = fieldName( Meta::valHasCover );
241
242 // remove all covers
243 m_tag->removeItem( name );
244
245 // add new cover
246 TagLib::ASF::Picture picture;
247 picture.setPicture( TagLib::ByteVector( bytes.data(), bytes.count() ) );
248 picture.setType( TagLib::ASF::Picture::FrontCover );
249 picture.setMimeType( "image/jpeg" );
250 m_tag->addAttribute( name, TagLib::ASF::Attribute( picture.render() ) );
251
252 return true;
253 }
254