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