|
|
|
/* This file is part of the KDE project
|
|
|
|
* Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
|
|
|
|
* Copyright (C) 2002 Ryan Cumming <bodnar42@phalynx.dhs.org>
|
|
|
|
* Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public
|
|
|
|
* License as published by the Free Software Foundation version 2.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "kfile_mp3.h"
|
|
|
|
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kgenericfactory.h>
|
|
|
|
#include <kstringvalidator.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#include <tqdict.h>
|
|
|
|
#include <tqvalidator.h>
|
|
|
|
#include <tqcstring.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
|
|
|
|
#include <tstring.h>
|
|
|
|
#include <tag.h>
|
|
|
|
#include <mpegfile.h>
|
|
|
|
#include <id3v1genres.h>
|
|
|
|
#include <id3v2framefactory.h>
|
|
|
|
|
|
|
|
typedef KGenericFactory<KMp3Plugin> Mp3Factory;
|
|
|
|
|
|
|
|
K_EXPORT_COMPONENT_FACTORY(kfile_mp3, Mp3Factory( "kfile_mp3" ))
|
|
|
|
|
|
|
|
KMp3Plugin::KMp3Plugin(TQObject *parent, const char *name, const TQStringList &args)
|
|
|
|
: KFilePlugin(parent, name, args)
|
|
|
|
{
|
|
|
|
kdDebug(7034) << "mp3 plugin\n";
|
|
|
|
|
|
|
|
KFileMimeTypeInfo *info = addMimeTypeInfo("audio/x-mp3");
|
|
|
|
|
|
|
|
// id3 group
|
|
|
|
|
|
|
|
KFileMimeTypeInfo::GroupInfo *group = addGroupInfo(info, "id3", i18n("ID3 Tag"));
|
|
|
|
|
|
|
|
setAttributes(group, KFileMimeTypeInfo::Addable |
|
|
|
|
KFileMimeTypeInfo::Removable);
|
|
|
|
|
|
|
|
KFileMimeTypeInfo::ItemInfo *item;
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Title", i18n("Title"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
setHint(item, KFileMimeTypeInfo::Name);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Artist", i18n("Artist"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
setHint(item, KFileMimeTypeInfo::Author);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Album", i18n("Album"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Date", i18n("Year"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Comment", i18n("Comment"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
setHint(item, KFileMimeTypeInfo::Description);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Tracknumber", i18n("Track"), TQVariant::Int);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Genre", i18n("Genre"), TQVariant::String);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Modifiable);
|
|
|
|
|
|
|
|
// technical group
|
|
|
|
|
|
|
|
group = addGroupInfo(info, "Technical", i18n("Technical Details"));
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Version", i18n("Version"), TQVariant::Int);
|
|
|
|
setPrefix(item, i18n("MPEG "));
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Layer", i18n("Layer"), TQVariant::Int);
|
|
|
|
item = addItemInfo(group, "CRC", i18n("CRC"), TQVariant::Bool);
|
|
|
|
item = addItemInfo(group, "Bitrate", i18n("Bitrate"), TQVariant::Int);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Averaged);
|
|
|
|
setHint(item, KFileMimeTypeInfo::Bitrate);
|
|
|
|
setSuffix(item, i18n(" kbps"));
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Sample Rate", i18n("Sample Rate"), TQVariant::Int);
|
|
|
|
setSuffix(item, i18n("Hz"));
|
|
|
|
|
|
|
|
item = addItemInfo(group, "Channels", i18n("Channels"), TQVariant::Int);
|
|
|
|
item = addItemInfo(group, "Copyright", i18n("Copyright"), TQVariant::Bool);
|
|
|
|
item = addItemInfo(group, "Original", i18n("Original"), TQVariant::Bool);
|
|
|
|
item = addItemInfo(group, "Length", i18n("Length"), TQVariant::Int);
|
|
|
|
setAttributes(item, KFileMimeTypeInfo::Cummulative);
|
|
|
|
setUnit(item, KFileMimeTypeInfo::Seconds);
|
|
|
|
item = addItemInfo(group, "Emphasis", i18n("Emphasis"), TQVariant::String);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KMp3Plugin::readInfo(KFileMetaInfo &info, uint what)
|
|
|
|
{
|
|
|
|
kdDebug(7034) << "mp3 plugin readInfo\n";
|
|
|
|
|
|
|
|
bool readId3 = false;
|
|
|
|
bool readTech = false;
|
|
|
|
|
|
|
|
typedef enum KFileMetaInfo::What What;
|
|
|
|
|
|
|
|
if(what & (KFileMetaInfo::Fastest |
|
|
|
|
KFileMetaInfo::DontCare |
|
|
|
|
KFileMetaInfo::ContentInfo))
|
|
|
|
{
|
|
|
|
readId3 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(what & (KFileMetaInfo::Fastest |
|
|
|
|
KFileMetaInfo::DontCare |
|
|
|
|
KFileMetaInfo::TechnicalInfo))
|
|
|
|
{
|
|
|
|
readTech = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!readId3 && !readTech)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( info.path().isEmpty() ) // remote file
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TagLib::MPEG::File file(TQFile::encodeName(info.path()).data(), readTech);
|
|
|
|
|
|
|
|
if(!file.isOpen())
|
|
|
|
{
|
|
|
|
kdDebug(7034) << "Couldn't open " << file.name() << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(readId3)
|
|
|
|
{
|
|
|
|
KFileMetaInfoGroup id3group = appendGroup(info, "id3");
|
|
|
|
|
|
|
|
TQString date = file.tag()->year() > 0 ? TQString::number(file.tag()->year()) : TQString();
|
|
|
|
TQString track = file.tag()->track() > 0 ? TQString::number(file.tag()->track()) : TQString();
|
|
|
|
|
|
|
|
TQString title = TQString(TStringToQString(file.tag()->title())).stripWhiteSpace();
|
|
|
|
if (!title.isEmpty())
|
|
|
|
appendItem(id3group, "Title", title);
|
|
|
|
TQString artist = TQString(TStringToQString(file.tag()->artist())).stripWhiteSpace();
|
|
|
|
if (!artist.isEmpty())
|
|
|
|
appendItem(id3group, "Artist", artist);
|
|
|
|
TQString album = TQString(TStringToQString(file.tag()->album())).stripWhiteSpace();
|
|
|
|
if (!album.isEmpty())
|
|
|
|
appendItem(id3group, "Album", album);
|
|
|
|
appendItem(id3group, "Date", date);
|
|
|
|
TQString comment = TQString(TStringToQString(file.tag()->comment())).stripWhiteSpace();
|
|
|
|
if (!comment.isEmpty())
|
|
|
|
appendItem(id3group, "Comment", comment);
|
|
|
|
appendItem(id3group, "Tracknumber", track);
|
|
|
|
TQString genre = TQString(TStringToQString(file.tag()->genre())).stripWhiteSpace();
|
|
|
|
if (!genre.isEmpty())
|
|
|
|
appendItem(id3group, "Genre", genre);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(readTech)
|
|
|
|
{
|
|
|
|
KFileMetaInfoGroup techgroup = appendGroup(info, "Technical");
|
|
|
|
|
|
|
|
TQString version;
|
|
|
|
switch(file.audioProperties()->version())
|
|
|
|
{
|
|
|
|
case TagLib::MPEG::Header::Version1:
|
|
|
|
version = "1.0";
|
|
|
|
break;
|
|
|
|
case TagLib::MPEG::Header::Version2:
|
|
|
|
version = "2.0";
|
|
|
|
break;
|
|
|
|
case TagLib::MPEG::Header::Version2_5:
|
|
|
|
version = "2.5";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const int dummy = 0; // TQVariant's bool constructor requires a dummy int value.
|
|
|
|
|
|
|
|
// CRC and Emphasis aren't yet implemented in TagLib (not that I think anyone cares)
|
|
|
|
|
|
|
|
appendItem(techgroup, "Version", version);
|
|
|
|
appendItem(techgroup, "Layer", file.audioProperties()->layer());
|
|
|
|
// appendItem(techgroup, "CRC", file.audioProperties()->crc());
|
|
|
|
appendItem(techgroup, "Bitrate", file.audioProperties()->bitrate());
|
|
|
|
appendItem(techgroup, "Sample Rate", file.audioProperties()->sampleRate());
|
|
|
|
appendItem(techgroup, "Channels", file.audioProperties()->channels());
|
|
|
|
appendItem(techgroup, "Copyright", TQVariant(file.audioProperties()->isCopyrighted(), dummy));
|
|
|
|
appendItem(techgroup, "Original", TQVariant(file.audioProperties()->isOriginal(), dummy));
|
|
|
|
appendItem(techgroup, "Length", file.audioProperties()->length());
|
|
|
|
// appendItem(techgroup, "Emphasis", file.audioProperties()->empahsis());
|
|
|
|
}
|
|
|
|
|
|
|
|
kdDebug(7034) << "reading finished\n";
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do translation between KFileMetaInfo items and TagLib::String in a tidy way.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Translator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Translator(const KFileMetaInfo &info) : m_info(info) {}
|
|
|
|
TagLib::String operator[](const char *key) const
|
|
|
|
{
|
|
|
|
return QStringToTString(m_info["id3"][key].value().toString());
|
|
|
|
}
|
|
|
|
int toInt(const char *key) const
|
|
|
|
{
|
|
|
|
return m_info["id3"][key].value().toInt();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
const KFileMetaInfo &m_info;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool KMp3Plugin::writeInfo(const KFileMetaInfo &info) const
|
|
|
|
{
|
|
|
|
TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);
|
|
|
|
TagLib::MPEG::File file(TQFile::encodeName(info.path()).data(), false);
|
|
|
|
|
|
|
|
if(!file.isOpen() || !TagLib::File::isWritable(file.name()))
|
|
|
|
{
|
|
|
|
kdDebug(7034) << "couldn't open " << info.path() << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Translator t(info);
|
|
|
|
|
|
|
|
file.tag()->setTitle(t["Title"]);
|
|
|
|
file.tag()->setArtist(t["Artist"]);
|
|
|
|
file.tag()->setAlbum(t["Album"]);
|
|
|
|
file.tag()->setYear(t.toInt("Date"));
|
|
|
|
file.tag()->setComment(t["Comment"]);
|
|
|
|
file.tag()->setTrack(t.toInt("Tracknumber"));
|
|
|
|
file.tag()->setGenre(t["Genre"]);
|
|
|
|
|
|
|
|
file.save();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A validator that will suggest a list of strings, but allow for free form
|
|
|
|
* strings as well.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class ComboValidator : public KStringListValidator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ComboValidator(const TQStringList &list, bool rejecting,
|
|
|
|
bool fixupEnabled, TQObject *parent, const char *name) :
|
|
|
|
KStringListValidator(list, rejecting, fixupEnabled, parent, name)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual TQValidator::State validate(TQString &, int &) const
|
|
|
|
{
|
|
|
|
return TQValidator::Acceptable;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TQValidator *KMp3Plugin::createValidator(const TQString & /* mimetype */,
|
|
|
|
const TQString &group, const TQString &key,
|
|
|
|
TQObject *parent, const char *name) const
|
|
|
|
{
|
|
|
|
kdDebug(7034) << "making a validator for " << group << "/" << key << endl;
|
|
|
|
|
|
|
|
if(key == "Tracknumber" || key == "Date")
|
|
|
|
{
|
|
|
|
return new TQIntValidator(0, 9999, parent, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(key == "Genre")
|
|
|
|
{
|
|
|
|
TQStringList l;
|
|
|
|
TagLib::StringList genres = TagLib::ID3v1::genreList();
|
|
|
|
for(TagLib::StringList::ConstIterator it = genres.begin(); it != genres.end(); ++it)
|
|
|
|
{
|
|
|
|
l.append(TStringToQString((*it)));
|
|
|
|
}
|
|
|
|
return new ComboValidator(l, false, true, parent, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "kfile_mp3.moc"
|