From 85f2f087cba4996d721f01d0bfff5c3e8065b2cb Mon Sep 17 00:00:00 2001 From: David Sansome Date: Mon, 2 Jan 2012 17:21:07 +0000 Subject: [PATCH] Start work on moving everything that uses taglib out into an external process. --- CMakeLists.txt | 4 +- src/CMakeLists.txt | 5 - src/core/song.cpp | 556 +- src/core/song.h | 53 +- src/core/tagreaderclient.cpp | 72 + src/core/tagreaderclient.h | 50 + src/main.cpp | 4 - src/translations/hi.po | 4724 ----------------- tagreader/common/CMakeLists.txt | 41 + tagreader/common/messagehandler.cpp | 107 + tagreader/common/messagehandler.h | 258 + tagreader/common/tagreadermessages.proto | 102 + tagreader/tagreader/CMakeLists.txt | 45 + .../tagreader}/fmpsparser.cpp | 0 .../core => tagreader/tagreader}/fmpsparser.h | 0 tagreader/tagreader/main.cpp | 53 + tagreader/tagreader/tagreaderworker.cpp | 541 ++ tagreader/tagreader/tagreaderworker.h | 75 + 18 files changed, 1419 insertions(+), 5271 deletions(-) create mode 100644 src/core/tagreaderclient.cpp create mode 100644 src/core/tagreaderclient.h delete mode 100644 src/translations/hi.po create mode 100644 tagreader/common/CMakeLists.txt create mode 100644 tagreader/common/messagehandler.cpp create mode 100644 tagreader/common/messagehandler.h create mode 100644 tagreader/common/tagreadermessages.proto create mode 100644 tagreader/tagreader/CMakeLists.txt rename {src/core => tagreader/tagreader}/fmpsparser.cpp (100%) rename {src/core => tagreader/tagreader}/fmpsparser.h (100%) create mode 100644 tagreader/tagreader/main.cpp create mode 100644 tagreader/tagreader/tagreaderworker.cpp create mode 100644 tagreader/tagreader/tagreaderworker.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a524a0645..6326119cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ find_package(OpenGL REQUIRED) find_package(Boost REQUIRED) find_package(Gettext REQUIRED) find_package(PkgConfig REQUIRED) -find_package(Protobuf) +find_package(Protobuf REQUIRED) pkg_check_modules(TAGLIB REQUIRED taglib>=1.6) pkg_check_modules(QJSON REQUIRED QJson) @@ -366,6 +366,8 @@ add_subdirectory(3rdparty/universalchardet) add_subdirectory(tests) add_subdirectory(dist) add_subdirectory(tools/ultimate_lyrics_parser) +add_subdirectory(tagreader/common) +add_subdirectory(tagreader/tagreader) option(WITH_DEBIAN OFF) if(WITH_DEBIAN) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 443e70be6..8f9a3217b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,14 +64,11 @@ set(SOURCES core/crashreporting.cpp core/database.cpp core/deletefiles.cpp - core/encoding.cpp core/filesystemmusicstorage.cpp core/fht.cpp - core/fmpsparser.cpp core/globalshortcutbackend.cpp core/globalshortcuts.cpp core/gnomeglobalshortcutbackend.cpp - core/logging.cpp core/mergedproxymodel.cpp core/multisortfilterproxy.cpp core/musicstorage.cpp @@ -956,13 +953,11 @@ add_dependencies(clementine_lib pot) target_link_libraries(clementine_lib - chardet echoprint sha2 ${ECHONEST_LIBRARIES} ${GOBJECT_LIBRARIES} ${GLIB_LIBRARIES} - ${TAGLIB_LIBRARIES} ${QJSON_LIBRARIES} ${QT_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} diff --git a/src/core/song.cpp b/src/core/song.cpp index b8b12aada..f5b44f2c7 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -15,38 +15,15 @@ along with Clementine. If not, see . */ -#include "fmpsparser.h" +#include "encoding.h" #include "logging.h" #include "mpris_common.h" #include "song.h" #include "timeconstants.h" +#include "tagreader/common/messagehandler.h" #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #ifdef HAVE_LIBLASTFM #include "internet/fixlastfm.h" #include @@ -62,6 +39,8 @@ #include #include +#include + #ifdef Q_OS_WIN32 # include # include @@ -79,7 +58,6 @@ #include using boost::scoped_ptr; -#include "encoding.h" #include "utilities.h" #include "covers/albumcoverloader.h" #include "engines/enginebase.h" @@ -87,12 +65,6 @@ using boost::scoped_ptr; #include "widgets/trackslider.h" -// Taglib added support for FLAC pictures in 1.7.0 -#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7) -# define TAGLIB_HAS_FLAC_PICTURELIST -#endif - - namespace { QStringList Prepend(const QString& text, const QStringList& list) { @@ -109,10 +81,6 @@ QStringList Updateify(const QStringList& list) { return ret; } -TagLib::String QStringToTaglibString(const QString& s) { - return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8); -} - } // namespace @@ -142,18 +110,10 @@ const QString Song::kFtsUpdateSpec = Updateify(Song::kFtsColumns).join(", "); const QString Song::kManuallyUnsetCover = "(unset)"; const QString Song::kEmbeddedCover = "(embedded)"; -TagLibFileRefFactory Song::kDefaultFactory; -QMutex Song::sTaglibMutex; - struct Song::Private : public QSharedData { Private(); - // This is here and not in Song itself so we don't have to include - // in the main header. - void ParseOggTag(const TagLib::Ogg::FieldListMap& map, const QTextCodec* codec, - QString* disc, QString* compilation); - bool valid_; int id_; @@ -254,37 +214,22 @@ Song::Private::Private() { } -TagLib::FileRef* TagLibFileRefFactory::GetFileRef(const QString& filename) { -#ifdef Q_OS_WIN32 - return new TagLib::FileRef(filename.toStdWString().c_str()); -#else - return new TagLib::FileRef(QFile::encodeName(filename).constData()); -#endif -} Song::Song() - : d(new Private), - factory_(&kDefaultFactory) + : d(new Private) { } Song::Song(const Song &other) - : d(other.d), - factory_(&kDefaultFactory) + : d(other.d) { } -Song::Song(FileRefFactory* factory) - : d(new Private), - factory_(factory) { -} - Song::~Song() { } Song& Song::operator =(const Song& other) { d = other.d; - factory_ = other.factory_; return *this; } @@ -430,15 +375,6 @@ void Song::set_genre_id3(int id) { set_genre(TStringToQString(TagLib::ID3v1::genre(id))); } -QString Song::Decode(const TagLib::String& tag, const QTextCodec* codec) { - if (codec && tag.isLatin1()) { // Never override UTF-8. - const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString(); - return codec->toUnicode(fixed.c_str()).trimmed(); - } else { - return TStringToQString(tag).trimmed(); - } -} - QString Song::Decode(const QString& tag, const QTextCodec* codec) { if (!codec) { return tag; @@ -447,275 +383,71 @@ QString Song::Decode(const QString& tag, const QTextCodec* codec) { return codec->toUnicode(tag.toUtf8()); } -bool Song::HasProperMediaFile() const { -#ifndef QT_NO_DEBUG_OUTPUT - if (qApp->thread() == QThread::currentThread()) - qLog(Warning) << "HasProperMediaFile() on GUI thread!"; -#endif - - QMutexLocker l(&sTaglibMutex); - scoped_ptr fileref(factory_->GetFileRef(d->url_.toLocalFile())); - - return !fileref->isNull() && fileref->tag(); -} - -void Song::InitFromFile(const QString& filename, int directory_id) { -#ifndef QT_NO_DEBUG_OUTPUT - if (qApp->thread() == QThread::currentThread()) - qLog(Warning) << "InitFromFile() on GUI thread!"; -#endif - +void Song::InitFromProtobuf(const pb::tagreader::SongMetadata& pb) { d->init_from_file_ = true; - - d->url_ = QUrl::fromLocalFile(filename); - d->directory_id_ = directory_id; - - QFileInfo info(filename); - d->basefilename_ = info.fileName(); - - QMutexLocker l(&sTaglibMutex); - scoped_ptr fileref(factory_->GetFileRef(filename)); - - if(fileref->isNull()) { - return; - } - - d->filesize_ = info.size(); - d->mtime_ = info.lastModified().toTime_t(); - d->ctime_ = info.created().toTime_t(); - - // This is single byte encoding, therefore can't be CJK. - UniversalEncodingHandler detector(NS_FILTER_NON_CJK); - - TagLib::Tag* tag = fileref->tag(); - QTextCodec* codec = NULL; - if (tag) { - TagLib::MPEG::File* file = dynamic_cast(fileref->file()); - if (file && (file->ID3v2Tag() || file->ID3v1Tag())) { - codec = detector.Guess(*fileref); - } - if (codec && - codec->name() != "UTF-8" && - codec->name() != "ISO-8859-1") { - // Mark tags where we detect an unusual codec as suspicious. - d->suspicious_tags_ = true; - } - - - d->title_ = Decode(tag->title()); - d->artist_ = Decode(tag->artist()); - d->album_ = Decode(tag->album()); - d->genre_ = Decode(tag->genre()); - d->year_ = tag->year(); - d->track_ = tag->track(); - - d->valid_ = true; - } - - QString disc; - QString compilation; - if (TagLib::MPEG::File* file = dynamic_cast(fileref->file())) { - if (file->ID3v2Tag()) { - const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap(); - - if (!map["TPOS"].isEmpty()) - disc = TStringToQString(map["TPOS"].front()->toString()).trimmed(); - - if (!map["TBPM"].isEmpty()) - d->bpm_ = TStringToQString(map["TBPM"].front()->toString()).trimmed().toFloat(); - - if (!map["TCOM"].isEmpty()) - d->composer_ = Decode(map["TCOM"].front()->toString()); - - if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft - d->albumartist_ = Decode(map["TPE2"].front()->toString()); - - if (!map["TCMP"].isEmpty()) - compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed(); - - if (!map["APIC"].isEmpty()) - set_embedded_cover(); - - // Find a suitable comment tag. For now we ignore iTunNORM comments. - for (int i=0 ; i(map["COMM"][i]); - - if (frame && TStringToQString(frame->description()) != "iTunNORM") { - d->comment_ = Decode(frame->text()); - break; - } - } - - // Parse FMPS frames - for (int i=0 ; i(map["TXXX"][i]); - - if (frame && frame->description().startsWith("FMPS_")) { - ParseFMPSFrame(TStringToQString(frame->description()), - TStringToQString(frame->fieldList()[1])); - } - } - } - } else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast(fileref->file())) { - if (file->tag()) { - d->ParseOggTag(file->tag()->fieldListMap(), NULL, &disc, &compilation); - } - d->comment_ = Decode(tag->comment()); - } else if (TagLib::FLAC::File* file = dynamic_cast(fileref->file())) { - if ( file->xiphComment() ) { - d->ParseOggTag(file->xiphComment()->fieldListMap(), NULL, &disc, &compilation); -#ifdef TAGLIB_HAS_FLAC_PICTURELIST - if (!file->pictureList().isEmpty()) { - set_embedded_cover(); - } -#endif - } - d->comment_ = Decode(tag->comment()); - } else if (TagLib::MP4::File* file = dynamic_cast(fileref->file())) { - if (file->tag()) { - TagLib::MP4::Tag* mp4_tag = file->tag(); - const TagLib::MP4::ItemListMap& items = mp4_tag->itemListMap(); - TagLib::MP4::ItemListMap::ConstIterator it = items.find("aART"); - if (it != items.end()) { - TagLib::StringList album_artists = it->second.toStringList(); - if (!album_artists.isEmpty()) { - d->albumartist_ = Decode(album_artists.front()); - } - } - } - } else if (tag) { - d->comment_ = Decode(tag->comment()); - } - - if ( !disc.isEmpty() ) { - int i = disc.indexOf('/'); - if ( i != -1 ) - // disc.right( i ).toInt() is total number of discs, we don't use this at the moment - d->disc_ = disc.left( i ).toInt(); - else - d->disc_ = disc.toInt(); - } - - if ( compilation.isEmpty() ) { - // well, it wasn't set, but if the artist is VA assume it's a compilation - if ( d->artist_.toLower() == "various artists" ) - d->compilation_ = true; - } else { - int i = compilation.toInt(); - d->compilation_ = (i == 1); - } - - if (fileref->audioProperties()) { - d->bitrate_ = fileref->audioProperties()->bitrate(); - d->samplerate_ = fileref->audioProperties()->sampleRate(); - set_length_nanosec(fileref->audioProperties()->length() * kNsecPerSec); - } - - // Get the filetype if we can - GuessFileType(fileref.get()); - - // Set integer fields to -1 if they're not valid - #define intval(x) (x <= 0 ? -1 : x) - d->track_ = intval(d->track_); - d->disc_ = intval(d->disc_); - d->bpm_ = intval(d->bpm_); - d->year_ = intval(d->year_); - d->bitrate_ = intval(d->bitrate_); - d->samplerate_ = intval(d->samplerate_); - d->lastplayed_ = intval(d->lastplayed_); - d->rating_ = intval(d->rating_); - #undef intval + d->valid_ = pb.valid(); + d->title_ = QStringFromStdString(pb.title()); + d->album_ = QStringFromStdString(pb.album()); + d->artist_ = QStringFromStdString(pb.artist()); + d->albumartist_ = QStringFromStdString(pb.albumartist()); + d->composer_ = QStringFromStdString(pb.composer()); + d->track_ = pb.track(); + d->disc_ = pb.disc(); + d->bpm_ = pb.bpm(); + d->year_ = pb.year(); + d->genre_ = QStringFromStdString(pb.genre()); + d->comment_ = QStringFromStdString(pb.comment()); + d->compilation_ = pb.compilation(); + d->rating_ = pb.rating(); + d->playcount_ = pb.playcount(); + d->skipcount_ = pb.skipcount(); + d->lastplayed_ = pb.lastplayed(); + d->score_ = pb.score(); + set_length_nanosec(pb.length_nanosec()); + d->bitrate_ = pb.bitrate(); + d->samplerate_ = pb.samplerate(); + d->url_ = QUrl::fromEncoded(QByteArray(pb.url().data(), pb.url().size())); + d->basefilename_ = QStringFromStdString(pb.basefilename()); + d->mtime_ = pb.mtime(); + d->ctime_ = pb.ctime(); + d->filesize_ = pb.filesize(); + d->suspicious_tags_ = pb.suspicious_tags(); + d->art_automatic_ = QStringFromStdString(pb.art_automatic()); + d->filetype_ = static_cast(pb.type()); } -void Song::ParseFMPSFrame(const QString& name, const QString& value) { - FMPSParser parser; - if (!parser.Parse(value) || parser.is_empty()) - return; +void Song::ToProtobuf(pb::tagreader::SongMetadata* pb) const { + const QByteArray url(d->url_.toEncoded()); - QVariant var; - if (name == "FMPS_Rating") { - var = parser.result()[0][0]; - if (var.type() == QVariant::Double) { - d->rating_ = var.toDouble(); - } - } else if (name == "FMPS_Rating_User") { - // Take a user rating only if there's no rating already set - if (d->rating_ == -1 && parser.result()[0].count() >= 2) { - var = parser.result()[0][1]; - if (var.type() == QVariant::Double) { - d->rating_ = var.toDouble(); - } - } - } else if (name == "FMPS_PlayCount") { - var = parser.result()[0][0]; - if (var.type() == QVariant::Double) { - d->playcount_ = var.toDouble(); - } - } else if (name == "FMPS_PlayCount_User") { - // Take a user rating only if there's no playcount already set - if (d->rating_ == -1 && parser.result()[0].count() >= 2) { - var = parser.result()[0][1]; - if (var.type() == QVariant::Double) { - d->playcount_ = var.toDouble(); - } - } - } -} - -void Song::Private::ParseOggTag(const TagLib::Ogg::FieldListMap& map, - const QTextCodec* codec, - QString* disc, QString* compilation) { - if (!map["COMPOSER"].isEmpty()) - composer_ = Decode(map["COMPOSER"].front(), codec); - - if (!map["ALBUMARTIST"].isEmpty()) { - albumartist_ = Decode(map["ALBUMARTIST"].front(), codec); - } else if (!map["ALBUM ARTIST"].isEmpty()) { - albumartist_ = Decode(map["ALBUM ARTIST"].front(), codec); - } - - if (!map["BPM"].isEmpty() ) - bpm_ = TStringToQString( map["BPM"].front() ).trimmed().toFloat(); - - if (!map["DISCNUMBER"].isEmpty() ) - *disc = TStringToQString( map["DISCNUMBER"].front() ).trimmed(); - - if (!map["COMPILATION"].isEmpty() ) - *compilation = TStringToQString( map["COMPILATION"].front() ).trimmed(); - - if (!map["COVERART"].isEmpty()) - art_automatic_ = kEmbeddedCover; -} - -void Song::GuessFileType(TagLib::FileRef* fileref) { -#ifdef TAGLIB_WITH_ASF - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Asf; -#endif - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Flac; -#ifdef TAGLIB_WITH_MP4 - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Mp4; -#endif - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Mpc; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Mpeg; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_OggFlac; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_OggSpeex; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_OggVorbis; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Aiff; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_Wav; - if (dynamic_cast(fileref->file())) - d->filetype_ = Type_TrueAudio; + pb->set_valid(d->valid_); + pb->set_title(DataCommaSizeFromQString(d->title_)); + pb->set_album(DataCommaSizeFromQString(d->album_)); + pb->set_artist(DataCommaSizeFromQString(d->artist_)); + pb->set_albumartist(DataCommaSizeFromQString(d->albumartist_)); + pb->set_composer(DataCommaSizeFromQString(d->composer_)); + pb->set_track(d->track_); + pb->set_disc(d->disc_); + pb->set_bpm(d->bpm_); + pb->set_year(d->year_); + pb->set_genre(DataCommaSizeFromQString(d->genre_)); + pb->set_comment(DataCommaSizeFromQString(d->comment_)); + pb->set_compilation(d->compilation_); + pb->set_rating(d->rating_); + pb->set_playcount(d->playcount_); + pb->set_skipcount(d->skipcount_); + pb->set_lastplayed(d->lastplayed_); + pb->set_score(d->score_); + pb->set_length_nanosec(length_nanosec()); + pb->set_bitrate(d->bitrate_); + pb->set_samplerate(d->samplerate_); + pb->set_url(url.constData(), url.size()); + pb->set_basefilename(DataCommaSizeFromQString(d->basefilename_)); + pb->set_mtime(d->mtime_); + pb->set_ctime(d->ctime_); + pb->set_filesize(d->filesize_); + pb->set_suspicious_tags(d->suspicious_tags_); + pb->set_art_automatic(DataCommaSizeFromQString(d->art_automatic_)); + pb->set_type(static_cast< ::pb::tagreader::SongMetadata_Type>(d->filetype_)); } void Song::InitFromQuery(const SqlRow& q, bool reliable_metadata, int col) { @@ -1371,92 +1103,11 @@ bool Song::IsMetadataEqual(const Song& other) const { d->cue_path_ == other.d->cue_path_; } -void Song::SetTextFrame(const QString& id, const QString& value, - TagLib::ID3v2::Tag* tag) { - TagLib::ByteVector id_vector = id.toUtf8().constData(); - - // Remove the frame if it already exists - while (tag->frameListMap().contains(id_vector) && - tag->frameListMap()[id_vector].size() != 0) { - tag->removeFrame(tag->frameListMap()[id_vector].front()); - } - - // Create and add a new frame - TagLib::ID3v2::TextIdentificationFrame* frame = - new TagLib::ID3v2::TextIdentificationFrame(id.toUtf8().constData(), - TagLib::String::UTF8); - frame->setText(QStringToTaglibString(value)); - tag->addFrame(frame); -} - bool Song::IsEditable() const { return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->filetype_ != Type_Unknown && !has_cue(); } -bool Song::Save() const { - const QString filename = d->url_.toLocalFile(); - if (filename.isNull()) - return false; - - QMutexLocker l(&sTaglibMutex); - scoped_ptr fileref(factory_->GetFileRef(filename)); - - if (!fileref || fileref->isNull()) // The file probably doesn't exist - return false; - - fileref->tag()->setTitle(QStringToTaglibString(d->title_)); - fileref->tag()->setArtist(QStringToTaglibString(d->artist_)); - fileref->tag()->setAlbum(QStringToTaglibString(d->album_)); - fileref->tag()->setGenre(QStringToTaglibString(d->genre_)); - fileref->tag()->setComment(QStringToTaglibString(d->comment_)); - fileref->tag()->setYear(d->year_); - fileref->tag()->setTrack(d->track_); - - if (TagLib::MPEG::File* file = dynamic_cast(fileref->file())) { - TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true); - SetTextFrame("TPOS", d->disc_ <= 0 -1 ? QString() : QString::number(d->disc_), tag); - SetTextFrame("TBPM", d->bpm_ <= 0 -1 ? QString() : QString::number(d->bpm_), tag); - SetTextFrame("TCOM", d->composer_, tag); - SetTextFrame("TPE2", d->albumartist_, tag); - SetTextFrame("TCMP", d->compilation_ ? "1" : "0", tag); - } - else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast(fileref->file())) { - TagLib::Ogg::XiphComment* tag = file->tag(); - tag->addField("COMPOSER", QStringToTaglibString(d->composer_), true); - tag->addField("BPM", QStringToTaglibString(d->bpm_ <= 0 -1 ? QString() : QString::number(d->bpm_)), true); - tag->addField("DISCNUMBER", QStringToTaglibString(d->disc_ <= 0 -1 ? QString() : QString::number(d->disc_)), true); - tag->addField("COMPILATION", QStringToTaglibString(d->compilation_ ? "1" : "0"), true); - } - else if (TagLib::FLAC::File* file = dynamic_cast(fileref->file())) { - TagLib::Ogg::XiphComment* tag = file->xiphComment(); - tag->addField("COMPOSER", QStringToTaglibString(d->composer_), true); - tag->addField("BPM", QStringToTaglibString(d->bpm_ <= 0 -1 ? QString() : QString::number(d->bpm_)), true); - tag->addField("DISCNUMBER", QStringToTaglibString(d->disc_ <= 0 -1 ? QString() : QString::number(d->disc_)), true); - tag->addField("COMPILATION", QStringToTaglibString(d->compilation_ ? "1" : "0"), true); - } - - bool ret = fileref->save(); - #ifdef Q_OS_LINUX - if (ret) { - // Linux: inotify doesn't seem to notice the change to the file unless we - // change the timestamps as well. (this is what touch does) - utimensat(0, QFile::encodeName(filename).constData(), NULL, 0); - } - #endif // Q_OS_LINUX - - return ret; -} - -bool Song::Save(const Song& song) { - return song.Save(); -} - -QFuture Song::BackgroundSave() const { - QFuture future = QtConcurrent::run(&Song::Save, Song(*this)); - return future; -} - bool Song::operator==(const Song& other) const { // TODO: this isn't working for radios return url() == other.url() && @@ -1468,79 +1119,6 @@ uint qHash(const Song& song) { return qHash(song.url().toString()) ^ qHash(song.beginning_nanosec()); } -QImage Song::LoadEmbeddedArt(const QString& filename) { - QImage ret; - if (filename.isEmpty()) - return ret; - - QMutexLocker l(&sTaglibMutex); - -#ifdef Q_OS_WIN32 - TagLib::FileRef ref(filename.toStdWString().c_str()); -#else - TagLib::FileRef ref(QFile::encodeName(filename).constData()); -#endif - - if (ref.isNull() || !ref.file()) - return ret; - - // MP3 - TagLib::MPEG::File* file = dynamic_cast(ref.file()); - if (file && file->ID3v2Tag()) { - TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"]; - if (apic_frames.isEmpty()) - return ret; - - TagLib::ID3v2::AttachedPictureFrame* pic = - static_cast(apic_frames.front()); - - ret.loadFromData((const uchar*) pic->picture().data(), pic->picture().size()); - return ret; - } - - // Ogg vorbis/speex - TagLib::Ogg::XiphComment* xiph_comment = - dynamic_cast(ref.file()->tag()); - - if (xiph_comment) { - TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap(); - - // Ogg lacks a definitive standard for embedding cover art, but it seems - // b64 encoding a field called COVERART is the general convention - if (!map.contains("COVERART")) - return ret; - - QByteArray image_data_b64(map["COVERART"].toString().toCString()); - QByteArray image_data = QByteArray::fromBase64(image_data_b64); - - if (!ret.loadFromData(image_data)) - ret.loadFromData(image_data_b64); //maybe it's not b64 after all - - return ret; - } - -#ifdef TAGLIB_HAS_FLAC_PICTURELIST - // Flac - TagLib::FLAC::File* flac_file = dynamic_cast(ref.file()); - if (flac_file && flac_file->xiphComment()) { - TagLib::List pics = flac_file->pictureList(); - if (!pics.isEmpty()) { - // Use the first picture in the file - this could be made cleverer and - // pick the front cover if it's present. - - std::list::iterator it = pics.begin(); - TagLib::FLAC::Picture* picture = *it; - - QByteArray image_data(picture->data().data(), picture->data().size()); - ret.loadFromData(image_data); - return ret; - } - } -#endif - - return ret; -} - bool Song::IsOnSameAlbum(const Song& other) const { if (is_compilation() != other.is_compilation()) return false; diff --git a/src/core/song.h b/src/core/song.h index b28f22d2b..695963403 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -26,6 +26,7 @@ #include "config.h" #include "engines/engine_fwd.h" +#include "tagreader/common/tagreadermessages.pb.h" class QSqlQuery; class QUrl; @@ -48,35 +49,13 @@ class QUrl; } #endif -namespace TagLib { - class FileRef; - class String; - - namespace ID3v2 { - class Tag; - } -} - class SqlRow; -class FileRefFactory { - public: - virtual ~FileRefFactory() {} - virtual TagLib::FileRef* GetFileRef(const QString& filename) = 0; -}; - -class TagLibFileRefFactory : public FileRefFactory { - public: - virtual TagLib::FileRef* GetFileRef(const QString& filename); -}; - - class Song { public: Song(); Song(const Song& other); - Song(FileRefFactory* factory); ~Song(); static const QStringList kColumns; @@ -94,7 +73,8 @@ class Song { static QString JoinSpec(const QString& table); - // Don't change these values - they're stored in the database + // Don't change these values - they're stored in the database, and defined + // in the tag reader protobuf. enum FileType { Type_Unknown = 0, Type_Asf = 1, @@ -115,18 +95,10 @@ class Song { static QString TextForFiletype(FileType type); QString TextForFiletype() const { return TextForFiletype(filetype()); } - // Helper function to load embedded cover art from a music file. This is not - // actually used by the Song class, but instead it is called by - // AlbumCoverLoader and is here so it can lock on the taglib mutex. - static QImage LoadEmbeddedArt(const QString& filename); - // Checks if this Song can be properly initialized from it's media file. - // This requires the 'filename' attribute to be set first. - bool HasProperMediaFile() const; - // Constructors void Init(const QString& title, const QString& artist, const QString& album, qint64 length_nanosec); void Init(const QString& title, const QString& artist, const QString& album, qint64 beginning, qint64 end); - void InitFromFile(const QString& filename, int directory_id); + void InitFromProtobuf(const pb::tagreader::SongMetadata& pb); void InitFromQuery(const SqlRow& query, bool reliable_metadata, int col = 0); void InitFromFilePartial(const QString& filename); // Just store the filename: incomplete but fast #ifdef HAVE_LIBLASTFM @@ -150,7 +122,6 @@ class Song { void ToWmdm(IWMDMMetaData* metadata) const; #endif - static QString Decode(const TagLib::String& tag, const QTextCodec* codec = NULL); static QString Decode(const QString& tag, const QTextCodec* codec = NULL); // Save @@ -160,6 +131,7 @@ class Song { void ToLastFM(lastfm::Track* track) const; #endif void ToXesam(QVariantMap* map) const; + void ToProtobuf(pb::tagreader::SongMetadata* pb) const; // Simple accessors bool is_valid() const; @@ -296,24 +268,9 @@ class Song { Song& operator=(const Song& other); - private: - void GuessFileType(TagLib::FileRef* fileref); - static bool Save(const Song& song); - - // Helper methods for taglib - static void SetTextFrame(const QString& id, const QString& value, - TagLib::ID3v2::Tag* tag); - void ParseFMPSFrame(const QString& name, const QString& value); - private: struct Private; QSharedDataPointer d; - - FileRefFactory* factory_; - - static TagLibFileRefFactory kDefaultFactory; - - static QMutex sTaglibMutex; }; Q_DECLARE_METATYPE(Song); diff --git a/src/core/tagreaderclient.cpp b/src/core/tagreaderclient.cpp new file mode 100644 index 000000000..4a900f451 --- /dev/null +++ b/src/core/tagreaderclient.cpp @@ -0,0 +1,72 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine 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, either version 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "tagreaderclient.h" + +#include + +TagReaderClient::TagReaderClient(QObject* parent) + : QObject(parent), + process_(NULL), + handler_(NULL) +{ +} + +void TagReaderClient::Start() { + delete process_; + delete handler_; + + process_ = new QProcess(this); + process_->start(); +} + +TagReaderReply* TagReaderClient::ReadFile(const QString& filename) { + pb::tagreader::Message message; + pb::tagreader::ReadFileRequest* req = message.mutable_read_file_request(); + + req->set_filename(DataCommaSizeFromQString(filename)); + + return SendMessageWithReply(&message); +} + +TagReaderReply* TagReaderClient::SaveFile(const QString& filename, const Song& metadata) { + pb::tagreader::Message message; + pb::tagreader::SaveFileRequest* req = message.mutable_save_file_request(); + + req->set_filename(DataCommaSizeFromQString(filename)); + metadata.ToProtobuf(req->mutable_metadata()); + + return SendMessageWithReply(&message); +} + +TagReaderReply* TagReaderClient::IsMediaFile(const QString& filename) { + pb::tagreader::Message message; + pb::tagreader::IsMediaFileRequest* req = message.mutable_is_media_file_request(); + + req->set_filename(DataCommaSizeFromQString(filename)); + + return SendMessageWithReply(&message); +} + +TagReaderReply* TagReaderClient::LoadEmbeddedArt(const QString& filename) { + pb::tagreader::Message message; + pb::tagreader::LoadEmbeddedArtRequest* req = message.mutable_load_embedded_art_request(); + + req->set_filename(DataCommaSizeFromQString(filename)); + + return SendMessageWithReply(&message); +} diff --git a/src/core/tagreaderclient.h b/src/core/tagreaderclient.h new file mode 100644 index 000000000..6e384e5ac --- /dev/null +++ b/src/core/tagreaderclient.h @@ -0,0 +1,50 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine 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, either version 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef TAGREADERCLIENT_H +#define TAGREADERCLIENT_H + +#include "messagehandler.h" +#include "song.h" +#include "tagreadermessages.pb.h" + +class QProcess; + +class TagReaderClient : public QObject { + Q_OBJECT + +public: + TagReaderClient(QObject* parent = 0); + + typedef AbstractMessageHandler HandlerType; + typedef typename HandlerType::ReplyType ReplyType; + + void Start(); + + ReplyType* ReadFile(const QString& filename); + ReplyType* SaveFile(const QString& filename, const Song& metadata); + ReplyType* IsMediaFile(const QString& filename); + ReplyType* LoadEmbeddedArt(const QString& filename); + +private: + QProcess* process_; + HandlerType* handler_; +}; + +typedef TagReaderClient::ReplyType TagReaderReply; + +#endif // TAGREADERCLIENT_H diff --git a/src/main.cpp b/src/main.cpp index 527ec0264..f08565b8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -275,10 +275,6 @@ int main(int argc, char *argv[]) { } } - // Detect technically invalid usage of non-ASCII in ID3v1 tags. - UniversalEncodingHandler handler; - TagLib::ID3v1::Tag::setStringHandler(&handler); - #ifdef Q_OS_LINUX // Force Clementine's menu to be shown in the Clementine window and not in // the Unity global menubar thing. See: diff --git a/src/translations/hi.po b/src/translations/hi.po deleted file mode 100644 index 69bbc56e7..000000000 --- a/src/translations/hi.po +++ /dev/null @@ -1,4724 +0,0 @@ -# Clementine. -# Copyright (C) 2010 David Sansome -# This file is distributed under the same license as the Clementine package. -# -# Translators: -# FIRST AUTHOR , 2010. -msgid "" -msgstr "" -"Project-Id-Version: Clementine Music Player\n" -"Report-Msgid-Bugs-To: http://code.google.com/p/clementine-player/issues/list\n" -"PO-Revision-Date: 2011-12-13 17:09+0000\n" -"Last-Translator: Clementine Buildbot \n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: hi\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" - -#: ../bin/src/ui_transcoderoptionsaac.h:130 -#: ../bin/src/ui_transcoderoptionsmp3.h:195 -#: ../bin/src/ui_transcoderoptionsspeex.h:220 -#: ../bin/src/ui_transcoderoptionsspeex.h:223 -#: ../bin/src/ui_transcoderoptionsvorbis.h:205 -#: ../bin/src/ui_transcoderoptionsvorbis.h:208 -#: ../bin/src/ui_transcoderoptionsvorbis.h:211 -#: ../bin/src/ui_transcoderoptionswma.h:80 -msgid " kbps" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:186 -#: ../bin/src/ui_playbacksettingspage.h:265 -#: ../bin/src/ui_playbacksettingspage.h:285 -msgid " ms" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:182 -msgid " pt" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:404 -#: ../bin/src/ui_visualisationselector.h:116 -msgid " seconds" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:143 -msgid " songs" -msgstr "" - -#: widgets/osd.cpp:209 -#, qt-format -msgid "%1 albums" -msgstr "" - -#: core/utilities.cpp:90 -#, qt-format -msgid "%1 days" -msgstr "" - -#: core/utilities.cpp:111 -#, qt-format -msgid "%1 days ago" -msgstr "" - -#: playlistparsers/playlistparser.cpp:74 -#, qt-format -msgid "%1 playlists (%2)" -msgstr "" - -#: playlist/playlistmanager.cpp:302 -#, qt-format -msgid "%1 selected of" -msgstr "" - -#: devices/deviceview.cpp:122 -#, qt-format -msgid "%1 song" -msgstr "" - -#: devices/deviceview.cpp:124 -#, qt-format -msgid "%1 songs" -msgstr "" - -#: smartplaylists/searchpreview.cpp:129 -#, qt-format -msgid "%1 songs found" -msgstr "" - -#: smartplaylists/searchpreview.cpp:126 -#, qt-format -msgid "%1 songs found (showing %2)" -msgstr "" - -#: playlist/playlistmanager.cpp:308 -#, qt-format -msgid "%1 tracks" -msgstr "" - -#: ui/albumcovermanager.cpp:437 -#, qt-format -msgid "%1 transferred" -msgstr "" - -#: widgets/osd.cpp:259 widgets/osd.cpp:264 widgets/osd.cpp:269 -#: widgets/osd.cpp:274 widgets/osd.cpp:279 widgets/osd.cpp:284 -#, qt-format -msgid "%1: Wiimotedev module" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:102 -#, qt-format -msgid "%L1 other listeners" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:100 -#, qt-format -msgid "%L1 total plays" -msgstr "" - -#: transcoder/transcodedialog.cpp:198 -#, c-format -msgid "%n failed" -msgstr "" - -#: transcoder/transcodedialog.cpp:193 -#, c-format -msgid "%n finished" -msgstr "" - -#: transcoder/transcodedialog.cpp:188 -#, c-format -msgid "%n remaining" -msgstr "" - -#: playlist/playlistheader.cpp:37 -msgid "&Align text" -msgstr "" - -#: playlist/playlistheader.cpp:40 -msgid "&Center" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:178 -msgid "&Custom" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:702 -msgid "&Extras" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:701 -msgid "&Help" -msgstr "" - -#: playlist/playlistheader.cpp:70 -#, qt-format -msgid "&Hide %1" -msgstr "" - -#: playlist/playlistheader.cpp:33 -msgid "&Hide..." -msgstr "" - -#: playlist/playlistheader.cpp:39 -msgid "&Left" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:699 -msgid "&Music" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:176 -msgid "&None" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:700 -msgid "&Playlist" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:637 -msgid "&Quit" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:673 -msgid "&Repeat mode" -msgstr "" - -#: playlist/playlistheader.cpp:41 -msgid "&Right" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:672 -msgid "&Shuffle mode" -msgstr "" - -#: playlist/playlistheader.cpp:34 -msgid "&Stretch columns to fit window" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:703 -msgid "&Tools" -msgstr "" - -#: ui/edittagdialog.cpp:45 -msgid "(different across multiple songs)" -msgstr "" - -#: ui/about.cpp:76 -msgid "...and all the Amarok contributors" -msgstr "" - -#: ../bin/src/ui_trackslider.h:70 ../bin/src/ui_trackslider.h:74 -msgid "0:00:00" -msgstr "" - -#: core/utilities.cpp:90 -msgid "1 day" -msgstr "" - -#: playlist/playlistmanager.cpp:308 -msgid "1 track" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:143 -#: ../bin/src/ui_magnatunesettingspage.h:174 -msgid "128k MP3" -msgstr "" - -#: library/library.cpp:55 -msgid "50 random tracks" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:165 -msgid "Upgrade to Premium now" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:199 -msgid "" -"

Tokens start with %, for example: %artist %album %title

\n" -"\n" -"

If you surround sections of text that contain a token with curly-braces, that section will be hidden if the token is empty.

" -msgstr "" - -#: internet/groovesharksettingspage.cpp:111 -msgid "A Grooveshark Anywhere account is required." -msgstr "" - -#: internet/spotifysettingspage.cpp:161 -msgid "A Spotify Premium account is required." -msgstr "" - -#: smartplaylists/wizard.cpp:72 -msgid "" -"A smart playlist is a dynamic list of songs that come from your library. " -"There are different types of smart playlist that offer different ways of " -"selecting songs." -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:145 -msgid "" -"A song will be included in the playlist if it matches these conditions." -msgstr "" - -#: smartplaylists/searchterm.cpp:288 -msgid "A-Z" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:163 -msgid "AAC" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:179 -msgid "AAC 128k" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:171 -msgid "AAC 32k" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:178 -msgid "AAC 64k" -msgstr "" - -#: core/song.cpp:393 -msgid "AIFF" -msgstr "" - -#: widgets/nowplayingwidget.cpp:119 -msgid "ALL GLORY TO THE HYPNOTOAD" -msgstr "" - -#: ui/about.cpp:32 -#, qt-format -msgid "About %1" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:658 -msgid "About Clementine..." -msgstr "" - -#: ../bin/src/ui_mainwindow.h:691 -msgid "About Qt..." -msgstr "" - -#: ../bin/src/ui_groovesharksettingspage.h:113 -#: ../bin/src/ui_magnatunesettingspage.h:155 -#: ../bin/src/ui_spotifysettingspage.h:209 -#: ../bin/src/ui_remotesettingspage.h:203 -#: ../bin/src/ui_lastfmsettingspage.h:145 -msgid "Account details" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:161 -msgid "Account details (Premium)" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:191 -msgid "Action" -msgstr "" - -#: wiimotedev/wiimotesettingspage.cpp:98 -msgid "Active/deactive Wiiremote" -msgstr "" - -#: ../bin/src/ui_addstreamdialog.h:113 -msgid "Add Stream" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:394 -msgid "Add a new line if supported by the notification type" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:193 -msgid "Add action" -msgstr "" - -#: globalsearch/globalsearchwidget.cpp:104 -msgid "Add and play now" -msgstr "" - -#: internet/savedradio.cpp:103 -msgid "Add another stream..." -msgstr "" - -#: library/librarysettingspage.cpp:62 -msgid "Add directory..." -msgstr "" - -#: ui/mainwindow.cpp:1569 -msgid "Add file" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:662 -msgid "Add file..." -msgstr "" - -#: transcoder/transcodedialog.cpp:210 -msgid "Add files to transcode" -msgstr "" - -#: ui/mainwindow.cpp:1594 -msgid "Add folder" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:677 -msgid "Add folder..." -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:159 -msgid "Add new folder..." -msgstr "" - -#: smartplaylists/searchtermwidget.cpp:333 -msgid "Add search term" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:361 -msgid "Add song album tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:367 -msgid "Add song albumartist tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:358 -msgid "Add song artist tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:373 -msgid "Add song composer tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:376 -msgid "Add song disc tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:382 -msgid "Add song genre tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:385 -msgid "Add song length tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:388 -msgid "Add song play count" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:391 -msgid "Add song skip count" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:364 -msgid "Add song title tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:379 -msgid "Add song track tag" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:370 -msgid "Add song year tag" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:664 -msgid "Add stream..." -msgstr "" - -#: internet/groovesharkservice.cpp:1002 -msgid "Add to Grooveshark favorites" -msgstr "" - -#: internet/groovesharkservice.cpp:1008 -msgid "Add to Grooveshark playlists" -msgstr "" - -#: ui/mainwindow.cpp:1398 -msgid "Add to another playlist" -msgstr "" - -#: globalsearch/globalsearchwidget.cpp:103 -#: ../bin/src/ui_albumcovermanager.h:152 -msgid "Add to playlist" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:204 -msgid "Add to the queue" -msgstr "" - -#: ../bin/src/ui_wiimoteshortcutgrabber.h:123 -msgid "Add wiimotedev action" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:206 -msgid "Add..." -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:123 -msgid "Added this month" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:117 -msgid "Added this week" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:122 -msgid "Added this year" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:116 -msgid "Added today" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:118 -#: ../bin/src/ui_libraryfilterwidget.h:120 -msgid "Added within three months" -msgstr "" - -#: internet/groovesharkservice.cpp:1253 -msgid "Adding song to favorites" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:130 -msgid "Advanced grouping..." -msgstr "" - -#: ../bin/src/ui_organisedialog.h:190 -msgid "After copying..." -msgstr "" - -#: playlist/playlist.cpp:1109 ui/organisedialog.cpp:52 -#: ui/qtsystemtrayicon.cpp:252 ../bin/src/ui_groupbydialog.h:129 -#: ../bin/src/ui_groupbydialog.h:142 ../bin/src/ui_groupbydialog.h:155 -#: ../bin/src/ui_albumcoversearcher.h:110 -#: ../bin/src/ui_albumcoversearcher.h:112 ../bin/src/ui_edittagdialog.h:656 -#: ../bin/src/ui_trackselectiondialog.h:209 -msgid "Album" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:272 -msgid "Album (ideal loudness for all tracks)" -msgstr "" - -#: playlist/playlist.cpp:1115 ui/organisedialog.cpp:55 -#: ../bin/src/ui_edittagdialog.h:658 -msgid "Album artist" -msgstr "" - -#: internet/jamendoservice.cpp:411 -msgid "Album info on jamendo.com..." -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:131 ../bin/src/ui_groupbydialog.h:144 -#: ../bin/src/ui_groupbydialog.h:157 -msgid "Albumartist" -msgstr "" - -#: ui/albumcovermanager.cpp:119 -msgid "Albums with covers" -msgstr "" - -#: ui/albumcovermanager.cpp:120 -msgid "Albums without covers" -msgstr "" - -#: ui/mainwindow.cpp:151 -msgid "All Files (*)" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:670 -msgid "All Glory to the Hypnotoad!" -msgstr "" - -#: ui/albumcovermanager.cpp:118 -msgid "All albums" -msgstr "" - -#: ui/albumcovermanager.cpp:263 -msgid "All artists" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:45 -msgid "All files (*)" -msgstr "" - -#: playlistparsers/playlistparser.cpp:61 -#, qt-format -msgid "All playlists (%1)" -msgstr "" - -#: ui/about.cpp:73 -msgid "All the translators" -msgstr "" - -#: library/library.cpp:79 -msgid "All tracks" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:140 -msgid "Allow mid/side encoding" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:214 -msgid "Alongside the originals" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:188 -msgid "Always hide the main window" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:187 -msgid "Always show the main window" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:196 -#: ../bin/src/ui_behavioursettingspage.h:210 -msgid "Always start playing" -msgstr "" - -#: internet/spotifyblobdownloader.cpp:59 -msgid "" -"An additional plugin is required to use Spotify in Clementine. Would you " -"like to download and install it now?" -msgstr "" - -#: devices/afcdevice.cpp:62 -msgid "An error occurred copying the iTunes database from the device" -msgstr "" - -#: devices/afcdevice.cpp:156 -msgid "An error occurred copying the iTunes database onto the device" -msgstr "" - -#: devices/gpodloader.cpp:61 -msgid "An error occurred loading the iTunes database" -msgstr "" - -#: ui/edittagdialog.cpp:611 -#, qt-format -msgid "An error occurred writing metadata to '%1'" -msgstr "" - -#: internet/lastfmservice.cpp:902 -#, qt-format -msgid "An unknown last.fm error occurred: %1" -msgstr "" - -#: ui/about.cpp:77 -msgid "And:" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:180 -msgid "Appearance" -msgstr "" - -#: core/commandlineoptions.cpp:157 -msgid "Append files/URLs to the playlist" -msgstr "" - -#: devices/deviceview.cpp:216 internet/internetservice.cpp:53 -#: library/libraryview.cpp:243 widgets/fileviewlist.cpp:31 -msgid "Append to current playlist" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:201 -msgid "Append to the playlist" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:275 -msgid "Apply compression to prevent clipping" -msgstr "" - -#: ui/equalizer.cpp:191 -#, qt-format -msgid "Are you sure you want to delete the \"%1\" preset?" -msgstr "" - -#: internet/groovesharkservice.cpp:1177 -msgid "Are you sure you want to delete this playlist?" -msgstr "" - -#: ui/edittagdialog.cpp:717 -msgid "Are you sure you want to reset this song's statistics?" -msgstr "" - -#: playlist/playlist.cpp:1108 ui/organisedialog.cpp:53 -#: ui/qtsystemtrayicon.cpp:250 ../bin/src/ui_groupbydialog.h:130 -#: ../bin/src/ui_groupbydialog.h:143 ../bin/src/ui_groupbydialog.h:156 -#: ../bin/src/ui_albumcoversearcher.h:106 -#: ../bin/src/ui_albumcoversearcher.h:108 ../bin/src/ui_edittagdialog.h:654 -#: ../bin/src/ui_trackselectiondialog.h:210 -#: ../bin/src/ui_lastfmstationdialog.h:96 -msgid "Artist" -msgstr "" - -#: ui/mainwindow.cpp:256 -msgid "Artist info" -msgstr "" - -#: internet/lastfmservice.cpp:191 -msgid "Artist radio" -msgstr "" - -#: songinfo/echonesttags.cpp:59 -msgid "Artist tags" -msgstr "" - -#: ui/organisedialog.cpp:54 -msgid "Artist's initial" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:209 -msgid "Audio format" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:210 -msgid "Authenticating..." -msgstr "" - -#: internet/digitallyimportedsettingspage.cpp:82 -#: internet/magnatunesettingspage.cpp:113 internet/lastfmservice.cpp:432 -#: internet/lastfmsettingspage.cpp:78 remote/remotesettingspage.cpp:113 -msgid "Authentication failed" -msgstr "" - -#: ui/about.cpp:64 -msgid "Authors" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:227 -msgid "Auto" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:161 -msgid "Automatic updating" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:170 -msgid "Automatically open single categories in the library tree" -msgstr "" - -#: widgets/freespacebar.cpp:45 -msgid "Available" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:221 -msgid "Average bitrate" -msgstr "" - -#: covers/coversearchstatisticsdialog.cpp:70 -msgid "Average image size" -msgstr "" - -#: playlist/playlist.cpp:1124 ui/organisedialog.cpp:59 -#: ../bin/src/ui_edittagdialog.h:638 -msgid "BPM" -msgstr "" - -#: ../bin/src/ui_backgroundstreamssettingspage.h:56 -msgid "Background Streams" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:418 -msgid "Background color" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:417 -msgid "Background opacity" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:643 -msgid "Ban" -msgstr "" - -#: analyzers/baranalyzer.cpp:19 -msgid "Bar analyzer" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:421 -msgid "Basic Blue" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:167 -msgid "Basic audio type" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:176 -msgid "Behavior" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsflac.h:83 -msgid "Best" -msgstr "" - -#: songinfo/echonestbiographies.cpp:83 -#, qt-format -msgid "Biography from %1" -msgstr "" - -#: playlist/playlist.cpp:1125 ../bin/src/ui_edittagdialog.h:640 -msgid "Bit rate" -msgstr "" - -#: ui/organisedialog.cpp:64 ../bin/src/ui_transcoderoptionsaac.h:129 -#: ../bin/src/ui_transcoderoptionsmp3.h:194 -#: ../bin/src/ui_transcoderoptionsspeex.h:218 -#: ../bin/src/ui_transcoderoptionswma.h:79 -msgid "Bitrate" -msgstr "" - -#: analyzers/blockanalyzer.cpp:24 -msgid "Block analyzer" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:141 -msgid "Block type" -msgstr "" - -#: devices/ilister.cpp:100 -msgid "Bluetooth MAC Address" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:414 -msgid "Body" -msgstr "" - -#: analyzers/boomanalyzer.cpp:8 -msgid "Boom analyzer" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:146 -msgid "Browse..." -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:284 -msgid "Buffer duration" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:192 -msgid "Buttons" -msgstr "" - -#: core/song.cpp:396 -msgid "CDDA" -msgstr "" - -#: library/library.cpp:95 -msgid "CUE sheet support" -msgstr "" - -#: internet/spotifyblobdownloader.cpp:43 -msgid "Cancel" -msgstr "" - -#: ../bin/src/ui_edittagdialog.h:634 -msgid "Change cover art" -msgstr "" - -#: songinfo/songinfotextview.cpp:74 -msgid "Change font size..." -msgstr "" - -#: core/globalshortcuts.cpp:61 -msgid "Change repeat mode" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:179 -msgid "Change shortcut..." -msgstr "" - -#: core/globalshortcuts.cpp:60 -msgid "Change shuffle mode" -msgstr "" - -#: core/commandlineoptions.cpp:163 -msgid "Change the language" -msgstr "" - -#: ui/mainwindow.cpp:596 -msgid "Check for updates..." -msgstr "" - -#: smartplaylists/wizard.cpp:80 -msgid "Choose a name for your smart playlist" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:280 -msgid "Choose automatically" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:426 -msgid "Choose color..." -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:427 -msgid "Choose font..." -msgstr "" - -#: ../bin/src/ui_visualisationselector.h:113 -msgid "Choose from the list" -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:147 -msgid "Choose how the playlist is sorted and how many songs it will contain." -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:188 -msgid "" -"Choose the websites you want Clementine to use when searching for lyrics." -msgstr "" - -#: ui/equalizer.cpp:109 -msgid "Classical" -msgstr "" - -#: widgets/lineedit.cpp:41 ../bin/src/ui_queuemanager.h:139 -msgid "Clear" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:645 ../bin/src/ui_mainwindow.h:647 -msgid "Clear playlist" -msgstr "" - -#: smartplaylists/searchtermwidget.cpp:321 -#: visualisations/visualisationcontainer.cpp:196 -#: ../bin/src/ui_mainwindow.h:628 ../bin/src/ui_visualisationoverlay.h:179 -msgid "Clementine" -msgstr "" - -#: ../bin/src/ui_errordialog.h:93 -msgid "Clementine Error" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:422 -msgid "Clementine Orange" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:70 -msgid "Clementine Visualization" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:376 -msgid "" -"Clementine can automatically convert the music you copy to this device into " -"a format that it can play." -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:200 remote/remotesettingspage.cpp:152 -msgid "" -"Clementine can be controlled remotely by an Android phone. To enable this " -"feature log in with the same Google account that is configured on your " -"phone." -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:396 -msgid "Clementine can show a message when the track changes." -msgstr "" - -#: visualisations/projectmvisualisation.cpp:128 -msgid "" -"Clementine could not load any projectM visualisations. Check that you have " -"installed Clementine properly." -msgstr "" - -#: internet/lastfmsettingspage.cpp:103 -msgid "" -"Clementine couldn't fetch your subscription status since there are problems " -"with your connection. Played tracks will be cached and sent later to " -"Last.fm." -msgstr "" - -#: widgets/prettyimage.cpp:201 -msgid "Clementine image viewer" -msgstr "" - -#: ../bin/src/ui_trackselectiondialog.h:206 -msgid "Clementine was unable to find results for this file" -msgstr "" - -#: library/libraryview.cpp:225 -msgid "Click here to add some music" -msgstr "" - -#: ../bin/src/ui_trackslider.h:72 -msgid "Click to toggle between remaining time and total time" -msgstr "" - -#: widgets/didyoumean.cpp:35 -msgid "Close" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:119 -msgid "Close visualization" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:280 -msgid "Closing this window will cancel the download." -msgstr "" - -#: ui/albumcovermanager.cpp:213 -msgid "Closing this window will stop searching for album covers." -msgstr "" - -#: ui/equalizer.cpp:110 -msgid "Club" -msgstr "" - -#: devices/ilister.cpp:95 -msgid "Color" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:170 -msgid "Combine identical results from different sources" -msgstr "" - -#: core/commandlineoptions.cpp:166 -msgid "Comma separated list of class:level, level is 0-3" -msgstr "" - -#: playlist/playlist.cpp:1134 smartplaylists/searchterm.cpp:279 -#: ui/organisedialog.cpp:62 ../bin/src/ui_edittagdialog.h:661 -msgid "Comment" -msgstr "" - -#: ../bin/src/ui_edittagdialog.h:662 -msgid "Complete tags automatically" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:695 -msgid "Complete tags automatically..." -msgstr "" - -#: playlist/playlist.cpp:1116 ui/organisedialog.cpp:56 -#: ../bin/src/ui_groupbydialog.h:132 ../bin/src/ui_groupbydialog.h:145 -#: ../bin/src/ui_groupbydialog.h:158 ../bin/src/ui_edittagdialog.h:659 -msgid "Composer" -msgstr "" - -#: internet/groovesharkservice.cpp:504 -msgid "Configure Grooveshark..." -msgstr "" - -#: internet/lastfmservice.cpp:111 -msgid "Configure Last.fm..." -msgstr "" - -#: internet/magnatuneservice.cpp:275 -msgid "Configure Magnatune..." -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:167 -msgid "Configure Shortcuts" -msgstr "" - -#: internet/spotifyservice.cpp:491 -msgid "Configure Spotify..." -msgstr "" - -#: ui/mainwindow.cpp:490 -msgid "Configure library..." -msgstr "" - -#: internet/digitallyimportedservicebase.cpp:185 -#: ../bin/src/ui_globalsearchsettingspage.h:174 -msgid "Configure..." -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:186 -msgid "Connect Wii Remotes using active/deactive action" -msgstr "" - -#: devices/devicemanager.cpp:324 devices/devicemanager.cpp:328 -msgid "Connect device" -msgstr "" - -#: internet/spotifyservice.cpp:246 -msgid "Connecting to Spotify" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:196 -msgid "Constant bitrate" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:379 -msgid "Convert all music" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:378 -msgid "Convert any music that the device can't play" -msgstr "" - -#: internet/groovesharkservice.cpp:1055 -msgid "Copy to clipboard" -msgstr "" - -#: library/libraryview.cpp:265 ui/mainwindow.cpp:523 -#: widgets/fileviewlist.cpp:43 -msgid "Copy to device..." -msgstr "" - -#: devices/deviceview.cpp:223 ui/mainwindow.cpp:520 -#: widgets/fileviewlist.cpp:38 -msgid "Copy to library..." -msgstr "" - -#: devices/afctransfer.cpp:50 -msgid "Copying iPod database" -msgstr "" - -#: transcoder/transcoder.cpp:62 -#, qt-format -msgid "" -"Could not create the GStreamer element \"%1\" - make sure you have all the " -"required GStreamer plugins installed" -msgstr "" - -#: transcoder/transcoder.cpp:425 -#, qt-format -msgid "" -"Couldn't find a muxer for %1, check you have the correct GStreamer plugins " -"installed" -msgstr "" - -#: transcoder/transcoder.cpp:419 -#, qt-format -msgid "" -"Couldn't find an encoder for %1, check you have the correct GStreamer " -"plugins installed" -msgstr "" - -#: internet/lastfmservice.cpp:899 -msgid "Couldn't load the last.fm radio station" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:203 -#, qt-format -msgid "Couldn't open output file %1" -msgstr "" - -#: ../bin/src/ui_albumcovermanager.h:149 -#: ../bin/src/ui_albumcoversearcher.h:104 ../bin/src/ui_mainwindow.h:668 -msgid "Cover Manager" -msgstr "" - -#: ui/edittagdialog.cpp:405 -msgid "Cover art from embedded image" -msgstr "" - -#: ui/edittagdialog.cpp:407 -#, qt-format -msgid "Cover art loaded automatically from %1" -msgstr "" - -#: ui/edittagdialog.cpp:400 -msgid "Cover art manually unset" -msgstr "" - -#: ui/edittagdialog.cpp:409 -msgid "Cover art not set" -msgstr "" - -#: ui/edittagdialog.cpp:403 -#, qt-format -msgid "Cover art set from %1" -msgstr "" - -#: covers/coversearchstatisticsdialog.cpp:60 ui/albumcoversearcher.cpp:104 -#, qt-format -msgid "Covers from %1" -msgstr "" - -#: internet/groovesharkservice.cpp:485 internet/groovesharkservice.cpp:1128 -msgid "Create a new Grooveshark playlist" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:262 -msgid "Cross-fade when changing tracks automatically" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:261 -msgid "Cross-fade when changing tracks manually" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:640 -msgid "Ctrl+Alt+V" -msgstr "Ctrl+Alt+V" - -#: ../bin/src/ui_mainwindow.h:644 -msgid "Ctrl+B" -msgstr "Ctrl+B" - -#: ../bin/src/ui_queuemanager.h:133 -msgid "Ctrl+Down" -msgstr "Ctrl+Down" - -#: ../bin/src/ui_mainwindow.h:651 -msgid "Ctrl+E" -msgstr "Ctrl+E" - -#: ../bin/src/ui_mainwindow.h:661 -msgid "Ctrl+H" -msgstr "Ctrl+H" - -#: ../bin/src/ui_mainwindow.h:679 -msgid "Ctrl+J" -msgstr "Ctrl+J" - -#: ../bin/src/ui_queuemanager.h:141 ../bin/src/ui_mainwindow.h:649 -msgid "Ctrl+K" -msgstr "Ctrl+K" - -#: ../bin/src/ui_mainwindow.h:642 -msgid "Ctrl+L" -msgstr "Ctrl+L" - -#: ../bin/src/ui_mainwindow.h:693 -msgid "Ctrl+M" -msgstr "Ctrl+M" - -#: ../bin/src/ui_mainwindow.h:681 -msgid "Ctrl+N" -msgstr "Ctrl+N" - -#: ../bin/src/ui_mainwindow.h:665 -msgid "Ctrl+O" -msgstr "Ctrl+O" - -#: ../bin/src/ui_mainwindow.h:657 -msgid "Ctrl+P" -msgstr "Ctrl+P" - -#: ../bin/src/ui_mainwindow.h:638 -msgid "Ctrl+Q" -msgstr "Ctrl+Q" - -#: ../bin/src/ui_mainwindow.h:683 -msgid "Ctrl+S" -msgstr "Ctrl+S" - -#: ../bin/src/ui_mainwindow.h:663 -msgid "Ctrl+Shift+A" -msgstr "Ctrl+Shift+A" - -#: ../bin/src/ui_mainwindow.h:685 -msgid "Ctrl+Shift+O" -msgstr "Ctrl+Shift+O" - -#: ../bin/src/ui_mainwindow.h:696 -msgid "Ctrl+T" -msgstr "Ctrl+T" - -#: ../bin/src/ui_queuemanager.h:129 -msgid "Ctrl+Up" -msgstr "Ctrl+Up" - -#: ui/equalizer.cpp:108 ../bin/src/ui_lastfmstationdialog.h:98 -msgid "Custom" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:409 -msgid "Custom message settings" -msgstr "" - -#: internet/lastfmservice.cpp:199 -msgid "Custom radio" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:423 -msgid "Custom..." -msgstr "" - -#: devices/devicekitlister.cpp:123 -msgid "DBus path" -msgstr "" - -#: ui/equalizer.cpp:111 -msgid "Dance" -msgstr "" - -#: playlist/playlist.cpp:1132 ../bin/src/ui_edittagdialog.h:649 -msgid "Date created" -msgstr "" - -#: playlist/playlist.cpp:1131 ../bin/src/ui_edittagdialog.h:648 -msgid "Date modified" -msgstr "" - -#: smartplaylists/searchterm.cpp:301 -msgid "Days" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:177 -msgid "De&fault" -msgstr "" - -#: core/commandlineoptions.cpp:153 -msgid "Decrease the volume by 4%" -msgstr "" - -#: core/globalshortcuts.cpp:53 wiimotedev/wiimotesettingspage.cpp:104 -msgid "Decrease volume" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:195 -msgid "Defaults" -msgstr "" - -#: ../bin/src/ui_visualisationselector.h:115 -msgid "Delay between visualizations" -msgstr "" - -#: internet/groovesharkservice.cpp:488 internet/groovesharkservice.cpp:1176 -msgid "Delete Grooveshark playlist" -msgstr "" - -#: devices/deviceview.cpp:393 library/libraryview.cpp:460 -#: ui/mainwindow.cpp:1829 widgets/fileview.cpp:185 -msgid "Delete files" -msgstr "" - -#: devices/deviceview.cpp:225 -msgid "Delete from device..." -msgstr "" - -#: library/libraryview.cpp:267 ui/mainwindow.cpp:524 -#: widgets/fileviewlist.cpp:44 -msgid "Delete from disk..." -msgstr "" - -#: ui/equalizer.cpp:190 ../bin/src/ui_equalizer.h:124 -msgid "Delete preset" -msgstr "" - -#: library/libraryview.cpp:259 -msgid "Delete smart playlist" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:194 -msgid "Delete the original files" -msgstr "" - -#: core/deletefiles.cpp:50 -msgid "Deleting files" -msgstr "" - -#: ui/mainwindow.cpp:1334 -msgid "Dequeue selected tracks" -msgstr "" - -#: ui/mainwindow.cpp:1332 -msgid "Dequeue track" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:211 ../bin/src/ui_organisedialog.h:189 -msgid "Destination" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:217 -msgid "Details..." -msgstr "" - -#: devices/devicekitlister.cpp:126 devices/giolister.cpp:158 -msgid "Device" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:368 -msgid "Device Properties" -msgstr "" - -#: devices/wmdmlister.cpp:368 -msgid "Device name" -msgstr "" - -#: devices/deviceview.cpp:212 -msgid "Device properties..." -msgstr "" - -#: ui/mainwindow.cpp:253 -msgid "Devices" -msgstr "" - -#: widgets/didyoumean.cpp:128 -msgid "Did you mean" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:160 -msgid "Digitally Imported" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:164 -msgid "Digitally Imported password" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:162 -msgid "Digitally Imported username" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:159 -msgid "Direct internet connection" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:145 -#: ../bin/src/ui_transcodedialog.h:204 -msgid "Directory" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:405 -msgid "Disable duration" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:398 -msgid "Disabled" -msgstr "" - -#: playlist/playlist.cpp:1112 ui/organisedialog.cpp:58 -#: ../bin/src/ui_edittagdialog.h:655 -msgid "Disc" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:234 -msgid "Discontinuous transmission" -msgstr "" - -#: internet/icecastfilterwidget.cpp:33 library/libraryfilterwidget.cpp:109 -#: ../bin/src/ui_librarysettingspage.h:169 -msgid "Display options" -msgstr "" - -#: core/commandlineoptions.cpp:161 -msgid "Display the on-screen-display" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:694 -msgid "Do a full library rescan" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:377 -msgid "Do not convert any music" -msgstr "" - -#: widgets/osd.cpp:307 ../bin/src/ui_playlistsequence.h:103 -msgid "Don't repeat" -msgstr "" - -#: library/libraryview.cpp:281 -msgid "Don't show in various artists" -msgstr "" - -#: widgets/osd.cpp:294 ../bin/src/ui_playlistsequence.h:107 -msgid "Don't shuffle" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:282 ui/albumcovermanager.cpp:215 -msgid "Don't stop!" -msgstr "" - -#: devices/deviceview.cpp:114 -msgid "Double click to open" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:198 -msgid "Double clicking a song will..." -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:252 -msgid "Download directory" -msgstr "" - -#: ../bin/src/ui_magnatunesettingspage.h:161 -msgid "Download membership" -msgstr "" - -#: internet/magnatuneservice.cpp:271 -msgid "Download this album" -msgstr "" - -#: internet/jamendoservice.cpp:413 -msgid "Download this album..." -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:216 -msgid "Download..." -msgstr "" - -#: internet/icecastservice.cpp:97 -msgid "Downloading Icecast directory" -msgstr "" - -#: internet/jamendoservice.cpp:183 -msgid "Downloading Jamendo catalogue" -msgstr "" - -#: internet/magnatuneservice.cpp:156 -msgid "Downloading Magnatune catalogue" -msgstr "" - -#: internet/spotifyblobdownloader.cpp:43 -msgid "Downloading Spotify plugin" -msgstr "" - -#: musicbrainz/tagfetcher.cpp:101 -msgid "Downloading metadata" -msgstr "" - -#: ui/notificationssettingspage.cpp:37 -msgid "Drag to reposition" -msgstr "" - -#: devices/wmdmlister.cpp:365 -msgid "Drive letter" -msgstr "" - -#: ../bin/src/ui_dynamicplaylistcontrols.h:109 -msgid "Dynamic mode is on" -msgstr "" - -#: internet/jamendoservice.cpp:111 library/library.cpp:88 -msgid "Dynamic random mix" -msgstr "" - -#: library/libraryview.cpp:257 -msgid "Edit smart playlist..." -msgstr "" - -#: ui/mainwindow.cpp:1367 -#, qt-format -msgid "Edit tag \"%1\"..." -msgstr "" - -#: ../bin/src/ui_mainwindow.h:654 -msgid "Edit tag..." -msgstr "" - -#: ../bin/src/ui_edittagdialog.h:663 -msgid "Edit tags" -msgstr "" - -#: ../bin/src/ui_edittagdialog.h:632 -msgid "Edit track information" -msgstr "" - -#: library/libraryview.cpp:271 ../bin/src/ui_mainwindow.h:650 -msgid "Edit track information..." -msgstr "" - -#: library/libraryview.cpp:273 -msgid "Edit tracks information..." -msgstr "" - -#: internet/savedradio.cpp:101 -msgid "Edit..." -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:183 -msgid "Enable Wii Remote support" -msgstr "" - -#: ../bin/src/ui_equalizer.h:126 -msgid "Enable equalizer" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:179 -msgid "Enable playlist background image" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:187 -msgid "Enable shortcuts only when Clementine is focused" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:171 -msgid "" -"Enable sources below to include them in search results. When identical " -"results are available from more than one source, ones at the top will take " -"priority." -msgstr "" - -#: core/globalshortcuts.cpp:62 -msgid "Enable/disable Last.fm scrobbling" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:235 -msgid "Encoding complexity" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:197 -msgid "Encoding engine quality" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:224 -msgid "Encoding mode" -msgstr "" - -#: ../bin/src/ui_coverfromurldialog.h:103 -msgid "Enter a URL to download a cover from the Internet:" -msgstr "" - -#: playlist/playlisttabbar.cpp:131 -msgid "Enter a new name for this playlist" -msgstr "" - -#: ../bin/src/ui_lastfmstationdialog.h:93 -msgid "" -"Enter an artist or tag to start listening to Last.fm radio." -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:131 -#: ../bin/src/ui_albumcovermanager.h:153 -msgid "Enter search terms here" -msgstr "" - -#: ../bin/src/ui_addstreamdialog.h:114 -msgid "Enter the URL of an internet radio stream:" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:115 -msgid "Entire collection" -msgstr "" - -#: ../bin/src/ui_equalizer.h:118 ../bin/src/ui_mainwindow.h:675 -msgid "Equalizer" -msgstr "" - -#: core/commandlineoptions.cpp:164 -msgid "Equivalent to --log-levels *:1" -msgstr "" - -#: core/commandlineoptions.cpp:165 -msgid "Equivalent to --log-levels *:3" -msgstr "" - -#: internet/groovesharkservice.cpp:938 -#: internet/magnatunedownloaddialog.cpp:225 library/libraryview.cpp:454 -#: ui/edittagdialog.cpp:732 ui/mainwindow.cpp:1797 ui/mainwindow.cpp:1902 -#: ui/mainwindow.cpp:2121 -msgid "Error" -msgstr "" - -#: devices/mtploader.cpp:56 -msgid "Error connecting MTP device" -msgstr "" - -#: ui/organiseerrordialog.cpp:55 -msgid "Error copying songs" -msgstr "" - -#: ui/organiseerrordialog.cpp:60 -msgid "Error deleting songs" -msgstr "" - -#: internet/spotifyblobdownloader.cpp:214 -msgid "Error downloading Spotify plugin" -msgstr "" - -#: playlist/songloaderinserter.cpp:71 playlist/songloaderinserter.cpp:133 -#, qt-format -msgid "Error loading %1" -msgstr "" - -#: internet/digitallyimportedservicebase.cpp:207 -#: internet/digitallyimportedurlhandler.cpp:73 -msgid "Error loading di.fm playlist" -msgstr "" - -#: transcoder/transcoder.cpp:393 -#, qt-format -msgid "Error processing %1: %2" -msgstr "" - -#: playlist/songloaderinserter.cpp:100 -msgid "Error while loading audio CD" -msgstr "" - -#: library/library.cpp:58 -msgid "Ever played" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:263 -msgid "Except between tracks on the same album or in the same CUE sheet" -msgstr "" - -#: ../bin/src/ui_dynamicplaylistcontrols.h:111 -msgid "Expand" -msgstr "" - -#: widgets/loginstatewidget.cpp:142 -#, qt-format -msgid "Expires on %1" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:659 -msgid "F1" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:655 -msgid "F2" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:630 -msgid "F5" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:632 -msgid "F6" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:634 -msgid "F7" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:636 -msgid "F8" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:140 -#: ../bin/src/ui_magnatunesettingspage.h:171 -#: ../bin/src/ui_transcodersettingspage.h:161 -msgid "FLAC" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:260 -msgid "Fade out when stopping a track" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:259 -msgid "Fading" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:264 -msgid "Fading duration" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsflac.h:82 -#: ../bin/src/ui_transcoderoptionsmp3.h:200 -msgid "Fast" -msgstr "" - -#: internet/groovesharkservice.cpp:557 -msgid "Favorites" -msgstr "" - -#: library/library.cpp:72 -msgid "Favourite tracks" -msgstr "" - -#: ../bin/src/ui_albumcovermanager.h:155 -msgid "Fetch Missing Covers" -msgstr "" - -#: ../bin/src/ui_albumcovermanager.h:150 -msgid "Fetch automatically" -msgstr "" - -#: ../bin/src/ui_coversearchstatisticsdialog.h:75 -msgid "Fetch completed" -msgstr "" - -#: ui/coverfromurldialog.cpp:71 ui/coverfromurldialog.cpp:82 -msgid "Fetching cover error" -msgstr "" - -#: ui/organisedialog.cpp:66 -msgid "File extension" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:384 -msgid "File formats" -msgstr "" - -#: playlist/playlist.cpp:1127 ../bin/src/ui_edittagdialog.h:650 -msgid "File name" -msgstr "" - -#: playlist/playlist.cpp:1128 -msgid "File name (without path)" -msgstr "" - -#: playlist/playlist.cpp:1129 ../bin/src/ui_edittagdialog.h:644 -msgid "File size" -msgstr "" - -#: playlist/playlist.cpp:1130 ../bin/src/ui_groupbydialog.h:133 -#: ../bin/src/ui_groupbydialog.h:146 ../bin/src/ui_groupbydialog.h:159 -#: ../bin/src/ui_edittagdialog.h:646 -msgid "File type" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:205 -msgid "Filename" -msgstr "" - -#: ui/mainwindow.cpp:251 -msgid "Files" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:202 -msgid "Files to transcode" -msgstr "" - -#: devices/wmdmlister.cpp:367 -msgid "Filesystem name" -msgstr "" - -#: devices/wmdmlister.cpp:372 -msgid "Filesystem serial number" -msgstr "" - -#: devices/wmdmlister.cpp:366 -msgid "Filesystem type" -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:87 -msgid "Find songs in your library that match the criteria you specify." -msgstr "" - -#: musicbrainz/tagfetcher.cpp:54 -msgid "Fingerprinting song" -msgstr "" - -#: smartplaylists/wizard.cpp:79 -msgid "Finish" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:125 -msgid "First level" -msgstr "" - -#: core/song.cpp:386 -msgid "Flac" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:181 -msgid "Font size" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:214 -msgid "For licensing reasons Spotify support is in a separate plugin." -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:204 -msgid "Force mono encoding" -msgstr "" - -#: devices/deviceview.cpp:209 devices/deviceview.cpp:315 -#: devices/deviceview.cpp:319 -msgid "Forget device" -msgstr "" - -#: devices/deviceview.cpp:316 -msgid "" -"Forgetting a device will remove it from this list and Clementine will have " -"to rescan all the songs again next time you connect it." -msgstr "" - -#: ../bin/src/ui_globalsearchwidget.h:59 -#: ../bin/src/ui_icecastfilterwidget.h:74 -#: ../bin/src/ui_internetviewcontainer.h:70 -#: ../bin/src/ui_libraryfilterwidget.h:114 -#: ../bin/src/ui_libraryviewcontainer.h:59 -#: ../bin/src/ui_playlistcontainer.h:143 ../bin/src/ui_querysearchpage.h:70 -#: ../bin/src/ui_querysortpage.h:136 ../bin/src/ui_searchpreview.h:104 -#: ../bin/src/ui_searchtermwidget.h:279 ../bin/src/ui_wizardfinishpage.h:83 -#: ../bin/src/ui_transcoderoptionsaac.h:128 -#: ../bin/src/ui_transcoderoptionsflac.h:80 -#: ../bin/src/ui_transcoderoptionsmp3.h:190 -#: ../bin/src/ui_transcoderoptionsspeex.h:216 -#: ../bin/src/ui_transcoderoptionsvorbis.h:201 -#: ../bin/src/ui_transcoderoptionswma.h:78 ../bin/src/ui_equalizerslider.h:82 -#: ../bin/src/ui_fileview.h:106 ../bin/src/ui_loginstatewidget.h:171 -#: ../bin/src/ui_trackslider.h:69 ../bin/src/ui_visualisationoverlay.h:178 -msgid "Form" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:136 -msgid "Format" -msgstr "" - -#: analyzers/analyzercontainer.cpp:46 -#: visualisations/visualisationcontainer.cpp:97 -msgid "Framerate" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:236 -msgid "Frames per buffer" -msgstr "" - -#: internet/lastfmservice.cpp:207 -msgid "Friends" -msgstr "" - -#: ui/equalizer.cpp:112 -msgid "Full Bass" -msgstr "" - -#: ui/equalizer.cpp:114 -msgid "Full Bass + Treble" -msgstr "" - -#: ui/equalizer.cpp:113 -msgid "Full Treble" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:276 -msgid "GStreamer audio engine" -msgstr "" - -#: ui/settingsdialog.cpp:111 -msgid "General" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:402 -msgid "General settings" -msgstr "" - -#: playlist/playlist.cpp:1114 ui/organisedialog.cpp:61 -#: ../bin/src/ui_groupbydialog.h:134 ../bin/src/ui_groupbydialog.h:147 -#: ../bin/src/ui_groupbydialog.h:160 ../bin/src/ui_edittagdialog.h:660 -msgid "Genre" -msgstr "" - -#: internet/groovesharkservice.cpp:1019 -msgid "Get an URL to share this Grooveshark song" -msgstr "" - -#: internet/groovesharkservice.cpp:706 -msgid "Getting Grooveshark popular songs" -msgstr "" - -#: internet/somafmservice.cpp:94 -msgid "Getting channels" -msgstr "" - -#: internet/digitallyimportedservicebase.cpp:106 -msgid "Getting streams" -msgstr "" - -#: ../bin/src/ui_addstreamdialog.h:116 -msgid "Give it a name:" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:686 -msgid "Go to next playlist tab" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:687 -msgid "Go to previous playlist tab" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:206 -msgid "Google password" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:204 -msgid "Google username" -msgstr "" - -#: covers/coversearchstatisticsdialog.cpp:54 ui/albumcovermanager.cpp:431 -#: ../bin/src/ui_coversearchstatisticsdialog.h:76 -#, qt-format -msgid "Got %1 covers out of %2 (%3 failed)" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:190 -msgid "Grey out non existent songs in my playlists" -msgstr "" - -#: ../bin/src/ui_groovesharksettingspage.h:112 -msgid "Grooveshark" -msgstr "" - -#: internet/groovesharkservice.cpp:406 -msgid "Grooveshark login error" -msgstr "" - -#: internet/groovesharkservice.cpp:548 -msgid "Grooveshark radio" -msgstr "" - -#: internet/groovesharkservice.cpp:1051 -msgid "Grooveshark song's URL" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:124 -msgid "Group Library by..." -msgstr "" - -#: library/libraryfilterwidget.cpp:103 -msgid "Group by" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:127 -msgid "Group by Album" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:124 -msgid "Group by Artist" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:125 -msgid "Group by Artist/Album" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:126 -msgid "Group by Artist/Year - Album" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:128 -msgid "Group by Genre/Album" -msgstr "" - -#: ../bin/src/ui_libraryfilterwidget.h:129 -msgid "Group by Genre/Artist/Album" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:163 -msgid "HTTP proxy" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:371 -msgid "Hardware information" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:372 -msgid "Hardware information is only available while the device is connected." -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:168 -msgid "Hide all other search boxes" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:202 -msgid "High" -msgstr "" - -#: analyzers/analyzercontainer.cpp:64 -#, qt-format -msgid "High (%1 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:112 -msgid "High (1024x1024)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:102 -msgid "High (35 fps)" -msgstr "" - -#: smartplaylists/searchterm.cpp:300 -msgid "Hours" -msgstr "" - -#: core/backgroundstreams.cpp:30 -msgid "Hypnotoad" -msgstr "" - -#: ../bin/src/ui_magnatunesettingspage.h:159 -msgid "I don't have a Magnatune account" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:370 -msgid "Icon" -msgstr "" - -#: widgets/fancytabwidget.cpp:651 -msgid "Icons on top" -msgstr "" - -#: musicbrainz/tagfetcher.cpp:85 -msgid "Identifying song" -msgstr "" - -#: devices/devicemanager.cpp:569 devices/devicemanager.cpp:577 -msgid "" -"If you continue, this device will work slowly and songs copied to it may not" -" work." -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:209 -msgid "" -"If you use the remote on more than one computer, this name will help you " -"choose which one to connect to on your phone." -msgstr "" - -#: ../bin/src/ui_organisedialog.h:204 -msgid "Ignore \"The\" in artist names" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:41 -msgid "Images (*.png *.jpg *.jpeg *.bmp *.gif *.xpm *.pbm *.pgm *.ppm *.xbm)" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:43 -msgid "Images (*.png *.jpg *.jpeg *.bmp *.xpm *.pbm *.ppm *.xbm)" -msgstr "" - -#: ../bin/src/ui_wizardfinishpage.h:86 -msgid "" -"In dynamic mode new tracks will be chosen and added to the playlist every " -"time a song finishes." -msgstr "" - -#: internet/spotifyservice.cpp:347 -msgid "Inbox" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:408 -msgid "Include album art in the notification" -msgstr "" - -#: ../bin/src/ui_querysearchpage.h:76 -msgid "Include all songs" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:177 -msgid "Include keyboard shortcut help in the tooltip" -msgstr "" - -#: core/commandlineoptions.cpp:152 -msgid "Increase the volume by 4%" -msgstr "" - -#: core/globalshortcuts.cpp:52 wiimotedev/wiimotesettingspage.cpp:103 -msgid "Increase volume" -msgstr "" - -#: wiimotedev/wiimotesettingspage.cpp:124 ../bin/src/ui_deviceproperties.h:373 -msgid "Information" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:203 -msgid "Insert..." -msgstr "" - -#: internet/spotifysettingspage.cpp:74 -msgid "Installed" -msgstr "" - -#: ui/mainwindow.cpp:252 -msgid "Internet" -msgstr "" - -#: ui/settingsdialog.cpp:135 -msgid "Internet providers" -msgstr "" - -#: internet/lastfmservice.cpp:438 -msgid "Invalid API key" -msgstr "" - -#: internet/lastfmservice.cpp:433 -msgid "Invalid format" -msgstr "" - -#: internet/lastfmservice.cpp:431 -msgid "Invalid method" -msgstr "" - -#: internet/lastfmservice.cpp:434 -msgid "Invalid parameters" -msgstr "" - -#: internet/lastfmservice.cpp:435 -msgid "Invalid resource specified" -msgstr "" - -#: internet/lastfmservice.cpp:430 -msgid "Invalid service" -msgstr "" - -#: internet/lastfmservice.cpp:437 -msgid "Invalid session key" -msgstr "" - -#: internet/groovesharkservice.cpp:399 -msgid "Invalid username and/or password" -msgstr "" - -#: internet/jamendoservice.cpp:124 -msgid "Jamendo" -msgstr "" - -#: internet/jamendoservice.cpp:107 -msgid "Jamendo Most Listened Tracks" -msgstr "" - -#: internet/jamendoservice.cpp:105 -msgid "Jamendo Top Tracks" -msgstr "" - -#: internet/jamendoservice.cpp:101 -msgid "Jamendo Top Tracks of the Month" -msgstr "" - -#: internet/jamendoservice.cpp:103 -msgid "Jamendo Top Tracks of the Week" -msgstr "" - -#: internet/jamendoservice.cpp:167 -msgid "Jamendo database" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:678 -msgid "Jump to the currently playing track" -msgstr "" - -#: wiimotedev/wiimoteshortcutgrabber.cpp:72 -#, qt-format -msgid "Keep buttons for %1 second..." -msgstr "" - -#: wiimotedev/wiimoteshortcutgrabber.cpp:73 -#: wiimotedev/wiimoteshortcutgrabber.cpp:117 -#: ../bin/src/ui_wiimoteshortcutgrabber.h:127 -#, qt-format -msgid "Keep buttons for %1 seconds..." -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:178 -msgid "Keep running in the background when the window is closed" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:193 -msgid "Keep the original files" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:671 -msgid "Kittens" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:180 -msgid "Language" -msgstr "" - -#: ui/equalizer.cpp:115 -msgid "Laptop/Headphones" -msgstr "" - -#: ui/equalizer.cpp:116 -msgid "Large Hall" -msgstr "" - -#: widgets/nowplayingwidget.cpp:91 -msgid "Large album cover" -msgstr "" - -#: widgets/fancytabwidget.cpp:647 -msgid "Large sidebar" -msgstr "" - -#: library/library.cpp:66 playlist/playlist.cpp:1121 -#: ../bin/src/ui_edittagdialog.h:641 -msgid "Last played" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:144 -msgid "Last.fm" -msgstr "" - -#: internet/lastfmservice.cpp:70 -#, qt-format -msgid "Last.fm Custom Radio: %1" -msgstr "" - -#: internet/lastfmservice.cpp:238 internet/lastfmservice.cpp:693 -#: internet/lastfmservice.cpp:728 -#, qt-format -msgid "Last.fm Library - %1" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:75 internet/lastfmservice.cpp:240 -#: internet/lastfmservice.cpp:243 -#, qt-format -msgid "Last.fm Mix Radio - %1" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:77 internet/lastfmservice.cpp:245 -#: internet/lastfmservice.cpp:248 -#, qt-format -msgid "Last.fm Neighbor Radio - %1" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:73 internet/lastfmservice.cpp:235 -#, qt-format -msgid "Last.fm Radio Station - %1" -msgstr "" - -#: internet/lastfmservice.cpp:68 -#, qt-format -msgid "Last.fm Similar Artists to %1" -msgstr "" - -#: internet/lastfmservice.cpp:69 -#, qt-format -msgid "Last.fm Tag Radio: %1" -msgstr "" - -#: internet/lastfmservice.cpp:442 -msgid "Last.fm is currently busy, please try again in a few minutes" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:148 -msgid "Last.fm password" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:86 -msgid "Last.fm play counts" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:139 -msgid "Last.fm tags" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:146 -msgid "Last.fm username" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:119 -msgid "Last.fm wiki" -msgstr "" - -#: library/library.cpp:82 -msgid "Least favourite tracks" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:282 -msgid "Leave blank for the default. Examples: \"/dev/dsp\", \"front\", etc." -msgstr "" - -#: playlist/playlist.cpp:1110 ui/organisedialog.cpp:63 -#: ui/qtsystemtrayicon.cpp:255 ../bin/src/ui_edittagdialog.h:636 -msgid "Length" -msgstr "" - -#: ui/mainwindow.cpp:240 ui/mainwindow.cpp:250 -msgid "Library" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:122 -msgid "Library advanced grouping" -msgstr "" - -#: ui/mainwindow.cpp:2022 -msgid "Library rescan notice" -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:83 -msgid "Library search" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:140 -msgid "Limits" -msgstr "" - -#: internet/groovesharkservice.cpp:549 -msgid "" -"Listen to Grooveshark songs based on what you've listened to previously" -msgstr "" - -#: ui/equalizer.cpp:117 -msgid "Live" -msgstr "" - -#: ../bin/src/ui_albumcovermanager.h:151 -msgid "Load" -msgstr "" - -#: ../bin/src/ui_coverfromurldialog.h:102 -msgid "Load cover from URL" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:60 -msgid "Load cover from URL..." -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:92 -msgid "Load cover from disk" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:58 -msgid "Load cover from disk..." -msgstr "" - -#: playlist/playlistcontainer.cpp:303 -msgid "Load playlist" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:684 -msgid "Load playlist..." -msgstr "" - -#: internet/lastfmservice.cpp:912 -msgid "Loading Last.fm radio" -msgstr "" - -#: devices/mtploader.cpp:42 -msgid "Loading MTP device" -msgstr "" - -#: devices/wmdmloader.cpp:47 -msgid "Loading Windows Media device" -msgstr "" - -#: devices/gpodloader.cpp:46 -msgid "Loading iPod database" -msgstr "" - -#: smartplaylists/generatorinserter.cpp:52 -msgid "Loading smart playlist" -msgstr "" - -#: library/librarymodel.cpp:135 -msgid "Loading songs" -msgstr "" - -#: internet/digitallyimportedurlhandler.cpp:51 -#: internet/somafmurlhandler.cpp:45 -msgid "Loading stream" -msgstr "" - -#: playlist/songloaderinserter.cpp:79 ui/edittagdialog.cpp:210 -msgid "Loading tracks" -msgstr "" - -#: playlist/songloaderinserter.cpp:139 -msgid "Loading tracks info" -msgstr "" - -#: library/librarymodel.cpp:130 widgets/prettyimage.cpp:168 -#: widgets/widgetfadehelper.cpp:93 ../bin/src/ui_searchpreview.h:106 -msgid "Loading..." -msgstr "" - -#: core/commandlineoptions.cpp:158 -msgid "Loads files/URLs, replacing current playlist" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:163 -#: ../bin/src/ui_groovesharksettingspage.h:116 -#: ../bin/src/ui_magnatunesettingspage.h:164 -#: ../bin/src/ui_spotifysettingspage.h:212 -#: ../bin/src/ui_remotesettingspage.h:205 -#: ../bin/src/ui_lastfmsettingspage.h:147 -msgid "Login" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:137 -msgid "Long term prediction profile (LTP)" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:641 -msgid "Love" -msgstr "" - -#: analyzers/analyzercontainer.cpp:62 -#, qt-format -msgid "Low (%1 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:100 -msgid "Low (15 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:110 -msgid "Low (256x256)" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:135 -msgid "Low complexity profile (LC)" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:187 -msgid "Lyrics" -msgstr "" - -#: songinfo/ultimatelyricsprovider.cpp:135 -#, qt-format -msgid "Lyrics from %1" -msgstr "" - -#: core/song.cpp:389 ../bin/src/ui_transcodersettingspage.h:159 -msgid "MP3" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:177 -msgid "MP3 256k" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:170 -msgid "MP3 96k" -msgstr "" - -#: core/song.cpp:387 -msgid "MP4 AAC" -msgstr "" - -#: core/song.cpp:388 -msgid "MPC" -msgstr "" - -#: internet/magnatuneservice.cpp:100 ../bin/src/ui_magnatunesettingspage.h:154 -msgid "Magnatune" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:131 -msgid "Magnatune Download" -msgstr "" - -#: widgets/osd.cpp:211 -msgid "Magnatune download finished" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:134 -msgid "Main profile (MAIN)" -msgstr "" - -#: internet/spotifyservice.cpp:496 -msgid "Make playlist available offline" -msgstr "" - -#: internet/lastfmservice.cpp:449 -msgid "Malformed response" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:160 -msgid "Manual proxy configuration" -msgstr "" - -#: devices/deviceproperties.cpp:153 -msgid "Manufacturer" -msgstr "" - -#: ../bin/src/ui_querysearchpage.h:74 -msgid "Match every search term (AND)" -msgstr "" - -#: ../bin/src/ui_querysearchpage.h:75 -msgid "Match one or more search terms (OR)" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsvorbis.h:209 -msgid "Maximum bitrate" -msgstr "" - -#: analyzers/analyzercontainer.cpp:63 -#, qt-format -msgid "Medium (%1 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:101 -msgid "Medium (25 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:111 -msgid "Medium (512x512)" -msgstr "" - -#: ../bin/src/ui_magnatunesettingspage.h:156 -msgid "Membership type" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsvorbis.h:206 -msgid "Minimum bitrate" -msgstr "" - -#: visualisations/projectmvisualisation.cpp:127 -msgid "Missing projectM presets" -msgstr "" - -#: devices/deviceproperties.cpp:152 -msgid "Model" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:163 -msgid "Monitor the library for changes" -msgstr "" - -#: smartplaylists/searchterm.cpp:303 -msgid "Months" -msgstr "" - -#: library/library.cpp:69 -msgid "Most played" -msgstr "" - -#: devices/giolister.cpp:157 -msgid "Mount point" -msgstr "" - -#: devices/devicekitlister.cpp:125 -msgid "Mount points" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:173 -#: ../bin/src/ui_queuemanager.h:131 ../bin/src/ui_songinfosettingspage.h:190 -msgid "Move down" -msgstr "" - -#: ui/mainwindow.cpp:521 widgets/fileviewlist.cpp:40 -msgid "Move to library..." -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:172 -#: ../bin/src/ui_queuemanager.h:127 ../bin/src/ui_songinfosettingspage.h:189 -msgid "Move up" -msgstr "" - -#: ui/mainwindow.cpp:149 -msgid "Music (*.mp3 *.ogg *.flac *.mpc *.m4a *.aac *.wma *.mp4 *.spx *.wav)" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:157 -msgid "Music Library" -msgstr "" - -#: core/globalshortcuts.cpp:54 wiimotedev/wiimotesettingspage.cpp:105 -#: ../bin/src/ui_mainwindow.h:692 -msgid "Mute" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:51 internet/lastfmservice.cpp:178 -msgid "My Last.fm Library" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:53 internet/lastfmservice.cpp:183 -msgid "My Last.fm Mix Radio" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:55 internet/lastfmservice.cpp:188 -msgid "My Last.fm Neighborhood" -msgstr "" - -#: globalsearch/lastfmsearchprovider.cpp:49 internet/lastfmservice.cpp:173 -msgid "My Last.fm Recommended Radio" -msgstr "" - -#: internet/lastfmservice.cpp:180 -msgid "My Mix Radio" -msgstr "" - -#: internet/lastfmservice.cpp:185 -msgid "My Neighborhood" -msgstr "" - -#: internet/lastfmservice.cpp:175 -msgid "My Radio Station" -msgstr "" - -#: internet/lastfmservice.cpp:170 -msgid "My Recommendations" -msgstr "" - -#: internet/groovesharkservice.cpp:1129 internet/groovesharkservice.cpp:1221 -#: ui/equalizer.cpp:172 ../bin/src/ui_deviceproperties.h:369 -#: ../bin/src/ui_magnatunedownloaddialog.h:135 -#: ../bin/src/ui_wizardfinishpage.h:84 -#: ../bin/src/ui_globalshortcutssettingspage.h:174 -msgid "Name" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:197 -msgid "Naming options" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:230 -msgid "Narrow band (NB)" -msgstr "" - -#: internet/lastfmservice.cpp:212 -msgid "Neighbors" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:184 -msgid "Network" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:157 -msgid "Network Proxy" -msgstr "" - -#: playlist/playlistdelegates.cpp:296 ui/edittagdialog.cpp:444 -msgid "Never" -msgstr "" - -#: library/library.cpp:62 -msgid "Never played" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:194 -#: ../bin/src/ui_behavioursettingspage.h:208 -msgid "Never start playing" -msgstr "" - -#: ui/mainwindow.cpp:1415 ../bin/src/ui_mainwindow.h:680 -msgid "New playlist" -msgstr "" - -#: library/libraryview.cpp:255 -msgid "New smart playlist..." -msgstr "" - -#: widgets/freespacebar.cpp:46 -msgid "New songs" -msgstr "" - -#: ../bin/src/ui_dynamicplaylistcontrols.h:110 -msgid "New tracks will be added automatically." -msgstr "" - -#: library/library.cpp:75 -msgid "Newest tracks" -msgstr "" - -#: ui/edittagdialog.cpp:158 ui/trackselectiondialog.cpp:48 -msgid "Next" -msgstr "" - -#: core/globalshortcuts.cpp:50 wiimotedev/wiimotesettingspage.cpp:99 -#: ../bin/src/ui_mainwindow.h:635 -msgid "Next track" -msgstr "" - -#: analyzers/analyzercontainer.cpp:80 -msgid "No analyzer" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:146 -msgid "No long blocks" -msgstr "" - -#: playlist/playlistcontainer.cpp:416 -msgid "" -"No matches found. Clear the search box to show the whole playlist again." -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:145 -msgid "No short blocks" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:128 ../bin/src/ui_groupbydialog.h:141 -#: ../bin/src/ui_groupbydialog.h:154 -msgid "None" -msgstr "" - -#: library/libraryview.cpp:455 ui/mainwindow.cpp:1798 ui/mainwindow.cpp:1903 -msgid "None of the selected songs were suitable for copying to a device" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:144 -msgid "Normal block type" -msgstr "" - -#: playlist/playlistsequence.cpp:170 -msgid "Not available while using a dynamic playlist" -msgstr "" - -#: devices/deviceview.cpp:106 -msgid "Not connected" -msgstr "" - -#: internet/lastfmservice.cpp:444 -msgid "Not enough content" -msgstr "" - -#: internet/lastfmservice.cpp:446 -msgid "Not enough fans" -msgstr "" - -#: internet/lastfmservice.cpp:445 -msgid "Not enough members" -msgstr "" - -#: internet/lastfmservice.cpp:447 -msgid "Not enough neighbors" -msgstr "" - -#: internet/spotifysettingspage.cpp:74 -msgid "Not installed" -msgstr "" - -#: globalsearch/globalsearchsettingspage.cpp:123 -msgid "Not logged in" -msgstr "" - -#: devices/deviceview.cpp:110 -msgid "Not mounted - double click to mount" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:397 -msgid "Notification type" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:356 -msgid "Notifications" -msgstr "" - -#: ui/macsystemtrayicon.mm:64 -msgid "Now Playing" -msgstr "" - -#: ui/notificationssettingspage.cpp:37 -msgid "OSD Preview" -msgstr "" - -#: core/song.cpp:390 -msgid "Ogg Flac" -msgstr "" - -#: core/song.cpp:391 -msgid "Ogg Speex" -msgstr "" - -#: core/song.cpp:392 ../bin/src/ui_magnatunedownloaddialog.h:139 -#: ../bin/src/ui_magnatunesettingspage.h:170 -msgid "Ogg Vorbis" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:142 -msgid "Only show the first" -msgstr "" - -#: internet/digitallyimportedservicebase.cpp:178 -#: internet/groovesharkservice.cpp:503 internet/icecastservice.cpp:299 -#: internet/jamendoservice.cpp:415 internet/magnatuneservice.cpp:273 -#: internet/somafmservice.cpp:84 -#, qt-format -msgid "Open %1 in browser" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:667 -msgid "Open &audio CD..." -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:382 -msgid "Open device" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:666 -msgid "Open file..." -msgstr "" - -#: devices/deviceview.cpp:220 internet/internetservice.cpp:73 -#: library/libraryview.cpp:247 widgets/fileviewlist.cpp:35 -#: ../bin/src/ui_behavioursettingspage.h:203 -msgid "Open in new playlist" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:169 -#: ../bin/src/ui_globalshortcutssettingspage.h:171 -msgid "Open..." -msgstr "" - -#: internet/lastfmservice.cpp:436 -msgid "Operation failed" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:193 -msgid "Optimize for bitrate" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:191 -msgid "Optimize for quality" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:210 -msgid "Options..." -msgstr "" - -#: ../bin/src/ui_organisedialog.h:188 -msgid "Organise Files" -msgstr "" - -#: library/libraryview.cpp:263 ui/mainwindow.cpp:522 -msgid "Organise files..." -msgstr "" - -#: core/organise.cpp:64 -msgid "Organising files" -msgstr "" - -#: ui/trackselectiondialog.cpp:165 -msgid "Original tags" -msgstr "" - -#: core/commandlineoptions.cpp:160 -msgid "Other options" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:283 -msgid "Output device" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:208 -msgid "Output options" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:277 -msgid "Output plugin" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:207 -msgid "Overwrite existing files" -msgstr "" - -#: internet/jamendoservice.cpp:210 -msgid "Parsing Jamendo catalogue" -msgstr "" - -#: ui/equalizer.cpp:118 -msgid "Party" -msgstr "" - -#: ../bin/src/ui_groovesharksettingspage.h:115 -#: ../bin/src/ui_magnatunesettingspage.h:165 -#: ../bin/src/ui_spotifysettingspage.h:211 -#: ../bin/src/ui_networkproxysettingspage.h:169 -msgid "Password" -msgstr "" - -#: devices/ilister.cpp:97 -msgid "Password Protected" -msgstr "" - -#: core/globalshortcuts.cpp:46 ui/mainwindow.cpp:871 ui/mainwindow.cpp:1257 -#: ui/qtsystemtrayicon.cpp:178 wiimotedev/wiimotesettingspage.cpp:106 -msgid "Pause" -msgstr "" - -#: core/commandlineoptions.cpp:147 -msgid "Pause playback" -msgstr "" - -#: widgets/osd.cpp:177 -msgid "Paused" -msgstr "" - -#: widgets/fancytabwidget.cpp:649 -msgid "Plain sidebar" -msgstr "" - -#: core/globalshortcuts.cpp:45 ui/mainwindow.cpp:505 ui/mainwindow.cpp:839 -#: ui/mainwindow.cpp:858 ui/mainwindow.cpp:1260 ui/qtsystemtrayicon.cpp:166 -#: ui/qtsystemtrayicon.cpp:192 wiimotedev/wiimotesettingspage.cpp:101 -#: ../bin/src/ui_mainwindow.h:631 -msgid "Play" -msgstr "" - -#: ../bin/src/ui_lastfmstationdialog.h:92 -msgid "Play Artist or Tag" -msgstr "" - -#: internet/lastfmservice.cpp:103 -msgid "Play artist radio..." -msgstr "" - -#: playlist/playlist.cpp:1119 ../bin/src/ui_edittagdialog.h:637 -msgid "Play count" -msgstr "" - -#: internet/lastfmservice.cpp:107 -msgid "Play custom radio..." -msgstr "" - -#: core/commandlineoptions.cpp:146 -msgid "Play if stopped, pause if playing" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:195 -#: ../bin/src/ui_behavioursettingspage.h:209 -msgid "Play if there is nothing already playing" -msgstr "" - -#: internet/lastfmservice.cpp:105 -msgid "Play tag radio..." -msgstr "" - -#: core/commandlineoptions.cpp:159 -msgid "Play the th track in the playlist" -msgstr "" - -#: core/globalshortcuts.cpp:47 wiimotedev/wiimotesettingspage.cpp:107 -msgid "Play/Pause" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:257 -msgid "Playback" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:208 -msgid "Player name" -msgstr "" - -#: core/commandlineoptions.cpp:144 -msgid "Player options" -msgstr "" - -#: playlist/playlistcontainer.cpp:297 playlist/playlistmanager.cpp:78 -#: playlist/playlistmanager.cpp:373 playlist/playlisttabbar.cpp:292 -msgid "Playlist" -msgstr "" - -#: widgets/osd.cpp:194 -msgid "Playlist finished" -msgstr "" - -#: core/commandlineoptions.cpp:156 -msgid "Playlist options" -msgstr "" - -#: playlist/playlistcontainer.cpp:93 playlist/specialplaylisttype.cpp:23 -msgid "Playlist search" -msgstr "" - -#: smartplaylists/wizard.cpp:71 -msgid "Playlist type" -msgstr "" - -#: internet/groovesharkservice.cpp:553 -msgid "Playlists" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:215 -msgid "Plugin status:" -msgstr "" - -#: ui/equalizer.cpp:119 -msgid "Pop" -msgstr "" - -#: internet/groovesharkservice.cpp:521 -msgid "Popular songs" -msgstr "" - -#: internet/groovesharkservice.cpp:525 -msgid "Popular songs of the Month" -msgstr "" - -#: internet/groovesharkservice.cpp:532 -msgid "Popular songs today" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:403 -msgid "Popup duration" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:166 -msgid "Port" -msgstr "" - -#: ui/equalizer.cpp:46 ../bin/src/ui_playbacksettingspage.h:274 -msgid "Pre-amp" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:166 -#: ../bin/src/ui_magnatunesettingspage.h:166 -#: ../bin/src/ui_spotifysettingspage.h:217 ../bin/src/ui_settingsdialog.h:115 -#: ../bin/src/ui_lastfmsettingspage.h:149 -msgid "Preferences" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:656 -msgid "Preferences..." -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:164 -msgid "Preferred album art filenames (comma separated)" -msgstr "" - -#: ../bin/src/ui_magnatunesettingspage.h:167 -msgid "Preferred audio format" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:218 -msgid "Preferred bitrate" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:380 -msgid "Preferred format" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:174 -msgid "Premium audio type" -msgstr "" - -#: ../bin/src/ui_equalizer.h:119 -msgid "Preset:" -msgstr "" - -#: ../bin/src/ui_wiimoteshortcutgrabber.h:124 -msgid "Press a button combination to use for" -msgstr "" - -#: ../bin/src/ui_globalshortcutgrabber.h:73 -msgid "Press a key" -msgstr "" - -#: ui/globalshortcutgrabber.cpp:39 ../bin/src/ui_globalshortcutgrabber.h:74 -#, qt-format -msgid "Press a key combination to use for %1..." -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:416 -msgid "Pretty OSD options" -msgstr "" - -#: ../bin/src/ui_searchpreview.h:105 ../bin/src/ui_songinfosettingspage.h:183 -#: ../bin/src/ui_notificationssettingspage.h:411 -#: ../bin/src/ui_organisedialog.h:208 -msgid "Preview" -msgstr "" - -#: ui/edittagdialog.cpp:157 ui/trackselectiondialog.cpp:47 -msgid "Previous" -msgstr "" - -#: core/globalshortcuts.cpp:51 wiimotedev/wiimotesettingspage.cpp:100 -#: ../bin/src/ui_mainwindow.h:629 -msgid "Previous track" -msgstr "" - -#: core/commandlineoptions.cpp:167 -msgid "Print out version information" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:131 -msgid "Profile" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:134 -#: ../bin/src/ui_transcodedialog.h:216 -msgid "Progress" -msgstr "" - -#: wiimotedev/wiimotesettingspage.cpp:227 -#: ../bin/src/ui_wiimoteshortcutgrabber.h:125 -msgid "Push Wiiremote button" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:138 -msgid "Put songs in a random order" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:107 -#: ../bin/src/ui_transcoderoptionsflac.h:81 -#: ../bin/src/ui_transcoderoptionsmp3.h:192 -#: ../bin/src/ui_transcoderoptionsspeex.h:217 -#: ../bin/src/ui_transcoderoptionsvorbis.h:202 -msgid "Quality" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:383 -msgid "Querying device..." -msgstr "" - -#: ../bin/src/ui_queuemanager.h:125 ../bin/src/ui_mainwindow.h:690 -msgid "Queue Manager" -msgstr "" - -#: ui/mainwindow.cpp:1338 -msgid "Queue selected tracks" -msgstr "" - -#: globalsearch/globalsearchwidget.cpp:105 library/libraryview.cpp:251 -#: ui/mainwindow.cpp:1336 -msgid "Queue track" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:271 -msgid "Radio (equal loudness for all tracks)" -msgstr "" - -#: internet/groovesharkservice.cpp:539 -msgid "Radios" -msgstr "" - -#: core/backgroundstreams.cpp:31 ../bin/src/ui_mainwindow.h:669 -msgid "Rain" -msgstr "" - -#: ../bin/src/ui_visualisationselector.h:112 -msgid "Random visualization" -msgstr "" - -#: core/globalshortcuts.cpp:64 -msgid "Rate the current song 0 stars" -msgstr "" - -#: core/globalshortcuts.cpp:65 -msgid "Rate the current song 1 star" -msgstr "" - -#: core/globalshortcuts.cpp:66 -msgid "Rate the current song 2 stars" -msgstr "" - -#: core/globalshortcuts.cpp:67 -msgid "Rate the current song 3 stars" -msgstr "" - -#: core/globalshortcuts.cpp:68 -msgid "Rate the current song 4 stars" -msgstr "" - -#: core/globalshortcuts.cpp:69 -msgid "Rate the current song 5 stars" -msgstr "" - -#: playlist/playlist.cpp:1118 ../bin/src/ui_edittagdialog.h:645 -msgid "Rating" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:279 ui/albumcovermanager.cpp:212 -msgid "Really cancel?" -msgstr "" - -#: internet/jamendoservice.cpp:416 internet/magnatuneservice.cpp:274 -msgid "Refresh catalogue" -msgstr "" - -#: internet/somafmservice.cpp:85 -msgid "Refresh channels" -msgstr "" - -#: internet/lastfmservice.cpp:109 -msgid "Refresh friends list" -msgstr "" - -#: internet/icecastservice.cpp:300 -msgid "Refresh station list" -msgstr "" - -#: internet/digitallyimportedservicebase.cpp:181 -msgid "Refresh streams" -msgstr "" - -#: ui/equalizer.cpp:120 -msgid "Reggae" -msgstr "" - -#: ../bin/src/ui_wiimoteshortcutgrabber.h:126 -msgid "Remember Wii remote swing" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:189 -msgid "Remember from last time" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:199 -msgid "Remote Control" -msgstr "" - -#: internet/savedradio.cpp:100 internet/lastfmservice.cpp:100 -#: ../bin/src/ui_queuemanager.h:135 ../bin/src/ui_searchtermwidget.h:282 -#: ../bin/src/ui_transcodedialog.h:207 -msgid "Remove" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:194 -msgid "Remove action" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:160 -msgid "Remove folder" -msgstr "" - -#: internet/groovesharkservice.cpp:498 -msgid "Remove from favorites" -msgstr "" - -#: internet/groovesharkservice.cpp:495 ../bin/src/ui_mainwindow.h:674 -msgid "Remove from playlist" -msgstr "" - -#: playlist/playlisttabbar.cpp:48 playlist/playlisttabbar.cpp:158 -msgid "Remove playlist" -msgstr "" - -#: internet/groovesharkservice.cpp:1311 -msgid "Removing song from favorites" -msgstr "" - -#: internet/groovesharkservice.cpp:1220 -#, qt-format -msgid "Rename \"%1\" playlist" -msgstr "" - -#: internet/groovesharkservice.cpp:491 -msgid "Rename Grooveshark playlist" -msgstr "" - -#: playlist/playlisttabbar.cpp:131 -msgid "Rename playlist" -msgstr "" - -#: playlist/playlisttabbar.cpp:49 -msgid "Rename playlist..." -msgstr "" - -#: ../bin/src/ui_mainwindow.h:652 -msgid "Renumber tracks in this order..." -msgstr "" - -#: playlist/playlistsequence.cpp:174 ../bin/src/ui_playlistsequence.h:112 -msgid "Repeat" -msgstr "" - -#: widgets/osd.cpp:309 ../bin/src/ui_playlistsequence.h:105 -msgid "Repeat album" -msgstr "" - -#: widgets/osd.cpp:310 ../bin/src/ui_playlistsequence.h:106 -msgid "Repeat playlist" -msgstr "" - -#: widgets/osd.cpp:308 ../bin/src/ui_playlistsequence.h:104 -msgid "Repeat track" -msgstr "" - -#: globalsearch/globalsearchwidget.cpp:107 -msgid "Replace and play now" -msgstr "" - -#: devices/deviceview.cpp:218 globalsearch/globalsearchwidget.cpp:106 -#: internet/internetservice.cpp:63 library/libraryview.cpp:245 -#: widgets/fileviewlist.cpp:33 -msgid "Replace current playlist" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:202 -msgid "Replace the playlist" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:205 -msgid "Replaces spaces with underscores" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:266 -msgid "Replay Gain" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:268 -msgid "Replay Gain mode" -msgstr "" - -#: ../bin/src/ui_dynamicplaylistcontrols.h:112 -msgid "Repopulate" -msgstr "" - -#: widgets/lineedit.cpp:51 -msgid "Reset" -msgstr "" - -#: ui/edittagdialog.cpp:716 ../bin/src/ui_edittagdialog.h:635 -msgid "Reset play counts" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:206 -msgid "Restrict to ASCII characters" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:175 -msgid "Results" -msgstr "" - -#: internet/groovesharkservice.cpp:677 -msgid "Retrieving Grooveshark favorites songs" -msgstr "" - -#: internet/groovesharkservice.cpp:600 -msgid "Retrieving Grooveshark playlists" -msgstr "" - -#: ui/equalizer.cpp:121 -msgid "Rock" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:164 -msgid "SOCKS proxy" -msgstr "" - -#: devices/deviceview.cpp:207 -msgid "Safely remove device" -msgstr "" - -#: ../bin/src/ui_organisedialog.h:196 -msgid "Safely remove the device after copying" -msgstr "" - -#: playlist/playlist.cpp:1126 ../bin/src/ui_edittagdialog.h:642 -msgid "Sample rate" -msgstr "" - -#: ui/organisedialog.cpp:65 -msgid "Samplerate" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:115 -msgid "Save album cover" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:59 -msgid "Save cover to disk..." -msgstr "" - -#: widgets/prettyimage.cpp:185 widgets/prettyimage.cpp:232 -msgid "Save image" -msgstr "" - -#: playlist/playlistcontainer.cpp:340 -msgid "Save playlist" -msgstr "" - -#: playlist/playlisttabbar.cpp:50 ../bin/src/ui_mainwindow.h:682 -msgid "Save playlist..." -msgstr "" - -#: ui/equalizer.cpp:172 ../bin/src/ui_equalizer.h:121 -msgid "Save preset" -msgstr "" - -#: ../bin/src/ui_addstreamdialog.h:115 -msgid "Save this stream in the Internet tab" -msgstr "" - -#: ui/edittagdialog.cpp:618 ui/trackselectiondialog.cpp:250 -msgid "Saving tracks" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:136 -msgid "Scalable sampling rate profile (SSR)" -msgstr "" - -#: playlist/playlist.cpp:1122 ../bin/src/ui_edittagdialog.h:643 -msgid "Score" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:150 -msgid "Scrobble tracks that I listen to" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:166 -#: ../bin/src/ui_albumcoversearcher.h:113 -msgid "Search" -msgstr "" - -#: internet/groovesharksearchplaylisttype.cpp:32 -#: internet/groovesharkservice.cpp:916 -msgid "Search Grooveshark" -msgstr "" - -#: internet/groovesharkservice.cpp:501 internet/groovesharkservice.cpp:515 -msgid "Search Grooveshark (opens a new tab)" -msgstr "" - -#: ../bin/src/ui_icecastfilterwidget.h:78 -msgid "Search Icecast stations" -msgstr "" - -#: internet/jamendoservice.cpp:422 -msgid "Search Jamendo" -msgstr "" - -#: internet/magnatuneservice.cpp:280 -msgid "Search Magnatune" -msgstr "" - -#: internet/spotifysearchplaylisttype.cpp:32 internet/spotifyservice.cpp:604 -msgid "Search Spotify" -msgstr "" - -#: internet/spotifyservice.cpp:338 -msgid "Search Spotify (opens a new tab)" -msgstr "" - -#: internet/spotifyservice.cpp:489 -msgid "Search Spotify (opens a new tab)..." -msgstr "" - -#: ../bin/src/ui_globalsearchwidget.h:61 -msgid "Search around all your sources (library, internet services, ...)" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:61 -msgid "Search for album covers..." -msgstr "" - -#: ../bin/src/ui_globalsearchwidget.h:63 -msgid "Search for anything" -msgstr "" - -#: ../bin/src/ui_querysearchpage.h:71 -msgid "Search mode" -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:146 -msgid "Search options" -msgstr "" - -#: smartplaylists/querywizardplugin.cpp:144 ../bin/src/ui_querysearchpage.h:78 -msgid "Search terms" -msgstr "" - -#: internet/groovesharkservice.cpp:282 -msgid "Searching on Grooveshark" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:138 -msgid "Second level" -msgstr "" - -#: core/globalshortcuts.cpp:56 wiimotedev/wiimotesettingspage.cpp:108 -msgid "Seek backward" -msgstr "" - -#: core/globalshortcuts.cpp:55 wiimotedev/wiimotesettingspage.cpp:109 -msgid "Seek forward" -msgstr "" - -#: core/commandlineoptions.cpp:155 -msgid "Seek the currently playing track by a relative amount" -msgstr "" - -#: core/commandlineoptions.cpp:154 -msgid "Seek the currently playing track to an absolute position" -msgstr "" - -#: visualisations/visualisationselector.cpp:40 -msgid "Select All" -msgstr "" - -#: visualisations/visualisationselector.cpp:42 -msgid "Select None" -msgstr "" - -#: ../bin/src/ui_trackselectiondialog.h:207 -msgid "Select best possible match" -msgstr "" - -#: ../bin/src/ui_visualisationselector.h:108 -msgid "Select visualizations" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:116 -msgid "Select visualizations..." -msgstr "" - -#: devices/devicekitlister.cpp:124 -msgid "Serial number" -msgstr "" - -#: internet/lastfmservice.cpp:439 -msgid "Service offline" -msgstr "" - -#: ui/mainwindow.cpp:1365 -#, qt-format -msgid "Set %1 to \"%2\"..." -msgstr "" - -#: core/commandlineoptions.cpp:151 -msgid "Set the volume to percent" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:653 -msgid "Set value for all selected tracks..." -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:207 -msgid "Settings" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:173 -msgid "Shortcut" -msgstr "" - -#: ui/globalshortcutssettingspage.cpp:133 -#: ../bin/src/ui_globalshortcutssettingspage.h:175 -#, qt-format -msgid "Shortcut for %1" -msgstr "" - -#: wiimotedev/wiimotesettingspage.cpp:124 -#, qt-format -msgid "Shortcut for %1 already exists" -msgstr "" - -#: library/libraryfilterwidget.cpp:62 -msgid "Show" -msgstr "" - -#: core/globalshortcuts.cpp:58 wiimotedev/wiimotesettingspage.cpp:111 -msgid "Show OSD" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:258 -msgid "Show a glowing animation on the current track" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:399 -msgid "Show a native desktop notification" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:407 -msgid "Show a notification when I change the repeat/shuffle mode" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:406 -msgid "Show a notification when I change the volume" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:401 -msgid "Show a popup from the system tray" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:400 -msgid "Show a pretty OSD" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:176 -msgid "Show a tooltip with more information about each result" -msgstr "" - -#: widgets/nowplayingwidget.cpp:113 -msgid "Show above status bar" -msgstr "" - -#: ui/mainwindow.cpp:478 -msgid "Show all songs" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:141 -msgid "Show all the songs" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:171 -msgid "Show cover art in library" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:172 -msgid "Show dividers" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:63 widgets/prettyimage.cpp:183 -msgid "Show fullsize..." -msgstr "" - -#: library/libraryview.cpp:275 ui/mainwindow.cpp:525 -msgid "Show in file browser..." -msgstr "" - -#: library/libraryview.cpp:279 -msgid "Show in various artists" -msgstr "" - -#: ui/mainwindow.cpp:479 -msgid "Show only duplicates" -msgstr "" - -#: ui/mainwindow.cpp:480 -msgid "Show only untagged" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:167 -msgid "Show the \"Search for anything\" box above the sidebar" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:151 -msgid "Show the \"love\" and \"ban\" buttons" -msgstr "" - -#: ../bin/src/ui_lastfmsettingspage.h:152 -msgid "Show the scrobble button in the main window" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:177 -msgid "Show tray icon" -msgstr "" - -#: core/globalshortcuts.cpp:57 -msgid "Show/Hide" -msgstr "" - -#: playlist/playlistsequence.cpp:173 ../bin/src/ui_playlistsequence.h:115 -msgid "Shuffle" -msgstr "" - -#: widgets/osd.cpp:297 ../bin/src/ui_playlistsequence.h:110 -msgid "Shuffle albums" -msgstr "" - -#: widgets/osd.cpp:295 ../bin/src/ui_playlistsequence.h:109 -msgid "Shuffle all" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:660 -msgid "Shuffle playlist" -msgstr "" - -#: widgets/osd.cpp:296 ../bin/src/ui_playlistsequence.h:108 -msgid "Shuffle tracks in this album" -msgstr "" - -#: ../bin/src/ui_remotesettingspage.h:202 ../bin/src/ui_loginstatewidget.h:173 -msgid "Sign out" -msgstr "" - -#: ../bin/src/ui_loginstatewidget.h:175 -msgid "Signing in..." -msgstr "" - -#: songinfo/echonestsimilarartists.cpp:57 -msgid "Similar artists" -msgstr "" - -#: ui/equalizer.cpp:123 -msgid "Ska" -msgstr "" - -#: core/commandlineoptions.cpp:149 -msgid "Skip backwards in playlist" -msgstr "" - -#: playlist/playlist.cpp:1120 ../bin/src/ui_edittagdialog.h:639 -msgid "Skip count" -msgstr "" - -#: core/commandlineoptions.cpp:150 -msgid "Skip forwards in playlist" -msgstr "" - -#: widgets/nowplayingwidget.cpp:90 -msgid "Small album cover" -msgstr "" - -#: widgets/fancytabwidget.cpp:648 -msgid "Small sidebar" -msgstr "" - -#: smartplaylists/wizard.cpp:67 -msgid "Smart playlist" -msgstr "" - -#: library/librarymodel.cpp:1128 -msgid "Smart playlists" -msgstr "" - -#: ui/equalizer.cpp:122 -msgid "Soft" -msgstr "" - -#: ui/equalizer.cpp:124 -msgid "Soft Rock" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:179 -msgid "Song Information" -msgstr "" - -#: ui/mainwindow.cpp:255 -msgid "Song info" -msgstr "" - -#: analyzers/sonogram.cpp:18 -msgid "Sonogram" -msgstr "" - -#: ../bin/src/ui_trackselectiondialog.h:205 -msgid "Sorry" -msgstr "" - -#: ../bin/src/ui_icecastfilterwidget.h:75 -msgid "Sort by genre (alphabetically)" -msgstr "" - -#: ../bin/src/ui_icecastfilterwidget.h:76 -msgid "Sort by genre (by popularity)" -msgstr "" - -#: ../bin/src/ui_icecastfilterwidget.h:77 -msgid "Sort by station name" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:139 -msgid "Sort songs by" -msgstr "" - -#: ../bin/src/ui_querysortpage.h:137 -msgid "Sorting" -msgstr "" - -#: ../bin/src/ui_globalsearchsettingspage.h:169 -msgid "Sources" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:162 -msgid "Speex" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:208 -msgid "Spotify" -msgstr "" - -#: internet/spotifyservice.cpp:179 -msgid "Spotify login error" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:213 -msgid "Spotify plugin" -msgstr "" - -#: internet/spotifyblobdownloader.cpp:58 -msgid "Spotify plugin not installed" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsmp3.h:201 -msgid "Standard" -msgstr "" - -#: internet/spotifyservice.cpp:343 -msgid "Starred" -msgstr "" - -#: core/commandlineoptions.cpp:145 -msgid "Start the playlist currently playing" -msgstr "" - -#: transcoder/transcodedialog.cpp:84 -msgid "Start transcoding" -msgstr "" - -#: internet/groovesharksearchplaylisttype.cpp:36 -#: internet/spotifysearchplaylisttype.cpp:36 -#, qt-format -msgid "Start typing in the search box above to find music on %1." -msgstr "" - -#: transcoder/transcoder.cpp:399 -#, qt-format -msgid "Starting %1" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:119 -msgid "Starting..." -msgstr "" - -#: internet/groovesharkservice.cpp:543 -msgid "Stations" -msgstr "" - -#: core/globalshortcuts.cpp:48 wiimotedev/wiimotesettingspage.cpp:102 -#: ../bin/src/ui_mainwindow.h:633 -msgid "Stop" -msgstr "" - -#: wiimotedev/wiimotesettingspage.cpp:110 -msgid "Stop after" -msgstr "" - -#: ui/mainwindow.cpp:507 ../bin/src/ui_mainwindow.h:639 -msgid "Stop after this track" -msgstr "" - -#: core/commandlineoptions.cpp:148 -msgid "Stop playback" -msgstr "" - -#: core/globalshortcuts.cpp:49 -msgid "Stop playing after current track" -msgstr "" - -#: widgets/osd.cpp:187 -msgid "Stopped" -msgstr "" - -#: core/song.cpp:398 -msgid "Stream" -msgstr "" - -#: ../bin/src/ui_magnatunesettingspage.h:160 -msgid "Streaming membership" -msgstr "" - -#: internet/groovesharkservice.cpp:566 -msgid "Subscribed playlists" -msgstr "" - -#: transcoder/transcoder.cpp:198 -#, qt-format -msgid "Successfully written %1" -msgstr "" - -#: ui/trackselectiondialog.cpp:169 -msgid "Suggested tags" -msgstr "" - -#: ../bin/src/ui_edittagdialog.h:651 -#: ../bin/src/ui_notificationssettingspage.h:413 -msgid "Summary" -msgstr "" - -#: analyzers/analyzercontainer.cpp:65 -#, qt-format -msgid "Super high (%1 fps)" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:103 -msgid "Super high (60 fps)" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:374 -msgid "Supported formats" -msgstr "" - -#: globalsearch/globalsearchtooltip.cpp:53 -msgid "Switch provider" -msgstr "" - -#: internet/spotifyservice.cpp:515 -msgid "Syncing Spotify inbox" -msgstr "" - -#: internet/spotifyservice.cpp:510 -msgid "Syncing Spotify playlist" -msgstr "" - -#: internet/spotifyservice.cpp:519 -msgid "Syncing Spotify starred tracks" -msgstr "" - -#: widgets/fancytabwidget.cpp:650 -msgid "Tabs on top" -msgstr "" - -#: ../bin/src/ui_lastfmstationdialog.h:97 -msgid "Tag" -msgstr "" - -#: ../bin/src/ui_trackselectiondialog.h:204 -msgid "Tag fetcher" -msgstr "" - -#: internet/lastfmservice.cpp:195 -msgid "Tag radio" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsvorbis.h:204 -msgid "Target bitrate" -msgstr "" - -#: ui/equalizer.cpp:125 -msgid "Techno" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:425 -msgid "Text options" -msgstr "" - -#: ui/about.cpp:69 -msgid "Thanks to" -msgstr "" - -#: ui/globalshortcutssettingspage.cpp:175 -#, qt-format -msgid "The \"%1\" command could not be started." -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:89 -#, qt-format -msgid "The directory %1 is not valid" -msgstr "" - -#: playlist/playlistmanager.cpp:151 playlist/playlistmanager.cpp:169 -#, qt-format -msgid "The playlist '%1' was empty or could not be loaded." -msgstr "" - -#: smartplaylists/searchtermwidget.cpp:322 -msgid "The second value must be greater than the first one!" -msgstr "" - -#: ui/coverfromurldialog.cpp:71 -msgid "The site you requested does not exist!" -msgstr "" - -#: ui/coverfromurldialog.cpp:82 -msgid "The site you requested is not an image!" -msgstr "" - -#: ui/mainwindow.cpp:2015 -msgid "" -"The version of Clementine you've just updated to requires a full library " -"rescan because of the new features listed below:" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:157 -msgid "There was a problem fetching the metadata from Magnatune" -msgstr "" - -#: ui/organiseerrordialog.cpp:56 -msgid "" -"There were problems copying some songs. The following files could not be " -"copied:" -msgstr "" - -#: ui/organiseerrordialog.cpp:61 -msgid "" -"There were problems deleting some songs. The following files could not be " -"deleted:" -msgstr "" - -#: library/libraryview.cpp:461 ui/mainwindow.cpp:1830 widgets/fileview.cpp:186 -msgid "" -"These files will be deleted from disk, are you sure you want to continue?" -msgstr "" - -#: devices/deviceview.cpp:394 -msgid "" -"These files will be deleted from the device, are you sure you want to " -"continue?" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:158 -msgid "These folders will be scanned for music to make up your library" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:158 -msgid "" -"These settings are used in the \"Transcode Music\" dialog, and when " -"converting music before copying it to a device." -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:151 -msgid "Third level" -msgstr "" - -#: internet/jamendoservice.cpp:167 -msgid "" -"This action will create a database which could be as big as 150 MB.\n" -"Do you want to continue anyway?" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:174 -msgid "This album is not available in the requested format" -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:381 -msgid "" -"This device must be connected and opened before Clementine can see what file" -" formats it supports." -msgstr "" - -#: ../bin/src/ui_deviceproperties.h:375 -msgid "This device supports the following file formats:" -msgstr "" - -#: devices/devicemanager.cpp:567 devices/devicemanager.cpp:575 -msgid "This device will not work properly" -msgstr "" - -#: devices/devicemanager.cpp:568 -msgid "" -"This is an MTP device, but you compiled Clementine without libmtp support." -msgstr "" - -#: devices/devicemanager.cpp:576 -msgid "This is an iPod, but you compiled Clementine without libgpod support." -msgstr "" - -#: devices/devicemanager.cpp:325 -msgid "" -"This is the first time you have connected this device. Clementine will now " -"scan the device to find music files - this may take some time." -msgstr "" - -#: playlist/playlisttabbar.cpp:159 -msgid "" -"This playlist will be removed; the action can't be undone. Are you sure you " -"want to continue?" -msgstr "" - -#: internet/lastfmservice.cpp:440 -msgid "This stream is for paid subscribers only" -msgstr "" - -#: devices/devicemanager.cpp:588 -#, qt-format -msgid "This type of device is not supported: %1" -msgstr "" - -#: ../bin/src/ui_songinfosettingspage.h:185 -msgid "Timeout" -msgstr "" - -#: devices/ilister.cpp:98 -msgid "Timezone" -msgstr "" - -#: playlist/playlist.cpp:1107 ui/organisedialog.cpp:51 -#: ui/qtsystemtrayicon.cpp:248 ../bin/src/ui_about.h:142 -#: ../bin/src/ui_edittagdialog.h:652 ../bin/src/ui_trackselectiondialog.h:211 -msgid "Title" -msgstr "" - -#: internet/groovesharkservice.cpp:939 -msgid "" -"To start Grooveshark radio, you should first listen to a few other " -"Grooveshark songs" -msgstr "" - -#: core/utilities.cpp:107 -msgid "Today" -msgstr "" - -#: core/globalshortcuts.cpp:59 -msgid "Toggle Pretty OSD" -msgstr "" - -#: visualisations/visualisationcontainer.cpp:94 -msgid "Toggle fullscreen" -msgstr "" - -#: ui/mainwindow.cpp:1340 -msgid "Toggle queue status" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:697 -msgid "Toggle scrobbling" -msgstr "" - -#: core/commandlineoptions.cpp:162 -msgid "Toggle visibility for the pretty on-screen-display" -msgstr "" - -#: covers/coversearchstatisticsdialog.cpp:71 -msgid "Total bytes transferred" -msgstr "" - -#: covers/coversearchstatisticsdialog.cpp:68 -msgid "Total network requests made" -msgstr "" - -#: playlist/playlist.cpp:1111 ui/organisedialog.cpp:57 -#: ../bin/src/ui_edittagdialog.h:653 ../bin/src/ui_trackselectiondialog.h:212 -msgid "Track" -msgstr "" - -#: ../bin/src/ui_transcodedialog.h:201 ../bin/src/ui_mainwindow.h:676 -msgid "Transcode Music" -msgstr "" - -#: ../bin/src/ui_transcodelogdialog.h:63 -msgid "Transcoder Log" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:157 -msgid "Transcoding" -msgstr "" - -#: transcoder/transcoder.cpp:307 -#, qt-format -msgid "Transcoding %1 files using %2 threads" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsdialog.h:54 -msgid "Transcoding options" -msgstr "" - -#: core/song.cpp:395 -msgid "TrueAudio" -msgstr "" - -#: analyzers/turbine.cpp:15 -msgid "Turbine" -msgstr "" - -#: ../bin/src/ui_dynamicplaylistcontrols.h:113 -msgid "Turn off" -msgstr "" - -#: devices/giolister.cpp:159 -msgid "URI" -msgstr "" - -#: core/commandlineoptions.cpp:144 -msgid "URL(s)" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:228 -msgid "Ultra wide band (UWB)" -msgstr "" - -#: internet/magnatunedownloaddialog.cpp:143 -#, qt-format -msgid "Unable to download %1 (%2)" -msgstr "" - -#: core/song.cpp:402 globalsearch/globalsearchitemdelegate.cpp:166 -#: globalsearch/globalsearchitemdelegate.cpp:173 library/librarymodel.cpp:297 -#: library/librarymodel.cpp:302 library/librarymodel.cpp:926 -#: playlist/playlistdelegates.cpp:306 playlist/playlistmanager.cpp:381 -#: playlist/playlistmanager.cpp:384 ui/albumcoverchoicecontroller.cpp:111 -msgid "Unknown" -msgstr "" - -#: internet/digitallyimportedclient.cpp:68 internet/lastfmservice.cpp:453 -msgid "Unknown error" -msgstr "" - -#: ui/albumcoverchoicecontroller.cpp:62 -msgid "Unset cover" -msgstr "" - -#: internet/groovesharkservice.cpp:1083 -msgid "Update Grooveshark playlist" -msgstr "" - -#: ../bin/src/ui_mainwindow.h:688 -msgid "Update changed library folders" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:162 -msgid "Update the library when Clementine starts" -msgstr "" - -#: library/librarywatcher.cpp:86 -#, qt-format -msgid "Updating %1" -msgstr "" - -#: devices/deviceview.cpp:102 -#, qt-format -msgid "Updating %1%..." -msgstr "" - -#: library/librarywatcher.cpp:84 -msgid "Updating library" -msgstr "" - -#: core/commandlineoptions.cpp:144 -msgid "Usage" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:168 -msgid "Use Gnome's shortcut keys" -msgstr "" - -#: ../bin/src/ui_playbacksettingspage.h:267 -msgid "Use Replay Gain metadata if it is available" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:189 -msgid "Use Wii Remote" -msgstr "" - -#: ../bin/src/ui_notificationssettingspage.h:410 -msgid "Use a custom message for notifications" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:167 -msgid "Use authentication" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsvorbis.h:203 -msgid "Use bitrate management engine" -msgstr "" - -#: ../bin/src/ui_wizardfinishpage.h:85 -msgid "Use dynamic mode" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:188 -msgid "Use notifications to report Wii Remote status" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsaac.h:139 -msgid "Use temporal noise shaping" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:183 -msgid "Use the system default" -msgstr "" - -#: ../bin/src/ui_networkproxysettingspage.h:158 -msgid "Use the system proxy settings" -msgstr "" - -#: ../bin/src/ui_spotifysettingspage.h:219 -msgid "Use volume normalisation" -msgstr "" - -#: widgets/freespacebar.cpp:47 -msgid "Used" -msgstr "" - -#: internet/groovesharkservice.cpp:402 -#, qt-format -msgid "User %1 doesn't have a Grooveshark Anywhere account" -msgstr "" - -#: ui/settingsdialog.cpp:128 -msgid "User interface" -msgstr "" - -#: ../bin/src/ui_groovesharksettingspage.h:114 -#: ../bin/src/ui_magnatunesettingspage.h:163 -#: ../bin/src/ui_spotifysettingspage.h:210 -#: ../bin/src/ui_networkproxysettingspage.h:168 -msgid "Username" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:191 -msgid "Using the menu to add a song will..." -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:142 -#: ../bin/src/ui_magnatunesettingspage.h:173 -msgid "VBR MP3" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:232 -msgid "Variable bit rate" -msgstr "" - -#: globalsearch/globalsearchitemdelegate.cpp:162 library/librarymodel.cpp:234 -#: playlist/playlistmanager.cpp:396 ui/albumcovermanager.cpp:264 -msgid "Various artists" -msgstr "" - -#: ui/about.cpp:34 -#, qt-format -msgid "Version %1" -msgstr "" - -#: ../bin/src/ui_albumcovermanager.h:154 -msgid "View" -msgstr "" - -#: ../bin/src/ui_visualisationselector.h:109 -msgid "Visualization mode" -msgstr "" - -#: ui/dbusscreensaver.cpp:35 ../bin/src/ui_mainwindow.h:689 -msgid "Visualizations" -msgstr "" - -#: ../bin/src/ui_visualisationoverlay.h:181 -msgid "Visualizations Settings" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:233 -msgid "Voice activity detection" -msgstr "" - -#: widgets/osd.cpp:201 -#, qt-format -msgid "Volume %1%" -msgstr "" - -#: devices/wmdmlister.cpp:369 -msgid "Volume name" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:160 -msgid "Vorbis" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:141 -#: ../bin/src/ui_magnatunesettingspage.h:172 -msgid "WAV" -msgstr "" - -#: ../bin/src/ui_transcodersettingspage.h:164 -msgid "WMA" -msgstr "" - -#: core/song.cpp:394 -msgid "Wav" -msgstr "" - -#: smartplaylists/searchterm.cpp:302 -msgid "Weeks" -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:186 -msgid "When Clementine starts" -msgstr "" - -#: ../bin/src/ui_librarysettingspage.h:166 -msgid "" -"When looking for album art Clementine will first look for picture files that contain one of these words.\n" -"If there are no matches then it will use the largest image in the directory." -msgstr "" - -#: devices/ilister.cpp:99 -msgid "WiFi MAC Address" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:229 -msgid "Wide band (WB)" -msgstr "" - -#: widgets/osd.cpp:260 -#, qt-format -msgid "Wii Remote %1: actived" -msgstr "" - -#: widgets/osd.cpp:270 -#, qt-format -msgid "Wii Remote %1: connected" -msgstr "" - -#: widgets/osd.cpp:285 -#, qt-format -msgid "Wii Remote %1: critical battery (%2%) " -msgstr "" - -#: widgets/osd.cpp:265 -#, qt-format -msgid "Wii Remote %1: disactived" -msgstr "" - -#: widgets/osd.cpp:275 -#, qt-format -msgid "Wii Remote %1: disconnected" -msgstr "" - -#: widgets/osd.cpp:280 -#, qt-format -msgid "Wii Remote %1: low battery (%2%)" -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:182 -msgid "Wiimotedev" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:181 -msgid "Windows Media 128k" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:172 -msgid "Windows Media 40k" -msgstr "" - -#: ../bin/src/ui_digitallyimportedsettingspage.h:180 -msgid "Windows Media 64k" -msgstr "" - -#: core/song.cpp:385 -msgid "Windows Media audio" -msgstr "" - -#: ui/mainwindow.cpp:2020 -msgid "Would you like to run a full rescan right now?" -msgstr "" - -#: playlist/playlist.cpp:1113 ui/organisedialog.cpp:60 -#: ../bin/src/ui_groupbydialog.h:135 ../bin/src/ui_groupbydialog.h:148 -#: ../bin/src/ui_groupbydialog.h:161 ../bin/src/ui_edittagdialog.h:657 -msgid "Year" -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:136 ../bin/src/ui_groupbydialog.h:149 -#: ../bin/src/ui_groupbydialog.h:162 -msgid "Year - Album" -msgstr "" - -#: smartplaylists/searchterm.cpp:304 -msgid "Years" -msgstr "" - -#: core/utilities.cpp:109 -msgid "Yesterday" -msgstr "" - -#: ../bin/src/ui_magnatunedownloaddialog.h:132 -msgid "You are about to download the following albums" -msgstr "" - -#: ../bin/src/ui_loginstatewidget.h:172 -msgid "You are not signed in." -msgstr "" - -#: widgets/loginstatewidget.cpp:75 -#, qt-format -msgid "You are signed in as %1." -msgstr "" - -#: widgets/loginstatewidget.cpp:73 -msgid "You are signed in." -msgstr "" - -#: ../bin/src/ui_groupbydialog.h:123 -msgid "You can change the way the songs in the library are organised." -msgstr "" - -#: internet/digitallyimportedsettingspage.cpp:46 -msgid "" -"You can listen for free without an account, but Premium members can listen " -"to higher quality streams without advertisements." -msgstr "" - -#: internet/magnatunesettingspage.cpp:53 -msgid "" -"You can listen to Magnatune songs for free without an account. Purchasing a" -" membership removes the messages at the end of each track." -msgstr "" - -#: ../bin/src/ui_backgroundstreamssettingspage.h:57 -msgid "You can listen to background streams at the same time as other music." -msgstr "" - -#: internet/lastfmsettingspage.cpp:140 -msgid "" -"You can scrobble tracks for free, but only paid subscribers can stream Last.fm radio from " -"Clementine." -msgstr "" - -#: ../bin/src/ui_wiimotesettingspage.h:184 -msgid "" -"You can use your Wii Remote as a remote control for Clementine. See the page on the " -"Clementine wiki for more information.\n" -msgstr "" - -#: internet/groovesharksettingspage.cpp:103 -msgid "You do not have a Grooveshark Anywhere account." -msgstr "" - -#: internet/spotifysettingspage.cpp:148 -msgid "You do not have a Spotify Premium account." -msgstr "" - -#: internet/spotifyservice.cpp:165 -msgid "" -"You have been logged out of Spotify, please re-enter your password in the " -"Settings dialog." -msgstr "" - -#: internet/spotifysettingspage.cpp:157 -msgid "You have been logged out of Spotify, please re-enter your password." -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:95 -msgid "You love this track" -msgstr "" - -#: ../bin/src/ui_globalshortcutssettingspage.h:170 -msgid "" -"You need to launch System Preferences and turn on \"Enable access for assistive devices\" to use global " -"shortcuts in Clementine." -msgstr "" - -#: ../bin/src/ui_behavioursettingspage.h:185 -msgid "You will need to restart Clementine if you change the language." -msgstr "" - -#: internet/lastfmsettingspage.cpp:107 -msgid "" -"You will not be able to play Last.fm radio stations as you are not a Last.fm" -" subscriber." -msgstr "" - -#: remote/remotesettingspage.cpp:126 -#, qt-format -msgid "You're logged in as %1" -msgstr "" - -#: remote/remotesettingspage.cpp:113 -msgid "Your Google credentials were incorrect" -msgstr "" - -#: internet/lastfmsettingspage.cpp:78 -msgid "Your Last.fm credentials were incorrect" -msgstr "" - -#: internet/magnatunesettingspage.cpp:113 -msgid "Your Magnatune credentials were incorrect" -msgstr "" - -#: ui/edittagdialog.cpp:732 ui/mainwindow.cpp:2121 -msgid "" -"Your gstreamer installation is missing the 'ofa' plugin. This is required " -"for automatic tag fetching. Try installing the 'gstreamer-plugins-bad' " -"package." -msgstr "" - -#: library/libraryview.cpp:219 -msgid "Your library is empty!" -msgstr "" - -#: globalsearch/savedradiosearchprovider.cpp:27 internet/savedradio.cpp:48 -msgid "Your radio streams" -msgstr "" - -#: songinfo/lastfmtrackinfoprovider.cpp:96 -#, qt-format -msgid "Your scrobbles: %1" -msgstr "" - -#: internet/groovesharksettingspage.cpp:107 -#: internet/spotifysettingspage.cpp:153 -msgid "Your username or password was incorrect." -msgstr "" - -#: smartplaylists/searchterm.cpp:288 -msgid "Z-A" -msgstr "" - -#: ui/equalizer.cpp:126 -msgid "Zero" -msgstr "" - -#: playlist/playlistundocommands.cpp:37 -#, c-format -msgid "add %n songs" -msgstr "" - -#: smartplaylists/searchterm.cpp:200 -msgid "after" -msgstr "" - -#: ../bin/src/ui_searchtermwidget.h:281 -msgid "ago" -msgstr "" - -#: ../bin/src/ui_searchtermwidget.h:280 -msgid "and" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:219 -msgid "automatic" -msgstr "" - -#: smartplaylists/searchterm.cpp:201 -msgid "before" -msgstr "" - -#: smartplaylists/searchterm.cpp:206 -msgid "between" -msgstr "" - -#: smartplaylists/searchterm.cpp:292 -msgid "biggest first" -msgstr "" - -#: playlist/playlistview.cpp:161 ui/edittagdialog.cpp:420 -msgid "bpm" -msgstr "" - -#: smartplaylists/searchterm.cpp:212 -msgid "contains" -msgstr "" - -#: ../bin/src/ui_transcoderoptionsspeex.h:222 -#: ../bin/src/ui_transcoderoptionsvorbis.h:207 -#: ../bin/src/ui_transcoderoptionsvorbis.h:210 -msgid "disabled" -msgstr "" - -#: widgets/osd.cpp:112 -#, qt-format -msgid "disc %1" -msgstr "" - -#: smartplaylists/searchterm.cpp:213 -msgid "does not contain" -msgstr "" - -#: globalsearch/globalsearchwidget.cpp:751 -msgid "e.g." -msgstr "" - -#: smartplaylists/searchterm.cpp:215 -msgid "ends with" -msgstr "" - -#: smartplaylists/searchterm.cpp:218 -msgid "equals" -msgstr "" - -#: smartplaylists/searchterm.cpp:216 -msgid "greater than" -msgstr "" - -#: smartplaylists/searchterm.cpp:204 -msgid "in the last" -msgstr "" - -#: playlist/playlistview.cpp:163 ui/edittagdialog.cpp:422 -#: internet/spotifysettingspage.cpp:59 internet/spotifysettingspage.cpp:60 -#: internet/spotifysettingspage.cpp:61 -msgid "kbps" -msgstr "" - -#: smartplaylists/searchterm.cpp:217 -msgid "less than" -msgstr "" - -#: smartplaylists/searchterm.cpp:290 -msgid "longest first" -msgstr "" - -#: playlist/playlistundocommands.cpp:99 -msgid "move songs" -msgstr "" - -#: smartplaylists/searchterm.cpp:289 -msgid "newest first" -msgstr "" - -#: smartplaylists/searchterm.cpp:219 -msgid "not equals" -msgstr "" - -#: smartplaylists/searchterm.cpp:205 -msgid "not in the last" -msgstr "" - -#: smartplaylists/searchterm.cpp:203 -msgid "not on" -msgstr "" - -#: smartplaylists/searchterm.cpp:289 -msgid "oldest first" -msgstr "" - -#: smartplaylists/searchterm.cpp:202 -msgid "on" -msgstr "" - -#: core/commandlineoptions.cpp:144 -msgid "options" -msgstr "" - -#: widgets/didyoumean.cpp:141 -msgid "press enter" -msgstr "" - -#: playlist/playlistundocommands.cpp:65 playlist/playlistundocommands.cpp:88 -#, c-format -msgid "remove %n songs" -msgstr "" - -#: smartplaylists/searchterm.cpp:290 -msgid "shortest first" -msgstr "" - -#: smartplaylists/searchterm.cpp:292 -msgid "smallest first" -msgstr "" - -#: smartplaylists/searchterm.cpp:214 -msgid "starts with" -msgstr "" - -#: playlist/playlistdelegates.cpp:177 -msgid "stop" -msgstr "" - -#: globalsearch/globalsearchitemdelegate.cpp:150 widgets/osd.cpp:114 -#, qt-format -msgid "track %1" -msgstr "" - - diff --git a/tagreader/common/CMakeLists.txt b/tagreader/common/CMakeLists.txt new file mode 100644 index 000000000..a6066af3c --- /dev/null +++ b/tagreader/common/CMakeLists.txt @@ -0,0 +1,41 @@ +include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/src) + +set(COMMON_SOURCES + messagehandler.cpp + + ${CMAKE_SOURCE_DIR}/src/core/encoding.cpp + ${CMAKE_SOURCE_DIR}/src/core/logging.cpp +) + +set(COMMON_HEADERS + messagehandler.h +) + +set(COMMON_MESSAGES + tagreadermessages.proto +) + +qt4_wrap_cpp(COMMON_MOC ${COMMON_HEADERS}) +protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${COMMON_MESSAGES}) + +add_library(clementine-tagreader-common STATIC + ${COMMON_SOURCES} + ${COMMON_MOC} + ${PROTO_SOURCES} +) + +# Use protobuf-lite if it's available +if(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + set(protobuf ${PROTOBUF_LITE_LIBRARY}) +else(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + set(protobuf ${PROTOBUF_LIBRARY}) +endif(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + +target_link_libraries(clementine-tagreader-common + ${protobuf} + ${CMAKE_THREAD_LIBS_INIT} + chardet +) + diff --git a/tagreader/common/messagehandler.cpp b/tagreader/common/messagehandler.cpp new file mode 100644 index 000000000..f2bb92c4b --- /dev/null +++ b/tagreader/common/messagehandler.cpp @@ -0,0 +1,107 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Note: this file is licensed under the Apache License instead of GPL because +// it is used by the Spotify blob which links against libspotify and is not GPL +// compatible. + + +#include "messagehandler.h" +#include "core/logging.h" + +#include +#include + +_MessageHandlerBase::_MessageHandlerBase(QIODevice* device, QObject* parent) + : QObject(parent), + device_(device), + flush_abstract_socket_(NULL), + flush_local_socket_(NULL), + reading_protobuf_(false), + expected_length_(0) { + buffer_.open(QIODevice::ReadWrite); + + connect(device, SIGNAL(readyRead()), SLOT(DeviceReadyRead())); + + // Yeah I know. + if (QAbstractSocket* socket = qobject_cast(device)) { + flush_abstract_socket_ = &QAbstractSocket::flush; + connect(socket, SIGNAL(disconnected()), SLOT(SocketClosed())); + } else if (QLocalSocket* socket = qobject_cast(device)) { + flush_local_socket_ = &QLocalSocket::flush; + connect(socket, SIGNAL(disconnected()), SLOT(SocketClosed())); + } else { + qFatal("Unsupported device type passed to _MessageHandlerBase"); + } +} + +void _MessageHandlerBase::DeviceReadyRead() { + while (device_->bytesAvailable()) { + if (!reading_protobuf_) { + // Read the length of the next message + QDataStream s(device_); + s >> expected_length_; + + reading_protobuf_ = true; + } + + // Read some of the message + buffer_.write(device_->read(expected_length_ - buffer_.size())); + + // Did we get everything? + if (buffer_.size() == expected_length_) { + // Parse the message + if (!MessageArrived(buffer_.data())) { + qLog(Error) << "Malformed protobuf message"; + device_->close(); + return; + } + + // Clear the buffer + buffer_.close(); + buffer_.setData(QByteArray()); + buffer_.open(QIODevice::ReadWrite); + reading_protobuf_ = false; + } + } +} + +void _MessageHandlerBase::WriteMessage(const QByteArray& data) { + QDataStream s(device_); + s << quint32(data.length()); + s.writeRawData(data.data(), data.length()); + + // Sorry. + if (flush_abstract_socket_) { + ((static_cast(device_))->*(flush_abstract_socket_))(); + } else if (flush_local_socket_) { + ((static_cast(device_))->*(flush_local_socket_))(); + } +} + +_MessageReplyBase::_MessageReplyBase(int id, QObject* parent) + : QObject(parent), + id_(id), + finished_(false) +{ +} + +void _MessageReplyBase::Abort() { + Q_ASSERT(!finished_); + finished_ = true; + + emit Finished(false); +} diff --git a/tagreader/common/messagehandler.h b/tagreader/common/messagehandler.h new file mode 100644 index 000000000..22bc6866c --- /dev/null +++ b/tagreader/common/messagehandler.h @@ -0,0 +1,258 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Note: this file is licensed under the Apache License instead of GPL because +// it is used by the Spotify blob which links against libspotify and is not GPL +// compatible. + + +#ifndef MESSAGEHANDLER_H +#define MESSAGEHANDLER_H + +#include +#include +#include +#include +#include +#include + +class QAbstractSocket; +class QIODevice; +class QLocalSocket; + +#define QStringFromStdString(x) \ + QString::fromUtf8(x.data(), x.size()) +#define DataCommaSizeFromQString(x) \ + x.toUtf8().constData(), x.toUtf8().length() + + +// Base QObject for a reply future class that is returned immediately for +// requests that will occur in the background. Similar to QNetworkReply. +// Use MessageReply instead. +class _MessageReplyBase : public QObject { + Q_OBJECT + +public: + _MessageReplyBase(int id, QObject* parent); + + int id() const { return id_; } + bool is_finished() const { return finished_; } + + void Abort(); + +signals: + void Finished(bool success); + +protected: + int id_; + bool finished_; +}; + + +// A reply future class that is returned immediately for requests that will +// occur in the background. Similar to QNetworkReply. +template +class MessageReply : public _MessageReplyBase { +public: + const MessageType& message() const { return message_; } + + void SetReply(const MessageType& message); + +private: + MessageType message_; +}; + + +// Reads and writes uint32 length encoded protobufs to a socket. +// This base QObject is separate from AbstractMessageHandler because moc can't +// handle templated classes. Use AbstractMessageHandler instead. +class _MessageHandlerBase : public QObject { + Q_OBJECT + +public: + _MessageHandlerBase(QIODevice* device, QObject* parent); + +protected slots: + void WriteMessage(const QByteArray& data); + void DeviceReadyRead(); + virtual bool SocketClosed() {} + +protected: + virtual bool MessageArrived(const QByteArray& data) = 0; + +protected: + typedef bool (QAbstractSocket::*FlushAbstractSocket)(); + typedef bool (QLocalSocket::*FlushLocalSocket)(); + + QIODevice* device_; + FlushAbstractSocket flush_abstract_socket_; + FlushLocalSocket flush_local_socket_; + + bool reading_protobuf_; + quint32 expected_length_; + QBuffer buffer_; +}; + + +// Reads and writes uint32 length encoded MessageType messages to a socket. +// You should subclass this and implement the MessageArrived(MessageType) +// method. +template +class AbstractMessageHandler : public _MessageHandlerBase { +public: + AbstractMessageHandler(QIODevice* device, QObject* parent); + + typedef MessageReply ReplyType; + + // Serialises the message and writes it to the socket. This version MUST be + // called from the thread in which the AbstractMessageHandler was created. + void SendMessage(const MessageType& message); + + // Serialises the message and writes it to the socket. This version may be + // called from any thread. + void SendMessageAsync(const MessageType& message); + + // Creates a new reply future for the request with the next sequential ID, + // and sets the request's ID to the ID of the reply. When a reply arrives + // for this request the reply is triggered automatically and MessageArrived + // is NOT called. Can be called from any thread. + ReplyType* NewReply(MessageType* message); + + // Same as NewReply, except the message is sent as well. Can be called from + // any thread. + ReplyType* SendMessageWithReply(MessageType* message); + + // Sets the "id" field of reply to the same as the request, and sends the + // reply on the socket. Used on the worker side. + void SendReply(const MessageType& request, MessageType* reply); + +protected: + // Called when a message is received from the socket. + virtual void MessageArrived(const MessageType& message) {} + + // _MessageHandlerBase + bool MessageArrived(const QByteArray& data); + +private: + QMutex mutex_; + int next_id_; + QMap pending_replies_; +}; + + +template +AbstractMessageHandler::AbstractMessageHandler( + QIODevice* device, QObject* parent) + : _MessageHandlerBase(device, parent), + next_id_(1) +{ +} + +template +void AbstractMessageHandler::SendMessage(const MessageType& message) { + Q_ASSERT(QThread::currentThread() == thread()); + + std::string data = message.SerializeAsString(); + WriteMessage(QByteArray(data.data(), data.size())); +} + +template +void AbstractMessageHandler::SendMessageAsync(const MessageType& message) { + std::string data = message.SerializeAsString(); + metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection, + Q_ARG(QByteArray, QByteArray(data.data(), data.size()))); +} + +template +void AbstractMessageHandler::SendReply(const MessageType& request, + MessageType* reply) { + reply->set_id(request.id()); + SendMessage(*reply); +} + +template +bool AbstractMessageHandler::MessageArrived(const QByteArray& data) { + MessageType message; + if (!message.ParseFromArray(data.constData(), data.size())) { + return false; + } + + ReplyType* reply = NULL; + { + QMutexLocker l(&mutex_); + reply = pending_replies_.take(message.id()); + } + + if (reply) { + // This is a reply to a message that we created earlier. + reply->SetReply(message); + } else { + MessageArrived(message); + } + + return true; +} + +template +typename AbstractMessageHandler::ReplyType* +AbstractMessageHandler::NewReply( + MessageType* message) { + ReplyType* reply = NULL; + + { + QMutexLocker l(&mutex_); + + const int id = next_id_ ++; + reply = new ReplyType(id, this); + pending_replies_[id] = reply; + } + + message->set_id(reply->id()); + return reply; +} + +template +typename AbstractMessageHandler::ReplyType* +AbstractMessageHandler::SendMessageWithReply( + MessageType* message) { + ReplyType* reply = NewReply(message); + + SendMessageAsync(*message); + + return reply; +} + +template +void AbstractMessageHandler::SocketClosed() { + QMutexLocker l(&mutex_); + + foreach (ReplyType* reply, pending_replies_) { + reply->Abort(); + } + pending_replies_.clear(); +} + +template +void MessageReply::SetReply(const MessageType& message) { + Q_ASSERT(!finished_); + + message_.MergeFrom(message); + finished_ = true; + + emit Finished(true); +} + +#endif // MESSAGEHANDLER_H diff --git a/tagreader/common/tagreadermessages.proto b/tagreader/common/tagreadermessages.proto new file mode 100644 index 000000000..505e0eba2 --- /dev/null +++ b/tagreader/common/tagreadermessages.proto @@ -0,0 +1,102 @@ +package pb.tagreader; + +option optimize_for = LITE_RUNTIME; + + +message SongMetadata { + enum Type { + UNKNOWN = 0; + ASF = 1; + FLAC = 2; + MP4 = 3; + MPC = 4; + MPEG = 5; + OGGFLAC = 6; + OGGSPEEX = 7; + OGGVORBIS = 8; + AIFF = 9; + WAV = 10; + TRUEAUDIO = 11; + CDDA = 12; + STREAM = 99; + } + + optional bool valid = 1; + optional string title = 2; + optional string album = 3; + optional string artist = 4; + optional string albumartist = 5; + optional string composer = 6; + optional int32 track = 7; + optional int32 disc = 8; + optional float bpm = 9; + optional int32 year = 10; + optional string genre = 11; + optional string comment = 12; + optional bool compilation = 13; + optional float rating = 14; + optional int32 playcount = 15; + optional int32 skipcount = 16; + optional int32 lastplayed = 17; + optional int32 score = 18; + optional uint64 length_nanosec = 19; + optional int32 bitrate = 20; + optional int32 samplerate = 21; + optional string url = 22; + optional string basefilename = 23; + optional int32 mtime = 24; + optional int32 ctime = 25; + optional int32 filesize = 26; + optional bool suspicious_tags = 27; + optional string art_automatic = 28; + optional Type type = 29; +} + +message ReadFileRequest { + optional string filename = 1; +} + +message ReadFileResponse { + optional SongMetadata metadata = 1; +} + +message SaveFileRequest { + optional string filename = 1; + optional SongMetadata metadata = 2; +} + +message SaveFileResponse { + optional bool success = 1; +} + +message IsMediaFileRequest { + optional string filename = 1; +} + +message IsMediaFileResponse { + optional bool success = 1; +} + +message LoadEmbeddedArtRequest { + optional string filename = 1; +} + +message LoadEmbeddedArtResponse { + optional bytes data = 1; +} + +message Message { + optional int32 id = 1; + + optional ReadFileRequest read_file_request = 2; + optional ReadFileResponse read_file_response = 3; + + optional SaveFileRequest save_file_request = 4; + optional SaveFileResponse save_file_response = 5; + + optional IsMediaFileRequest is_media_file_request = 6; + optional IsMediaFileResponse is_media_file_response = 7; + + optional LoadEmbeddedArtRequest load_embedded_art_request = 8; + optional LoadEmbeddedArtResponse load_embedded_art_response = 9; +} diff --git a/tagreader/tagreader/CMakeLists.txt b/tagreader/tagreader/CMakeLists.txt new file mode 100644 index 000000000..7bd153312 --- /dev/null +++ b/tagreader/tagreader/CMakeLists.txt @@ -0,0 +1,45 @@ +include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/../common) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common) +include_directories(${CMAKE_SOURCE_DIR}/src) + +link_directories(${SPOTIFY_LIBRARY_DIRS}) + +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +set(SOURCES + fmpsparser.cpp + main.cpp + tagreaderworker.cpp +) + +set(HEADERS +) + +qt4_wrap_cpp(MOC ${HEADERS}) + +add_executable(clementine-tagreader + ${SOURCES} + ${MOC} +) + +target_link_libraries(clementine-tagreader + ${TAGLIB_LIBRARIES} + ${QT_QTCORE_LIBRARY} + ${QT_QTNETWORK_LIBRARY} + clementine-tagreader-common +) + +if(APPLE) + target_link_libraries(clementine-tagreader + /System/Library/Frameworks/Foundation.framework + ) +endif(APPLE) + +if(NOT APPLE) + # macdeploy.py takes care of this on mac + install(TARGETS clementine-tagreader + RUNTIME DESTINATION bin + ) +endif(NOT APPLE) diff --git a/src/core/fmpsparser.cpp b/tagreader/tagreader/fmpsparser.cpp similarity index 100% rename from src/core/fmpsparser.cpp rename to tagreader/tagreader/fmpsparser.cpp diff --git a/src/core/fmpsparser.h b/tagreader/tagreader/fmpsparser.h similarity index 100% rename from src/core/fmpsparser.h rename to tagreader/tagreader/fmpsparser.h diff --git a/tagreader/tagreader/main.cpp b/tagreader/tagreader/main.cpp new file mode 100644 index 000000000..80b002e0f --- /dev/null +++ b/tagreader/tagreader/main.cpp @@ -0,0 +1,53 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine 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, either version 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "tagreaderworker.h" +#include "core/encoding.h" + +#include +#include +#include + +#include + +int main(int argc, char** argv) { + QCoreApplication a(argc, argv); + QStringList args(a.arguments()); + + if (args.count() != 2) { + std::cerr << "This program is used internally by Clementine to parse tags in music files\n" + "without exposing the whole application to crashes caused by malformed\n" + "files. It is not meant to be run on its own.\n"; + return 1; + } + + // Detect technically invalid usage of non-ASCII in ID3v1 tags. + UniversalEncodingHandler handler; + TagLib::ID3v1::Tag::setStringHandler(&handler); + + // Connect to the parent process. + QLocalSocket socket; + socket.connectToServer(args[1]); + if (!socket.waitForConnected(2000)) { + std::cerr << "Failed to connect to the parent process.\n"; + return 1; + } + + TagReaderWorker worker(&socket); + + return a.exec(); +} diff --git a/tagreader/tagreader/tagreaderworker.cpp b/tagreader/tagreader/tagreaderworker.cpp new file mode 100644 index 000000000..1cabf3694 --- /dev/null +++ b/tagreader/tagreader/tagreaderworker.cpp @@ -0,0 +1,541 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine 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, either version 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "fmpsparser.h" +#include "tagreaderworker.h" +#include "core/encoding.h" +#include "core/timeconstants.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Taglib added support for FLAC pictures in 1.7.0 +#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7) +# define TAGLIB_HAS_FLAC_PICTURELIST +#endif + + +using boost::scoped_ptr; + + +class FileRefFactory { + public: + virtual ~FileRefFactory() {} + virtual TagLib::FileRef* GetFileRef(const QString& filename) = 0; +}; + +class TagLibFileRefFactory : public FileRefFactory { + public: + virtual TagLib::FileRef* GetFileRef(const QString& filename) { + #ifdef Q_OS_WIN32 + return new TagLib::FileRef(filename.toStdWString().c_str()); + #else + return new TagLib::FileRef(QFile::encodeName(filename).constData()); + #endif + } +}; + +namespace { + +TagLib::String StdStringToTaglibString(const std::string& s) { + return TagLib::String(s.c_str(), TagLib::String::UTF8); +} + +TagLib::String QStringToTaglibString(const QString& s) { + return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8); +} + +} + + +TagReaderWorker::TagReaderWorker(QIODevice* socket, QObject* parent) + : AbstractMessageHandler(socket, parent), + kEmbeddedCover("(embedded)") +{ +} + +void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) { + pb::tagreader::Message reply; + + if (message.has_read_file_request()) { + ReadFile(QStringFromStdString(message.read_file_request().filename()), + reply.mutable_read_file_response()->mutable_metadata()); + } else if (message.has_save_file_request()) { + reply.mutable_save_file_response()->set_success( + SaveFile(QStringFromStdString(message.save_file_request().filename()), + message.save_file_request().metadata())); + } else if (message.has_is_media_file_request()) { + reply.mutable_is_media_file_response()->set_success( + IsMediaFile(QStringFromStdString(message.is_media_file_request().filename()))); + } else if (message.has_load_embedded_art_request()) { + QByteArray data = LoadEmbeddedArt( + QStringFromStdString(message.load_embedded_art_request().filename())); + reply.mutable_load_embedded_art_response()->set_data( + data.constData(), data.size()); + } + + SendReply(message, &reply); +} + +void TagReaderWorker::ReadFile(const QString& filename, + pb::tagreader::SongMetadata* song) const { + const QByteArray url(QUrl::fromLocalFile(filename).toEncoded()); + const QFileInfo info(filename); + + song->set_basefilename(DataCommaSizeFromQString(info.fileName())); + song->set_url(url.constData(), url.size()); + song->set_filesize(info.size()); + song->set_mtime(info.lastModified().toTime_t()); + song->set_ctime(info.created().toTime_t()); + + scoped_ptr fileref(factory_->GetFileRef(filename)); + if(fileref->isNull()) { + return; + } + + // This is single byte encoding, therefore can't be CJK. + UniversalEncodingHandler detector(NS_FILTER_NON_CJK); + + TagLib::Tag* tag = fileref->tag(); + QTextCodec* codec = NULL; + if (tag) { + TagLib::MPEG::File* file = dynamic_cast(fileref->file()); + if (file && (file->ID3v2Tag() || file->ID3v1Tag())) { + codec = detector.Guess(*fileref); + } + if (codec && + codec->name() != "UTF-8" && + codec->name() != "ISO-8859-1") { + // Mark tags where we detect an unusual codec as suspicious. + song->set_suspicious_tags(true); + } + + + Decode(tag->title(), NULL, song->mutable_title()); + Decode(tag->artist(), NULL, song->mutable_artist()); + Decode(tag->album(), NULL, song->mutable_album()); + Decode(tag->genre(), NULL, song->mutable_genre()); + song->set_year(tag->year()); + song->set_track(tag->track()); + song->set_valid(true); + } + + QString disc; + QString compilation; + if (TagLib::MPEG::File* file = dynamic_cast(fileref->file())) { + if (file->ID3v2Tag()) { + const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap(); + + if (!map["TPOS"].isEmpty()) + disc = TStringToQString(map["TPOS"].front()->toString()).trimmed(); + + if (!map["TBPM"].isEmpty()) + song->set_bpm(TStringToQString(map["TBPM"].front()->toString()).trimmed().toFloat()); + + if (!map["TCOM"].isEmpty()) + Decode(map["TCOM"].front()->toString(), NULL, song->mutable_composer()); + + if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft + Decode(map["TPE2"].front()->toString(), NULL, song->mutable_albumartist()); + + if (!map["TCMP"].isEmpty()) + compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed(); + + if (!map["APIC"].isEmpty()) + song->set_art_automatic(kEmbeddedCover); + + // Find a suitable comment tag. For now we ignore iTunNORM comments. + for (int i=0 ; i(map["COMM"][i]); + + if (frame && TStringToQString(frame->description()) != "iTunNORM") { + Decode(frame->text(), NULL, song->mutable_comment()); + break; + } + } + + // Parse FMPS frames + for (int i=0 ; i(map["TXXX"][i]); + + if (frame && frame->description().startsWith("FMPS_")) { + ParseFMPSFrame(TStringToQString(frame->description()), + TStringToQString(frame->fieldList()[1]), + song); + } + } + } + } else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast(fileref->file())) { + if (file->tag()) { + ParseOggTag(file->tag()->fieldListMap(), NULL, &disc, &compilation, song); + } + Decode(tag->comment(), NULL, song->mutable_comment()); + } else if (TagLib::FLAC::File* file = dynamic_cast(fileref->file())) { + if ( file->xiphComment() ) { + ParseOggTag(file->xiphComment()->fieldListMap(), NULL, &disc, &compilation, song); +#ifdef TAGLIB_HAS_FLAC_PICTURELIST + if (!file->pictureList().isEmpty()) { + song->set_art_automatic(kEmbeddedCover); + } +#endif + } + Decode(tag->comment(), NULL, song->mutable_comment()); + } else if (TagLib::MP4::File* file = dynamic_cast(fileref->file())) { + if (file->tag()) { + TagLib::MP4::Tag* mp4_tag = file->tag(); + const TagLib::MP4::ItemListMap& items = mp4_tag->itemListMap(); + TagLib::MP4::ItemListMap::ConstIterator it = items.find("aART"); + if (it != items.end()) { + TagLib::StringList album_artists = it->second.toStringList(); + if (!album_artists.isEmpty()) { + Decode(album_artists.front(), NULL, song->mutable_albumartist()); + } + } + } + } else if (tag) { + Decode(tag->comment(), NULL, song->mutable_comment()); + } + + if (!disc.isEmpty()) { + const int i = disc.indexOf('/'); + if (i != -1) { + // disc.right( i ).toInt() is total number of discs, we don't use this at the moment + song->set_disc(disc.left(i).toInt()); + } else { + song->set_disc(disc.toInt()); + } + } + + if (compilation.isEmpty()) { + // well, it wasn't set, but if the artist is VA assume it's a compilation + if (QStringFromStdString(song->artist()).toLower() == "various artists") { + song->set_compilation(true); + } + } else { + song->set_compilation(compilation.toInt() == 1); + } + + if (fileref->audioProperties()) { + song->set_bitrate(fileref->audioProperties()->bitrate()); + song->set_samplerate(fileref->audioProperties()->sampleRate()); + song->set_length_nanosec(fileref->audioProperties()->length() * kNsecPerSec); + } + + // Get the filetype if we can + song->set_type(GuessFileType(fileref.get())); + + // Set integer fields to -1 if they're not valid + #define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); } + SetDefault(track); + SetDefault(disc); + SetDefault(bpm); + SetDefault(year); + SetDefault(bitrate); + SetDefault(samplerate); + SetDefault(lastplayed); + SetDefault(rating); + #undef SetDefault +} + +void TagReaderWorker::Decode(const TagLib::String& tag, const QTextCodec* codec, + std::string* output) { + QString tmp; + + if (codec && tag.isLatin1()) { // Never override UTF-8. + const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString(); + tmp = codec->toUnicode(fixed.c_str()).trimmed(); + } else { + tmp = TStringToQString(tag).trimmed(); + } + + output->assign(DataCommaSizeFromQString(tmp)); +} + +void TagReaderWorker::Decode(const QString& tag, const QTextCodec* codec, + std::string* output) { + if (!codec) { + output->assign(DataCommaSizeFromQString(tag)); + } else { + const QString decoded(codec->toUnicode(tag.toUtf8())); + output->assign(DataCommaSizeFromQString(decoded)); + } +} + +void TagReaderWorker::ParseFMPSFrame(const QString& name, const QString& value, + pb::tagreader::SongMetadata* song) const { + FMPSParser parser; + if (!parser.Parse(value) || parser.is_empty()) + return; + + QVariant var; + if (name == "FMPS_Rating") { + var = parser.result()[0][0]; + if (var.type() == QVariant::Double) { + song->set_rating(var.toDouble()); + } + } else if (name == "FMPS_Rating_User") { + // Take a user rating only if there's no rating already set + if (song->rating() == -1 && parser.result()[0].count() >= 2) { + var = parser.result()[0][1]; + if (var.type() == QVariant::Double) { + song->set_rating(var.toDouble()); + } + } + } else if (name == "FMPS_PlayCount") { + var = parser.result()[0][0]; + if (var.type() == QVariant::Double) { + song->set_playcount(var.toDouble()); + } + } else if (name == "FMPS_PlayCount_User") { + // Take a user rating only if there's no playcount already set + if (song->rating() == -1 && parser.result()[0].count() >= 2) { + var = parser.result()[0][1]; + if (var.type() == QVariant::Double) { + song->set_playcount(var.toDouble()); + } + } + } +} + +void TagReaderWorker::ParseOggTag(const TagLib::Ogg::FieldListMap& map, + const QTextCodec* codec, + QString* disc, QString* compilation, + pb::tagreader::SongMetadata* song) const { + if (!map["COMPOSER"].isEmpty()) + Decode(map["COMPOSER"].front(), codec, song->mutable_composer()); + + if (!map["ALBUMARTIST"].isEmpty()) { + Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist()); + } else if (!map["ALBUM ARTIST"].isEmpty()) { + Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist()); + } + + if (!map["BPM"].isEmpty() ) + song->set_bpm(TStringToQString( map["BPM"].front() ).trimmed().toFloat()); + + if (!map["DISCNUMBER"].isEmpty() ) + *disc = TStringToQString( map["DISCNUMBER"].front() ).trimmed(); + + if (!map["COMPILATION"].isEmpty() ) + *compilation = TStringToQString( map["COMPILATION"].front() ).trimmed(); + + if (!map["COVERART"].isEmpty()) + song->set_art_automatic(kEmbeddedCover); +} + +pb::tagreader::SongMetadata_Type TagReaderWorker::GuessFileType( + TagLib::FileRef* fileref) const { +#ifdef TAGLIB_WITH_ASF + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_ASF; +#endif + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_FLAC; +#ifdef TAGLIB_WITH_MP4 + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_MP4; +#endif + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_MPC; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_MPEG; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_OGGFLAC; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_OGGSPEEX; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_OGGVORBIS; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_AIFF; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_WAV; + if (dynamic_cast(fileref->file())) + return pb::tagreader::SongMetadata_Type_TRUEAUDIO; + + return pb::tagreader::SongMetadata_Type_UNKNOWN; +} + +bool TagReaderWorker::SaveFile(const QString& filename, + const pb::tagreader::SongMetadata& song) const { + if (filename.isNull()) + return false; + + scoped_ptr fileref(factory_->GetFileRef(filename)); + + if (!fileref || fileref->isNull()) // The file probably doesn't exist + return false; + + fileref->tag()->setTitle(StdStringToTaglibString(song.title())); + fileref->tag()->setArtist(StdStringToTaglibString(song.artist())); + fileref->tag()->setAlbum(StdStringToTaglibString(song.album())); + fileref->tag()->setGenre(StdStringToTaglibString(song.genre())); + fileref->tag()->setComment(StdStringToTaglibString(song.comment())); + fileref->tag()->setYear(song.year()); + fileref->tag()->setTrack(song.track()); + + if (TagLib::MPEG::File* file = dynamic_cast(fileref->file())) { + TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true); + SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag); + SetTextFrame("TBPM", song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm()), tag); + SetTextFrame("TCOM", song.composer(), tag); + SetTextFrame("TPE2", song.albumartist(), tag); + SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag); + } + else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast(fileref->file())) { + TagLib::Ogg::XiphComment* tag = file->tag(); + tag->addField("COMPOSER", StdStringToTaglibString(song.composer()), true); + tag->addField("BPM", QStringToTaglibString(song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm())), true); + tag->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true); + tag->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true); + } + else if (TagLib::FLAC::File* file = dynamic_cast(fileref->file())) { + TagLib::Ogg::XiphComment* tag = file->xiphComment(); + tag->addField("COMPOSER", StdStringToTaglibString(song.composer()), true); + tag->addField("BPM", QStringToTaglibString(song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm())), true); + tag->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true); + tag->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true); + } + + bool ret = fileref->save(); + #ifdef Q_OS_LINUX + if (ret) { + // Linux: inotify doesn't seem to notice the change to the file unless we + // change the timestamps as well. (this is what touch does) + utimensat(0, QFile::encodeName(filename).constData(), NULL, 0); + } + #endif // Q_OS_LINUX + + return ret; +} + +void TagReaderWorker::SetTextFrame(const char* id, const QString& value, + TagLib::ID3v2::Tag* tag) const { + const QByteArray utf8(value.toUtf8()); + SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag); +} + +void TagReaderWorker::SetTextFrame(const char* id, const std::string& value, + TagLib::ID3v2::Tag* tag) const { + TagLib::ByteVector id_vector(id); + + // Remove the frame if it already exists + while (tag->frameListMap().contains(id_vector) && + tag->frameListMap()[id_vector].size() != 0) { + tag->removeFrame(tag->frameListMap()[id_vector].front()); + } + + // Create and add a new frame + TagLib::ID3v2::TextIdentificationFrame* frame = + new TagLib::ID3v2::TextIdentificationFrame(id_vector, + TagLib::String::UTF8); + frame->setText(StdStringToTaglibString(value)); + tag->addFrame(frame); +} + +bool TagReaderWorker::IsMediaFile(const QString& filename) const { + scoped_ptr fileref(factory_->GetFileRef(filename)); + return !fileref->isNull() && fileref->tag(); +} + +QByteArray TagReaderWorker::LoadEmbeddedArt(const QString& filename) const { + if (filename.isEmpty()) + return QByteArray(); + +#ifdef Q_OS_WIN32 + TagLib::FileRef ref(filename.toStdWString().c_str()); +#else + TagLib::FileRef ref(QFile::encodeName(filename).constData()); +#endif + + if (ref.isNull() || !ref.file()) + return QByteArray(); + + // MP3 + TagLib::MPEG::File* file = dynamic_cast(ref.file()); + if (file && file->ID3v2Tag()) { + TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"]; + if (apic_frames.isEmpty()) + return QByteArray(); + + TagLib::ID3v2::AttachedPictureFrame* pic = + static_cast(apic_frames.front()); + + return QByteArray((const char*) pic->picture().data(), pic->picture().size()); + } + + // Ogg vorbis/speex + TagLib::Ogg::XiphComment* xiph_comment = + dynamic_cast(ref.file()->tag()); + + if (xiph_comment) { + TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap(); + + // Ogg lacks a definitive standard for embedding cover art, but it seems + // b64 encoding a field called COVERART is the general convention + if (!map.contains("COVERART")) + return QByteArray(); + + return QByteArray::fromBase64(map["COVERART"].toString().toCString()); + } + +#ifdef TAGLIB_HAS_FLAC_PICTURELIST + // Flac + TagLib::FLAC::File* flac_file = dynamic_cast(ref.file()); + if (flac_file && flac_file->xiphComment()) { + TagLib::List pics = flac_file->pictureList(); + if (!pics.isEmpty()) { + // Use the first picture in the file - this could be made cleverer and + // pick the front cover if it's present. + + std::list::iterator it = pics.begin(); + TagLib::FLAC::Picture* picture = *it; + + return QByteArray(picture->data().data(), picture->data().size()); + } + } +#endif + + return QByteArray(); +} diff --git a/tagreader/tagreader/tagreaderworker.h b/tagreader/tagreader/tagreaderworker.h new file mode 100644 index 000000000..a5b94c355 --- /dev/null +++ b/tagreader/tagreader/tagreaderworker.h @@ -0,0 +1,75 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine 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, either version 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef TAGREADERWORKER_H +#define TAGREADERWORKER_H + +#include "messagehandler.h" +#include "tagreadermessages.pb.h" + +#include + + +namespace TagLib { + class FileRef; + class String; + + namespace ID3v2 { + class Tag; + } +} + +class FileRefFactory; + +class TagReaderWorker : public AbstractMessageHandler { +public: + TagReaderWorker(QIODevice* socket, QObject* parent = NULL); + +protected: + void MessageArrived(const pb::tagreader::Message& message); + +private: + void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const; + bool SaveFile(const QString& filename, const pb::tagreader::SongMetadata& song) const; + bool IsMediaFile(const QString& filename) const; + QByteArray LoadEmbeddedArt(const QString& filename) const; + + static void Decode(const TagLib::String& tag, const QTextCodec* codec, + std::string* output); + static void Decode(const QString& tag, const QTextCodec* codec, + std::string* output); + + void ParseFMPSFrame(const QString& name, const QString& value, + pb::tagreader::SongMetadata* song) const; + void ParseOggTag(const TagLib::Ogg::FieldListMap& map, + const QTextCodec* codec, + QString* disc, QString* compilation, + pb::tagreader::SongMetadata* song) const; + pb::tagreader::SongMetadata_Type GuessFileType(TagLib::FileRef* fileref) const; + + void SetTextFrame(const char* id, const QString& value, + TagLib::ID3v2::Tag* tag) const; + void SetTextFrame(const char* id, const std::string& value, + TagLib::ID3v2::Tag* tag) const; + +private: + FileRefFactory* factory_; + + const std::string kEmbeddedCover; +}; + +#endif // TAGREADERWORKER_H