From 99bccf6281f902236948c9aa899509453c818130 Mon Sep 17 00:00:00 2001
From: "craig.p.drummond"
Date: Tue, 22 Oct 2013 18:22:41 +0000
Subject: [PATCH] Read/write tags in an external app - as per Clementine and
Amarok. Isolates Cantata from TagLib crashes.
---
CMakeLists.txt | 32 ++--
ChangeLog | 2 +
INSTALL | 6 +
config.h.cmake | 1 +
context/songview.cpp | 4 +-
devices/actiondialog.cpp | 4 +-
devices/device.cpp | 10 +-
devices/extractjob.cpp | 4 +-
devices/fsdevice.cpp | 4 +-
gui/covers.cpp | 6 +-
gui/mainwindow.cpp | 14 +-
http/httpserver.cpp | 6 +-
http/httpsocket.cpp | 2 +-
mpd/mpdparseutils.cpp | 24 ---
mpd/mpdparseutils.h | 1 -
mpd/song.cpp | 32 +++-
mpd/song.h | 8 +
replaygain/rgdialog.cpp | 17 +-
replaygain/tagreader.cpp | 4 +-
support/utils.cpp | 25 +++
support/utils.h | 1 +
tags/CMakeLists.txt | 38 ++++
tags/main.cpp | 36 ++++
tags/tagclient.cpp | 365 +++++++++++++++++++++++++++++++++++++++
tags/tagclient.h | 91 ++++++++++
tags/tageditor.cpp | 12 +-
tags/tags.cpp | 48 ++---
tags/tags.h | 33 +++-
tags/tagserver.cpp | 93 ++++++++++
tags/tagserver.h | 48 +++++
30 files changed, 864 insertions(+), 107 deletions(-)
create mode 100644 tags/CMakeLists.txt
create mode 100644 tags/main.cpp
create mode 100644 tags/tagclient.cpp
create mode 100644 tags/tagclient.h
create mode 100644 tags/tagserver.cpp
create mode 100644 tags/tagserver.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 685a9f0e0..3d1368d23 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -41,6 +41,7 @@ option(ENABLE_CDPARANOIA "Enable CDParanoia libraries(required for AudioCD suppo
option(ENABLE_CDDB "Enable CDDB libraries(either this or MusicBrianz required for AudioCD support)" ON)
option(ENABLE_MUSICBRAINZ "Enable MusicBrianz libraries(either this or CDDB required for AudioCD support)" ON)
option(ENABLE_PROXY_CONFIG "Enable proxy config in settings dialog" OFF)
+option(ENABLE_EXTERNAL_TAGS "Enable usage of external app for reading/writing tags" ON)
if (ENABLE_QT5)
set(ENABLE_KDE FALSE)
@@ -126,7 +127,8 @@ if (ENABLE_QT5)
find_package(Qt5Network REQUIRED)
find_package(Qt5Concurrent REQUIRED)
set(QTCORELIBS ${Qt5Core_LIBRARIES})
- set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES})
+ set(QTNETWORKLIBS ${Qt5Network_LIBRARIES})
+ set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${QTNETWORKLIBS} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES})
set(QTINCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS})
set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
@@ -140,7 +142,8 @@ if (ENABLE_QT5)
else (ENABLE_QT5)
find_package(Qt4 REQUIRED QtCore QtGui QtXml QtNetwork)
set(QTCORELIBS ${QT_QTCORE_LIBRARY})
- set(QTLIBS ${QT_QTSVG_LIBRARY} ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QT_QTGUI_LIBRARY} ${QT_QTNETWORK_LIBRARY})
+ set(QTNETWORKLIBS ${QT_QTNETWORK_LIBRARY})
+ set(QTLIBS ${QT_QTSVG_LIBRARY} ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QT_QTGUI_LIBRARY} ${QTNETWORKLIBS})
if (QT_QTDBUS_FOUND)
set(QTLIBS ${QTLIBS} ${QT_QTDBUS_LIBRARY})
endif (QT_QTDBUS_FOUND)
@@ -307,10 +310,10 @@ set(CANTATA_RCS cantata.qrc)
if (TAGLIB_FOUND)
set(CANTATA_SRCS ${CANTATA_SRCS}
- tags/tageditor.cpp tags/trackorganiser.cpp tags/tags.cpp
+ tags/tageditor.cpp tags/trackorganiser.cpp tags/tagclient.cpp
devices/filenameschemedialog.cpp)
set(CANTATA_MOC_HDRS ${CANTATA_MOC_HDRS}
- tags/tageditor.h tags/trackorganiser.h
+ tags/tageditor.h tags/trackorganiser.h tags/tagclient.h
devices/filenameschemedialog.h devices/device.h)
set(CANTATA_UIS ${CANTATA_UIS}
tags/tageditor.ui tags/trackorganiser.ui
@@ -324,9 +327,14 @@ if (TAGLIB_FOUND)
set(ENABLE_REPLAYGAIN_SUPPORT 1)
add_subdirectory(replaygain)
endif (FFMPEG_FOUND OR MPG123_FOUND)
+ if (ENABLE_EXTERNAL_TAGS)
+ add_subdirectory(tags)
+ else (ENABLE_EXTERNAL_TAGS)
+ set(CANTATA_SRCS ${CANTATA_SRCS} tags/tags.cpp tags/filetyperesolver.cpp)
+ endif (ENABLE_EXTERNAL_TAGS)
if (WIN32 OR APPLE)
- set(CANTATA_SRCS ${CANTATA_SRCS} devices/device.cpp tags/filetyperesolver.cpp)
+ set(CANTATA_SRCS ${CANTATA_SRCS} devices/device.cpp)
else (WIN32 OR APPLE)
set(ENABLE_DEVICES_SUPPORT 1)
if (NOT ENABLE_KDE)
@@ -354,7 +362,7 @@ if (TAGLIB_FOUND)
set(CANTATA_SRCS ${CANTATA_SRCS} devices/devicespage.cpp devices/filejob.cpp
devices/device.cpp devices/fsdevice.cpp devices/umsdevice.cpp
- models/devicesmodel.cpp tags/filetyperesolver.cpp devices/actiondialog.cpp devices/devicepropertieswidget.cpp
+ models/devicesmodel.cpp devices/actiondialog.cpp devices/devicepropertieswidget.cpp
devices/devicepropertiesdialog.cpp devices/encoders.cpp devices/freespaceinfo.cpp
devices/transcodingjob.cpp devices/valueslider.cpp devices/syncdialog.cpp devices/synccollectionwidget.cpp
online/onlinedevice.cpp)
@@ -530,12 +538,16 @@ target_link_libraries(cantata support qtiocompressor ${QTLIBS} ${ZLIB_LIBRARIES}
include_directories(${QTINCLUDES} ${ZLIB_INCLUDE_DIRS})
if (TAGLIB_FOUND)
+ # Cantata still links to taglib, even if external tag reader/writer is used,
+ # because JamendoService uses taglib for ID3 genres.
target_link_libraries(cantata ${TAGLIB_LIBRARIES})
include_directories(${TAGLIB_INCLUDES})
- if (TAGLIB-EXTRAS_FOUND)
- target_link_libraries(cantata ${TAGLIB-EXTRAS_LIBRARIES})
- include_directories(${TAGLIB-EXTRAS_INCLUDES})
- endif (TAGLIB-EXTRAS_FOUND)
+ if (NOT ENABLE_EXTERNAL_TAGS)
+ if (TAGLIB-EXTRAS_FOUND)
+ target_link_libraries(cantata ${TAGLIB-EXTRAS_LIBRARIES})
+ include_directories(${TAGLIB-EXTRAS_INCLUDES})
+ endif (TAGLIB-EXTRAS_FOUND)
+ endif (NOT ENABLE_EXTERNAL_TAGS)
if (NOT ENABLE_KDE AND NOT WIN32 AND NOT APPLE)
target_link_libraries(cantata solidlite)
endif (NOT ENABLE_KDE AND NOT WIN32 AND NOT APPLE)
diff --git a/ChangeLog b/ChangeLog
index ea3ba19eb..3c01312cd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -81,6 +81,8 @@
46. When displaying tag editor, track orgainiser, or replay gain dialogs, check
that the song files can be accessed. (For speed reasons, only the 1st few
files are checked)
+47. Read/write tags in an external app - as per Clementine and Amarok. Isolates
+ Cantata from TagLib crashes.
1.1.3
-----
diff --git a/INSTALL b/INSTALL
index 2948b373e..1437a6407 100644
--- a/INSTALL
+++ b/INSTALL
@@ -76,6 +76,12 @@ The following options may be passed to CMake:
soundcloud, etc.
Default: ON (OFF for windows)
+ -DENABLE_EXTERNAL_TAGS=ON
+ Enable usage of external app for reading/writing tags. Helps to
+ isoloate Cantata from TagLib crashes.
+ Default: ON
+
+
Windows specific:
-DCANTATA_WINDOWS_INSTALLER_DEST=
diff --git a/config.h.cmake b/config.h.cmake
index b8771d396..8d28892b5 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -33,6 +33,7 @@
#cmakedefine ENABLE_REMOTE_DEVICES 1
#cmakedefine TAGLIB_CAN_SAVE_ID3VER 1
#cmakedefine ENABLE_PROXY_CONFIG 1
+#cmakedefine ENABLE_EXTERNAL_TAGS 1
#cmakedefine CDPARANOIA_HAS_CACHEMODEL_SIZE 1
#cmakedefine QT_QTDBUS_FOUND 1
diff --git a/context/songview.cpp b/context/songview.cpp
index 5c7d23433..d7675965b 100644
--- a/context/songview.cpp
+++ b/context/songview.cpp
@@ -31,7 +31,7 @@
#include "messagebox.h"
#include "localize.h"
#ifdef TAGLIB_FOUND
-#include "tags.h"
+#include "tagclient.h"
#endif
#include "icons.h"
#include "utils.h"
@@ -309,7 +309,7 @@ void SongView::update(const Song &s, bool force)
return;
} else {
#ifdef TAGLIB_FOUND
- QString tagLyrics=Tags::readLyrics(MPDConnection::self()->getDetails().dir+songFile);
+ QString tagLyrics=TagClient::self()->readLyrics(MPDConnection::self()->getDetails().dir+songFile);
if (!tagLyrics.isEmpty()) {
text->setText(fixNewLines(tagLyrics));
diff --git a/devices/actiondialog.cpp b/devices/actiondialog.cpp
index 6b3e9a161..a49d49071 100644
--- a/devices/actiondialog.cpp
+++ b/devices/actiondialog.cpp
@@ -41,7 +41,7 @@
#include "freespaceinfo.h"
#include "icons.h"
#include "config.h"
-#include "tags.h"
+#include "tagclient.h"
#include "treeview.h"
#include "onlineservicesmodel.h"
#ifdef ENABLE_REPLAYGAIN_SUPPORT
@@ -600,7 +600,7 @@ void ActionDialog::actionStatus(int status, bool copiedCover)
actionedTime+=currentSong.time;
#endif
#ifdef ENABLE_REPLAYGAIN_SUPPORT
- if (Copy==mode && sourceIsAudioCd && !albumsWithoutRgTags.contains(currentSong.album) && Tags::readReplaygain(destFile).isEmpty()) {
+ if (Copy==mode && sourceIsAudioCd && !albumsWithoutRgTags.contains(currentSong.album) && TagClient::self()->readReplaygain(destFile).isEmpty()) {
albumsWithoutRgTags.insert(currentSong.album);
}
#endif
diff --git a/devices/device.cpp b/devices/device.cpp
index a9c0d9b51..116abe8e5 100644
--- a/devices/device.cpp
+++ b/devices/device.cpp
@@ -39,7 +39,7 @@
#include "audiocddevice.h"
#endif // defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND
#include "encoders.h"
-#include "tags.h"
+#include "tagclient.h"
#include "song.h"
#include "mpdparseutils.h"
#include "musiclibraryitemartist.h"
@@ -198,7 +198,7 @@ bool Device::fixVariousArtists(const QString &file, Song &song, bool applyFix)
{
Song orig=song;
if (!file.isEmpty() && song.albumartist.isEmpty()) {
- song=Tags::read(file);
+ song=TagClient::self()->read(file);
}
if (song.artist.isEmpty() || song.albumartist.isEmpty() || !Song::isVariousArtists(song.albumartist)) {
@@ -214,7 +214,7 @@ bool Device::fixVariousArtists(const QString &file, Song &song, bool applyFix)
needsUpdating=song.fixVariousArtists();
}
- if (needsUpdating && (file.isEmpty() || Tags::Update_Modified==Tags::updateArtistAndTitle(file, song))) {
+ if (needsUpdating && (file.isEmpty() || Tags::Update_Modified==TagClient::self()->updateArtistAndTitle(file, song))) {
return true;
}
song=orig;
@@ -232,7 +232,7 @@ static QByteArray save(const QImage &img) {
void Device::embedCover(const QString &file, Song &song, unsigned int coverMaxSize)
{
- if (Tags::readImage(file).isNull()) {
+ if (TagClient::self()->readImage(file).isNull()) {
Covers::Image coverImage=Covers::self()->getImage(song);
if (!coverImage.img.isNull()) {
QByteArray imgData;
@@ -248,7 +248,7 @@ void Device::embedCover(const QString &file, Song &song, unsigned int coverMaxSi
imgData=save(coverImage.img);
}
}
- Tags::embedImage(file, imgData);
+ TagClient::self()->embedImage(file, imgData);
}
}
}
diff --git a/devices/extractjob.cpp b/devices/extractjob.cpp
index e99cfd22f..33837cc0f 100644
--- a/devices/extractjob.cpp
+++ b/devices/extractjob.cpp
@@ -23,7 +23,7 @@
#include "extractjob.h"
#include "device.h"
#include "utils.h"
-#include "tags.h"
+#include "tagclient.h"
#include "cdparanoia.h"
#include "covers.h"
#include "mpdconnection.h"
@@ -150,7 +150,7 @@ void ExtractJob::run()
process.closeWriteChannel();
process.waitForFinished();
Utils::setFilePerms(destFile);
- Tags::update(destFile, Song(), song, 3);
+ TagClient::self()->update(destFile, Song(), song, 3);
if (!stopRequested && !coverFile.isEmpty()) {
QString mpdCover=MPDConnection::self()->getDetails().coverName;
diff --git a/devices/fsdevice.cpp b/devices/fsdevice.cpp
index b3ab1843d..dff2d6744 100644
--- a/devices/fsdevice.cpp
+++ b/devices/fsdevice.cpp
@@ -22,7 +22,7 @@
*/
#include "umsdevice.h"
-#include "tags.h"
+#include "tagclient.h"
#include "musiclibrarymodel.h"
#include "musiclibraryitemsong.h"
#include "musiclibraryitemalbum.h"
@@ -162,7 +162,7 @@ void MusicScanner::scanFolder(MusicLibraryItemRoot *library, const QString &topL
song.file=fname;
QSet::iterator it=existing.find(song);
if (existing.end()==it) {
- song=Tags::read(info.absoluteFilePath());
+ song=TagClient::self()->read(info.absoluteFilePath());
song.file=fname;
} else {
song=*it;
diff --git a/gui/covers.cpp b/gui/covers.cpp
index a2c5e9d22..45230b5da 100644
--- a/gui/covers.cpp
+++ b/gui/covers.cpp
@@ -34,7 +34,7 @@
#include "podcastservice.h"
#include "onlineservicesmodel.h"
#ifdef TAGLIB_FOUND
-#include "tags.h"
+#include "tagclient.h"
#endif
#include
#include
@@ -907,7 +907,7 @@ Covers::Image Covers::locateImage(const Song &song)
#ifdef TAGLIB_FOUND
QImage img;
if (prevFileName.startsWith(constCoverInTagPrefix)) {
- img=Tags::readImage(prevFileName.mid(constCoverInTagPrefix.length()));
+ img=TagClient::self()->readImage(prevFileName.mid(constCoverInTagPrefix.length()));
} else {
img=QImage(prevFileName);
}
@@ -990,7 +990,7 @@ Covers::Image Covers::locateImage(const Song &song)
#ifdef TAGLIB_FOUND
QString fileName=haveAbsPath ? song.file : (MPDConnection::self()->getDetails().dir+songFile);
if (QFile::exists(fileName)) {
- QImage img(Tags::readImage(fileName));
+ QImage img(TagClient::self()->readImage(fileName));
if (!img.isNull()) {
DBUG_CLASS("Covers") << "Got cover image from tag" << fileName;
return Image(img, constCoverInTagPrefix+fileName);
diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp
index d7bcb400d..95c874a26 100644
--- a/gui/mainwindow.cpp
+++ b/gui/mainwindow.cpp
@@ -93,6 +93,7 @@
#ifdef TAGLIB_FOUND
#include "trackorganiser.h"
#include "tageditor.h"
+#include "tagclient.h"
#ifdef ENABLE_REPLAYGAIN_SUPPORT
#include "rgdialog.h"
#endif
@@ -878,6 +879,9 @@ MainWindow::~MainWindow()
#ifndef ENABLE_KDE_SUPPORT
MediaKeys::self()->stop();
#endif
+ #ifdef TAGLIB_FOUND
+ TagClient::self()->stop();
+ #endif
}
void MainWindow::initSizes()
@@ -1454,8 +1458,8 @@ void MainWindow::showServerInfo()
"| Uptime: | %4 |
"
"| Time playing: | %5 |
",
(version>>16)&0xFF, (version>>8)&0xFF, version&0xFF,
- MPDParseUtils::formatDuration(MPDStats::self()->uptime()),
- MPDParseUtils::formatDuration(MPDStats::self()->playtime()))+
+ Utils::formatDuration(MPDStats::self()->uptime()),
+ Utils::formatDuration(MPDStats::self()->playtime()))+
QLatin1String("
")+
i18n("| Database |
"
"| Artists: | %1 |
"
@@ -1465,7 +1469,7 @@ void MainWindow::showServerInfo()
"| Total duration: | %5 |
"
"| Last update: | %6 |
",
MPDStats::self()->artists(), MPDStats::self()->albums(), MPDStats::self()->songs(), handlers.join(", "),
- MPDParseUtils::formatDuration(MPDStats::self()->dbPlaytime()), MPDStats::self()->dbUpdate().toString(Qt::SystemLocaleShortDate)),
+ Utils::formatDuration(MPDStats::self()->dbPlaytime()), MPDStats::self()->dbUpdate().toString(Qt::SystemLocaleShortDate)),
i18n("Server Information"));
}
@@ -2179,9 +2183,9 @@ void MainWindow::updatePlayQueueStats(int songs, quint32 time)
}
#ifdef ENABLE_KDE_SUPPORT
- playQueueStatsLabel->setText(i18np("1 Track (%2)", "%1 Tracks (%2)", songs, MPDParseUtils::formatDuration(time)));
+ playQueueStatsLabel->setText(i18np("1 Track (%2)", "%1 Tracks (%2)", songs, Utils::formatDuration(time)));
#else
- playQueueStatsLabel->setText(QTP_TRACKS_DURATION_STR(songs, MPDParseUtils::formatDuration(time)));
+ playQueueStatsLabel->setText(QTP_TRACKS_DURATION_STR(songs, Utils::formatDuration(time)));
#endif
}
diff --git a/http/httpserver.cpp b/http/httpserver.cpp
index d36c2db64..6fc95654b 100644
--- a/http/httpserver.cpp
+++ b/http/httpserver.cpp
@@ -24,7 +24,7 @@
#include "httpserver.h"
#include "httpsocket.h"
#ifdef TAGLIB_FOUND
-#include "tags.h"
+#include "tagclient.h"
#endif
#include "settings.h"
#include "thread.h"
@@ -201,13 +201,13 @@ QByteArray HttpServer::encodeUrl(const QString &file) const
}
}
#ifdef TAGLIB_FOUND
- s=Tags::read(f);
+ s=TagClient::self()->read(f);
#endif
s.file=f;
#else
DBUG << "file" << file;
#ifdef TAGLIB_FOUND
- s=Tags::read(file);
+ s=TagClient::self()->read(file);
#endif
s.file=file;
#endif
diff --git a/http/httpsocket.cpp b/http/httpsocket.cpp
index 128c4e7c0..3bd388f05 100644
--- a/http/httpsocket.cpp
+++ b/http/httpsocket.cpp
@@ -64,7 +64,7 @@ static QString detectMimeType(const QString &file)
if (suffix == QLatin1String("mp3")) {
return QLatin1String("audio/mpeg");
}
- #ifdef TAGLIB_FOUND
+ #if defined TAGLIB_FOUND && !defined ENABLE_EXTERNAL_TAGS
if (suffix == QLatin1String("ogg")) {
#ifdef Q_OS_WIN32
const wchar_t *encodedName = reinterpret_cast< const wchar_t * >(file.utf16());
diff --git a/mpd/mpdparseutils.cpp b/mpd/mpdparseutils.cpp
index bfcf8b14f..7dd911659 100644
--- a/mpd/mpdparseutils.cpp
+++ b/mpd/mpdparseutils.cpp
@@ -636,30 +636,6 @@ QList