Fix copying album covers to iPod

Cover file was deleted too soon.
File needs to be accessible until itdb_write is called.

Fixes #663
This commit is contained in:
Jonas Kvinge 2021-03-20 19:00:42 +01:00
parent 66ed485803
commit c4a6d81cda
2 changed files with 68 additions and 39 deletions

View File

@ -20,6 +20,8 @@
#include "config.h"
#include <memory>
#include <glib.h>
#include <gpod/itdb.h>
@ -35,6 +37,7 @@
#include <QImage>
#include <QtDebug>
#include <QTemporaryFile>
#include <QStandardPaths>
#include "core/logging.h"
#include "core/application.h"
@ -130,7 +133,7 @@ void GPodDevice::LoadFinished(Itdb_iTunesDB *db, bool success) {
void GPodDevice::LoaderError(const QString &message) { app_->AddError(message); }
bool GPodDevice::StartCopy(QList<Song::FileType> *supported_filetypes) {
void GPodDevice::Start() {
{
// Wait for the database to be loaded
@ -141,7 +144,14 @@ bool GPodDevice::StartCopy(QList<Song::FileType> *supported_filetypes) {
// Ensure only one "organize files" can be active at any one time
db_busy_.lock();
}
bool GPodDevice::StartCopy(QList<Song::FileType> *supported_filetypes) {
Start();
if (supported_filetypes) GetSupportedFiletypes(supported_filetypes);
return true;
}
@ -178,29 +188,31 @@ bool GPodDevice::CopyToStorage(const CopyJob &job) {
Itdb_Track *track = AddTrackToITunesDb(job.metadata_);
std::shared_ptr<QTemporaryFile> cover_file;
if (job.albumcover_) {
bool result = false;
if (!job.metadata_.image().isNull()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// Workaround, see issue: https://github.com/strawberrymusicplayer/strawberry/issues/519
// result = itdb_track_set_thumbnails_from_data(track, job.metadata_.image().constBits(), job.metadata_.image().sizeInBytes());
QTemporaryFile cover_file;
if (cover_file.open()) {
QImage image = job.metadata_.image();
if (image.save(cover_file.fileName(), "JPG")) {
result = itdb_track_set_thumbnails(track, cover_file.fileName().toUtf8().constData());
if (result) track->has_artwork = 1;
}
cover_file.close();
cover_file.remove();
}
#ifdef Q_OS_LINUX
QString temp_path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/organize";
#else
result = itdb_track_set_thumbnails_from_data(track, job.metadata_.image().constBits(), job.metadata_.image().byteCount());
if (result) track->has_artwork = 1;
QString temp_path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
#endif
if (!QDir(temp_path).exists()) QDir().mkpath(temp_path);
cover_file = std::make_shared<QTemporaryFile>(temp_path + "/track-albumcover-XXXXXX.jpg");
cover_file->setAutoRemove(true);
if (cover_file->open()) {
QImage image = job.metadata_.image();
if (image.save(cover_file->fileName(), "JPG")) {
result = itdb_track_set_thumbnails(track, QFile::encodeName(cover_file->fileName()));
if (result) {
cover_files_ << cover_file;
track->has_artwork = 1;
}
}
}
}
else if (!job.cover_source_.isEmpty()) {
result = itdb_track_set_thumbnails(track, job.cover_source_.toUtf8().constData());
result = itdb_track_set_thumbnails(track, QFile::encodeName(job.cover_source_));
if (result) track->has_artwork = 1;
}
else {
@ -215,7 +227,7 @@ bool GPodDevice::CopyToStorage(const CopyJob &job) {
GError *error = nullptr;
itdb_cp_track_to_ipod(track, QDir::toNativeSeparators(job.source_).toLocal8Bit().constData(), &error);
if (error) {
qLog(Error) << "copying failed:" << error->message;
qLog(Error) << "Copying failed:" << error->message;
app_->AddError(QString::fromUtf8(error->message));
g_error_free(error);
@ -248,24 +260,28 @@ bool GPodDevice::CopyToStorage(const CopyJob &job) {
}
void GPodDevice::WriteDatabase(bool success) {
bool GPodDevice::WriteDatabase() {
// Write the itunes database
GError *error = nullptr;
itdb_write(db_, &error);
cover_files_.clear();
if (error) {
qLog(Error) << "Writing database failed:" << error->message;
app_->AddError(QString::fromUtf8(error->message));
g_error_free(error);
return false;
}
else return true;
}
void GPodDevice::Finish(const bool success) {
// Update the collection model
if (success) {
// Write the itunes database
GError *error = nullptr;
itdb_write(db_, &error);
if (error) {
qLog(Error) << "writing database failed:" << error->message;
app_->AddError(QString::fromUtf8(error->message));
g_error_free(error);
}
else {
FinaliseDatabase();
// Update the collection model
if (!songs_to_add_.isEmpty()) backend_->AddOrUpdateSongs(songs_to_add_);
if (!songs_to_remove_.isEmpty()) backend_->DeleteSongs(songs_to_remove_);
}
if (!songs_to_add_.isEmpty()) backend_->AddOrUpdateSongs(songs_to_add_);
if (!songs_to_remove_.isEmpty()) backend_->DeleteSongs(songs_to_remove_);
}
// This is done in the organize thread so close the unique DB connection.
@ -273,16 +289,21 @@ void GPodDevice::WriteDatabase(bool success) {
songs_to_add_.clear();
songs_to_remove_.clear();
cover_files_.clear();
db_busy_.unlock();
}
void GPodDevice::FinishCopy(bool success) {
WriteDatabase(success);
if (success) success = WriteDatabase();
Finish(success);
ConnectedDevice::FinishCopy(success);
}
void GPodDevice::StartDelete() { StartCopy(nullptr); }
void GPodDevice::StartDelete() { Start(); }
bool GPodDevice::RemoveTrackFromITunesDb(const QString &path, const QString &relative_to) {
@ -342,8 +363,11 @@ bool GPodDevice::DeleteFromStorage(const DeleteJob &job) {
}
void GPodDevice::FinishDelete(bool success) {
WriteDatabase(success);
if (success) success = WriteDatabase();
Finish(success);
ConnectedDevice::FinishDelete(success);
}
bool GPodDevice::GetSupportedFiletypes(QList<Song::FileType> *ret) {

View File

@ -23,6 +23,8 @@
#include "config.h"
#include <memory>
#include <gpod/itdb.h>
#include <QObject>
@ -32,6 +34,7 @@
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QTemporaryFile>
#include "core/song.h"
#include "core/musicstorage.h"
@ -76,10 +79,11 @@ class GPodDevice : public ConnectedDevice, public virtual MusicStorage {
Itdb_Track *AddTrackToITunesDb(const Song &metadata);
void AddTrackToModel(Itdb_Track *track, const QString &prefix);
bool RemoveTrackFromITunesDb(const QString &path, const QString &relative_to = QString());
virtual void FinaliseDatabase() {}
private:
void WriteDatabase(bool success);
void Start();
void Finish(const bool success);
bool WriteDatabase();
protected:
GPodLoader *loader_;
@ -93,6 +97,7 @@ class GPodDevice : public ConnectedDevice, public virtual MusicStorage {
QMutex db_busy_;
SongList songs_to_add_;
SongList songs_to_remove_;
QList<std::shared_ptr<QTemporaryFile>> cover_files_;
};
#endif // GPODDEVICE_H