File indexing completed on 2024-05-19 04:49:32

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Téo Mrnjavac <teo@kde.org>                                        *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "TranscodingConfiguration.h"
0018 
0019 #include "TranscodingProperty.h"
0020 #include "TranscodingFormat.h"
0021 #include "TranscodingController.h"
0022 #include "core/support/Components.h"
0023 
0024 #include <KLocalizedString>
0025 
0026 using namespace Transcoding;
0027 
0028 QMap<Encoder, QString> Configuration::s_encoderNames;
0029 
0030 Configuration::Configuration( Encoder encoder, TrackSelection trackSelection )
0031     : m_encoder( encoder )
0032     , m_trackSelection( trackSelection )
0033 {
0034 }
0035 
0036 void
0037 Configuration::addProperty( const QByteArray &name, const QVariant &value )
0038 {
0039     m_values.insert( name, value );
0040 }
0041 
0042 QVariant
0043 Configuration::property( const QByteArray &name ) const
0044 {
0045     return m_values.value( name );
0046 }
0047 
0048 Configuration
0049 Configuration::fromConfigGroup( const KConfigGroup &serialized )
0050 {
0051     QString encoderName = serialized.readEntry( "Encoder", QString() );
0052     Encoder encoder = encoderNames().key( encoderName, INVALID );
0053     TrackSelection trackSelection = TrackSelection( serialized.readEntry( "TrackSelection", int( TranscodeAll ) ) );
0054     Configuration ret( encoder, trackSelection );
0055     if( !ret.isValid() )
0056         return ret; // return ret, so that its trackSelection value may be used
0057 
0058     Controller *controller = Amarok::Components::transcodingController();
0059     // reset controller to 0 if it doesn't contain encoder to prevent bogus format() call
0060     if( controller && !controller->allEncoders().contains( ret.encoder() ) )
0061         controller = nullptr;
0062     Format *format = controller ? controller->format( ret.encoder() ) : nullptr;
0063 
0064     PropertyList emptyList;
0065     foreach( const Property &property, format ? format->propertyList() : emptyList )
0066     {
0067         Configuration invalid( INVALID );
0068         QString key = QStringLiteral( "Parameter ").append( property.name() );
0069         QVariant value = serialized.readEntry( key, QString() /* does not work with QVariant() */ );
0070 
0071         if( !value.isValid() )
0072             return invalid;
0073         if( !value.canConvert( property.variantType() ) )
0074             return invalid;
0075         switch( property.type() )
0076         {
0077             case Property::TRADEOFF:
0078                 if( value.toInt() < property.min() )
0079                     return invalid;
0080                 if( value.toInt() > property.max() )
0081                     return invalid;
0082                 break;
0083         }
0084         ret.m_values.insert( property.name(), value );
0085     }
0086     return ret;
0087 }
0088 
0089 void
0090 Configuration::saveToConfigGroup( KConfigGroup &group ) const
0091 {
0092     group.deleteGroup(); // remove all keys
0093     Q_ASSERT( encoderNames().contains( m_encoder ) );
0094     QString encoderName = encoderNames().value( m_encoder );
0095     group.writeEntry( QStringLiteral( "Encoder" ), encoderName );
0096     group.writeEntry( QStringLiteral( "TrackSelection" ), int( m_trackSelection ) );
0097     QMapIterator<QByteArray, QVariant> it( m_values );
0098     while( it.hasNext() )
0099     {
0100         it.next();
0101         group.writeEntry( QStringLiteral( "Parameter " ).append( it.key() ), it.value() );
0102     }
0103 }
0104 
0105 QString
0106 Configuration::prettyName() const
0107 {
0108     if( !isValid() )
0109         return i18n( "Invalid" );
0110     if( isJustCopy() )
0111         return i18n( "Just Copy" );
0112 
0113     Format *format = Amarok::Components::transcodingController()->format( m_encoder );
0114     if( format->propertyList().isEmpty() )
0115         return formatPrettyPrefix();
0116 
0117     // we take only the first property into account, assume it's the most significant
0118     const Property &property = format->propertyList().first();
0119     QByteArray name = property.name();
0120     Q_ASSERT( m_values.contains( name ) );
0121     Q_ASSERT( m_values.value( name ).type() == property.variantType() );
0122     QString propertyText;
0123     switch( property.type() )
0124     {
0125         case Property::TRADEOFF:
0126         {
0127             const int currValue = m_values.value( name ).toInt();
0128             const int min = property.min();
0129             const int max = property.max();
0130             Q_ASSERT( min <= currValue && currValue <= max );
0131             if( property.valueLabels().size() == ( max - min + 1 ) )
0132                 propertyText = property.valueLabels().at( currValue - min );
0133             else
0134                 propertyText = i18nc( "%1 example: 'Compression level' %2 example: '5'",
0135                                       "%1 %2", property.prettyName(), currValue );
0136             break;
0137         }
0138     }
0139 
0140     return i18nc( "Displayed next to the \"Transcode:\" label. "
0141                   "%1 example: 'All Tracks to MP3' %2 example: 'VBR 175kb/s'",
0142                   "%1, %2", formatPrettyPrefix(), propertyText );
0143 
0144 }
0145 
0146 const QMap<Encoder, QString>&
0147 Configuration::encoderNames()
0148 {
0149     if( !s_encoderNames.isEmpty() )
0150         return s_encoderNames;
0151 
0152     s_encoderNames.insert( INVALID, QStringLiteral( "INVALID" ) );
0153     s_encoderNames.insert( JUST_COPY, QStringLiteral( "JUST_COPY" ) );
0154     s_encoderNames.insert( AAC, QStringLiteral( "AAC" ) );
0155     s_encoderNames.insert( ALAC, QStringLiteral( "ALAC" ) );
0156     s_encoderNames.insert( FLAC, QStringLiteral( "FLAC" ) );
0157     s_encoderNames.insert( MP3, QStringLiteral( "MP3" ) );
0158     s_encoderNames.insert( OPUS, QStringLiteral( "OPUS" ) );
0159     s_encoderNames.insert( VORBIS, QStringLiteral( "VORBIS" ) );
0160     s_encoderNames.insert( WMA2, QStringLiteral( "WMA2" ) );
0161     return s_encoderNames;
0162 }
0163 
0164 bool
0165 Configuration::isJustCopy( const Meta::TrackPtr &srcTrack,
0166                            const QStringList &playableFileTypes ) const
0167 {
0168     if( m_encoder == INVALID || m_encoder == JUST_COPY )
0169         return true;
0170 
0171     if( !srcTrack )
0172         return false;
0173 
0174     switch( m_trackSelection )
0175     {
0176         case TranscodeUnlessSameType:
0177         {
0178             QString srcExt = srcTrack->type();
0179             QString destExt = Amarok::Components::transcodingController()->format( m_encoder )->fileExtension();
0180             if( destExt.compare( srcExt, Qt::CaseInsensitive )  == 0 ) //if source and destination file formats are the same
0181                 return true;
0182             else
0183                 return false;
0184         }
0185         case TranscodeOnlyIfNeeded:
0186         {
0187             QString srcExt = srcTrack->type();
0188             //check if the file is already in a format supported by the target collection
0189             if( playableFileTypes.isEmpty() || playableFileTypes.contains( srcExt ) )
0190                 return true; // if isEmpty(), assume all formats compatible
0191             else
0192                 return false;
0193         }
0194         case TranscodeAll:
0195             return false;
0196     }
0197     return false; // shouldn't really get here
0198 }
0199 
0200 QString
0201 Configuration::formatPrettyPrefix() const
0202 {
0203     Format *format = Amarok::Components::transcodingController()->format( m_encoder );
0204 
0205     switch( m_trackSelection )
0206     {
0207         case TranscodeAll:
0208             return i18nc( "Displayed next to the \"Transcode:\" label. "
0209                           "%1 example: 'MP3'",
0210                           "All Tracks to %1", format->prettyName() );
0211         case TranscodeUnlessSameType:
0212             return i18nc( "Displayed next to the \"Transcode:\" label. "
0213                           "%1 example: 'MP3'",
0214                           "Non-%1 Tracks to %1", format->prettyName() );
0215         case TranscodeOnlyIfNeeded:
0216             return i18nc( "Displayed next to the \"Transcode:\" label. "
0217                           "%1 example: 'MP3'",
0218                           "When Needed to %1", format->prettyName() );
0219     }
0220     return format->prettyName();
0221 }
0222 
0223 void
0224 Configuration::setTrackSelection( TrackSelection trackSelection )
0225 {
0226     m_trackSelection = trackSelection;
0227 }
0228 
0229 bool
0230 Configuration::operator!=( const Configuration &other ) const
0231 {
0232     return m_encoder != other.m_encoder ||
0233             m_trackSelection != other.m_trackSelection;
0234 }