diff --git a/core/audex.cpp b/core/audex.cpp index 68bc0de..1227b11 100644 --- a/core/audex.cpp +++ b/core/audex.cpp @@ -1,1060 +1,1060 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "audex.h" /* The heart of audex */ Audex::Audex(QWidget *parent, ProfileModel *profile_model, CDDAModel *cdda_model) : QObject(parent) { Q_UNUSED(parent); this->profile_model = profile_model; this->cdda_model = cdda_model; p_profile_name = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_NAME_INDEX)).toString(); p_suffix = profile_model->getSelectedEncoderSuffixFromCurrentIndex(); p_single_file = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SF_INDEX)).toBool(); encoder_wrapper = new EncoderWrapper(this, profile_model->getSelectedEncoderPatternFromCurrentIndex(), profile_model->getSelectedEncoderNameAndVersion(), Preferences::deletePartialFiles()); if (!encoder_wrapper) { qDebug() << "PANIC ERROR. Could not load object EncoderWrapper. Low mem?"; return; } cdda_extract_thread = new CDDAExtractThread(this, cdda_model->paranoia()); if (!cdda_extract_thread) { qDebug() << "PANIC ERROR. Could not load object CDDAExtractThread. Low mem?"; return; } cdda_extract_thread->setSampleOffset(Preferences::sampleOffset()); jobs = new AudexJobs(); connect(jobs, SIGNAL(newJobAvailable()), this, SLOT(start_encode())); wave_file_writer = new WaveFileWriter(); last_measuring_point_sector = -1; timer_extract = new QTimer(this); connect(timer_extract, SIGNAL(timeout()), this, SLOT(calculate_speed_extract())); timer_extract->start(4000); last_measuring_point_encoder_percent = -1; timer_encode = new QTimer(this); connect(timer_encode, SIGNAL(timeout()), this, SLOT(calculate_speed_encode())); timer_encode->start(2000); connect(encoder_wrapper, SIGNAL(progress(int)), this, SLOT(progress_encode(int))); connect(encoder_wrapper, SIGNAL(finished()), this, SLOT(finish_encode())); connect(encoder_wrapper, SIGNAL(info(const QString &)), this, SLOT(slot_info(const QString &))); connect(encoder_wrapper, SIGNAL(warning(const QString &)), this, SLOT(slot_warning(const QString &))); connect(encoder_wrapper, SIGNAL(error(const QString &, const QString &)), this, SLOT(slot_error(const QString &, const QString &))); connect(cdda_extract_thread, SIGNAL(progress(int, int, int)), this, SLOT(progress_extract(int, int, int))); connect(cdda_extract_thread, SIGNAL(output(const QByteArray &)), this, SLOT(write_to_wave(const QByteArray &))); connect(cdda_extract_thread, SIGNAL(finished()), this, SLOT(finish_extract())); connect(cdda_extract_thread, SIGNAL(terminated()), this, SLOT(finish_extract())); connect(cdda_extract_thread, SIGNAL(info(const QString &)), this, SLOT(slot_info(const QString &))); connect(cdda_extract_thread, SIGNAL(warning(const QString &)), this, SLOT(slot_warning(const QString &))); connect(cdda_extract_thread, SIGNAL(error(const QString &, const QString &)), this, SLOT(slot_error(const QString &, const QString &))); process_counter = 0; timeout_done = false; timeout_counter = 0; _finished = false; _finished_successful = false; en_track_index = 0; en_track_count = 0; ex_track_index = 0; ex_track_count = 0; current_sector = 0; current_encoder_percent = 0; overall_frames = 0; } Audex::~Audex() { delete encoder_wrapper; delete cdda_extract_thread; delete wave_file_writer; delete jobs; delete tmp_dir; } bool Audex::prepare() { if (profile_model->currentProfileIndex() < 0) { slot_error(i18n("No profile selected. Operation abort.")); return false; } qDebug() << "Using profile with index" << profile_model->currentProfileIndex(); tmp_dir = new TmpDir("audex", "work"); tmp_path = tmp_dir->tmpPath(); if (tmp_dir->error()) return false; return true; } void Audex::start() { emit changedEncodeTrack(0, 0, ""); emit info(i18n("Start ripping and encoding with profile \"%1\"...", p_profile_name)); if (check()) start_extract(); else request_finish(false); } void Audex::cancel() { request_finish(false); } const QStringList &Audex::extractProtocol() { return cdda_extract_thread->protocol(); } const QStringList &Audex::encoderProtocol() { return encoder_wrapper->protocol(); } void Audex::start_extract() { if (_finished) return; if (p_single_file) { if (ex_track_count >= 1) { if (!jobs->jobInProgress() && !jobs->pendingJobs()) request_finish(true); return; } ex_track_index++; QString artist = cdda_model->artist(); QString title = cdda_model->title(); QString year = cdda_model->year(); QString genre = cdda_model->genre(); QString suffix = p_suffix; QString basepath = Preferences::basePath(); int cdnum; if (!cdda_model->isMultiCD()) cdnum = 0; else cdnum = cdda_model->cdNum(); int nooftracks = cdda_model->numOfAudioTracks(); bool overwrite = Preferences::overwriteExistingFiles(); QString targetFilename; if (!construct_target_filename_for_singlefile(targetFilename, cdnum, nooftracks, artist, title, year, genre, suffix, basepath, overwrite)) { request_finish(false); return; } ex_track_target_filename = targetFilename; cdda_model->setCustomData("filename", targetFilename); // if empty (maybe because it already exists) skip if (!targetFilename.isEmpty()) { emit changedExtractTrack(ex_track_index, 1, artist, title); QString sourceFilename = tmp_path + QString("%1").arg(DiscIDCalculator::FreeDBId(cdda_model->discSignature())) + ".wav"; ex_track_source_filename = sourceFilename; wave_file_writer->open(sourceFilename); if (Preferences::paranoiaMode()) { cdda_extract_thread->setParanoiaMode(3); } else { cdda_extract_thread->setParanoiaMode(0); } cdda_extract_thread->setNeverSkip(Preferences::neverSkip()); cdda_extract_thread->setTrackToRip(0); cdda_extract_thread->start(); process_counter++; ex_track_count++; } else { if (!jobs->jobInProgress() && !jobs->pendingJobs()) request_finish(true); } } else { if (ex_track_count >= cdda_model->numOfAudioTracksInSelection()) { if (!jobs->jobInProgress() && !jobs->pendingJobs()) request_finish(true); return; } ex_track_index++; bool skip = !cdda_model->isTrackInSelection(ex_track_index); if (!cdda_model->isAudioTrack(ex_track_index)) skip = true; if (!skip) { QString artist = cdda_model->artist(); QString title = cdda_model->title(); QString tartist = cdda_model->data(cdda_model->index(ex_track_index - 1, CDDA_MODEL_COLUMN_ARTIST_INDEX)).toString(); QString ttitle = cdda_model->data(cdda_model->index(ex_track_index - 1, CDDA_MODEL_COLUMN_TITLE_INDEX)).toString(); QString year = cdda_model->year(); QString genre = cdda_model->genre(); QString suffix = p_suffix; QString basepath = Preferences::basePath(); bool fat32_compatible = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool(); bool replacespaceswithunderscores = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_UNDERSCORE_INDEX)).toBool(); bool _2digitstracknum = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_2DIGITSTRACKNUM_INDEX)).toBool(); int cdnum; if (!cdda_model->isMultiCD()) cdnum = 0; else cdnum = cdda_model->cdNum(); int trackoffset = cdda_model->trackOffset(); int nooftracks = cdda_model->numOfAudioTracks(); bool overwrite = Preferences::overwriteExistingFiles(); QString targetFilename; if (cdda_model->isVarious()) { if (!construct_target_filename(targetFilename, ex_track_index, cdnum, nooftracks, trackoffset, artist, title, tartist, ttitle, year, genre, suffix, basepath, fat32_compatible, replacespaceswithunderscores, _2digitstracknum, overwrite, (ex_track_index == 1))) { request_finish(false); return; } } else { if (!construct_target_filename(targetFilename, ex_track_index, cdnum, nooftracks, trackoffset, artist, title, artist, ttitle, year, genre, suffix, basepath, fat32_compatible, replacespaceswithunderscores, _2digitstracknum, overwrite, (ex_track_index == 1))) { request_finish(false); return; } } ex_track_target_filename = targetFilename; // if empty (maybe because it already exists) skip if (!targetFilename.isEmpty()) { emit changedExtractTrack(ex_track_index, cdda_model->numOfAudioTracks(), tartist, ttitle); QString sourceFilename = tmp_path + QString("%1").arg(DiscIDCalculator::FreeDBId(cdda_model->discSignature())) + '.' + QString("%1").arg(ex_track_index) + ".wav"; ex_track_source_filename = sourceFilename; wave_file_writer->open(sourceFilename); if (Preferences::paranoiaMode()) { cdda_extract_thread->setParanoiaMode(3); } else { cdda_extract_thread->setParanoiaMode(0); } cdda_extract_thread->setNeverSkip(Preferences::neverSkip()); cdda_extract_thread->setTrackToRip(ex_track_index); cdda_extract_thread->start(); process_counter++; ex_track_count++; } else { en_track_count++; ex_track_count++; // important to check for finish cdda_extract_thread->skipTrack(ex_track_index); start_extract(); } } else { start_extract(); } } } void Audex::finish_extract() { process_counter--; wave_file_writer->close(); if (_finished) { QFile file(ex_track_source_filename); file.remove(); if (!process_counter) execute_finish(); return; } jobs->addNewJob(ex_track_source_filename, ex_track_target_filename, ex_track_index); start_extract(); } void Audex::start_encode() { if (_finished) return; if (p_single_file) { if (en_track_count >= 1) { request_finish(true); return; } if (encoder_wrapper->isProcessing()) return; AudexJob *job = jobs->orderJob(); if (!job) return; int cdnum = cdda_model->cdNum(); int nooftracks = cdda_model->numOfAudioTracks(); QString artist = cdda_model->artist(); QString title = cdda_model->title(); QString year = cdda_model->year(); QString genre = cdda_model->genre(); QString suffix = p_suffix; CachedImage *cover = cdda_model->cover(); QString targetFilename = job->targetFilename(); en_track_target_filename = targetFilename; emit changedEncodeTrack(job->trackNo(), 1, targetFilename); en_track_count++; en_track_filename = job->sourceFilename(); en_track_index = job->trackNo(); if (!encoder_wrapper->encode(job->trackNo(), cdnum, 0, nooftracks, artist, title, artist, title, genre, year, suffix, cover, false, tmp_path, job->sourceFilename(), targetFilename)) { request_finish(false); } process_counter++; } else { if (en_track_count >= cdda_model->numOfAudioTracksInSelection()) { request_finish(true); return; } if (encoder_wrapper->isProcessing()) return; AudexJob *job = jobs->orderJob(); if (!job) return; int cdnum = cdda_model->cdNum(); int trackoffset = cdda_model->trackOffset(); int nooftracks = cdda_model->numOfAudioTracks(); QString artist = cdda_model->artist(); QString title = cdda_model->title(); QString tartist = cdda_model->data(cdda_model->index(job->trackNo() - 1, CDDA_MODEL_COLUMN_ARTIST_INDEX)).toString(); QString ttitle = cdda_model->data(cdda_model->index(job->trackNo() - 1, CDDA_MODEL_COLUMN_TITLE_INDEX)).toString(); QString year = cdda_model->year(); QString genre = cdda_model->genre(); QString suffix = p_suffix; CachedImage *cover = cdda_model->cover(); bool fat32_compatible = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool(); QString targetFilename = job->targetFilename(); en_track_target_filename = targetFilename; emit changedEncodeTrack(job->trackNo(), cdda_model->numOfAudioTracks(), targetFilename); en_track_count++; en_track_filename = job->sourceFilename(); en_track_index = job->trackNo(); if (cdda_model->isVarious()) { if (!encoder_wrapper->encode(job->trackNo(), cdnum, trackoffset, nooftracks, artist, title, tartist, ttitle, genre, year, suffix, cover, fat32_compatible, tmp_path, job->sourceFilename(), targetFilename)) { request_finish(false); } } else { if (!encoder_wrapper->encode(job->trackNo(), cdnum, trackoffset, nooftracks, artist, title, artist, ttitle, genre, year, suffix, cover, fat32_compatible, tmp_path, job->sourceFilename(), targetFilename)) { request_finish(false); } } process_counter++; } } void Audex::finish_encode() { process_counter--; jobs->reportJobFinished(); cdda_model->setCustomDataPerTrack(en_track_index, "filename", en_track_target_filename); cdda_model->setCustomDataPerTrack(en_track_index, "ripped", true); QFile file(en_track_filename); file.remove(); if (_finished) { if (!process_counter) execute_finish(); return; } emit changedEncodeTrack(0, 0, ""); progress_encode(0); start_encode(); } void Audex::calculate_speed_extract() { if ((last_measuring_point_sector > -1) && (cdda_extract_thread->isProcessing())) { double new_value = (double)(current_sector - last_measuring_point_sector) / (2.0f * 75.0f); if (new_value < 0.0f) new_value = 0.0f; if ((new_value < 0.2f) && (!timeout_done)) { timeout_counter += 2; if (timeout_counter >= 300) { timeout_done = true; emit timeout(); } } emit speedExtract(new_value); } else { emit speedExtract(0.0f); } last_measuring_point_sector = current_sector; } void Audex::calculate_speed_encode() { if ((last_measuring_point_encoder_percent > -1) && (encoder_wrapper->isProcessing()) && (current_encoder_percent > 0)) { int song_length = cdda_model->data(cdda_model->index(en_track_index - 1, CDDA_MODEL_COLUMN_LENGTH_INDEX), CDDA_MODEL_INTERNAL_ROLE).toInt(); double new_value = (double)((double)song_length / 100.0f) * ((double)current_encoder_percent - (double)last_measuring_point_encoder_percent); if (new_value < 0.0f) new_value = 0.0f; emit speedEncode(new_value); } else { emit speedEncode(0.0f); } last_measuring_point_encoder_percent = current_encoder_percent; } void Audex::progress_extract(int percent_of_track, int sector, int overall_sectors_read) { if (overall_frames == 0) { QSet sel = cdda_model->selectedTracks(); QSet::ConstIterator it(sel.begin()), end(sel.end()); for (; it != end; ++it) { if ((*it < 0) || (*it > cdda_extract_thread->cddaParanoia()->numOfTracks()) || (!cdda_extract_thread->cddaParanoia()->isAudioTrack((*it)))) { continue; } overall_frames += cdda_extract_thread->cddaParanoia()->numOfFramesOfTrack((*it)); } } float fraction = 0.0f; if (overall_frames > 0) fraction = (float)overall_sectors_read / (float)overall_frames; emit progressExtractTrack(percent_of_track); emit progressExtractOverall((int)(fraction * 100.0f)); current_sector = sector; } void Audex::progress_encode(int percent) { emit progressEncodeTrack(percent); if (percent > 0) { emit progressEncodeOverall(((en_track_count > 0 ? ((en_track_count - 1) * 100.0f) : 0) + (percent * 1.0f)) / (float)cdda_model->numOfAudioTracksInSelection()); } current_encoder_percent = percent; } void Audex::write_to_wave(const QByteArray &data) { wave_file_writer->write(data); } void Audex::slot_error(const QString &description, const QString &solution) { emit error(description, solution); request_finish(false); } void Audex::slot_warning(const QString &description) { emit warning(description); } void Audex::slot_info(const QString &description) { emit info(description); } void Audex::check_if_thread_still_running() { if (cdda_extract_thread->isRunning()) { // this could happen if the thread is stuck in paranoia_read // because of an unreadable cd cdda_extract_thread->terminate(); qDebug() << "Terminate extracting thread."; } } bool Audex::construct_target_filename(QString &targetFilename, int trackno, int cdno, int nooftracks, int gindex, const QString &artist, const QString &title, const QString &tartist, const QString &ttitle, const QString &year, const QString &genre, const QString &ext, const QString &basepath, bool fat_compatible, bool replacespaceswithunderscores, bool _2digitstracknum, bool overwrite_existing_files, bool is_first_track) { Q_UNUSED(is_first_track); PatternParser patternparser; targetFilename = ((basepath.right(1) == "/") ? basepath : basepath + "/") + patternparser.parseFilenamePattern(profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PATTERN_INDEX)).toString(), trackno, cdno, gindex, nooftracks, artist, title, tartist, ttitle, year, genre, ext, fat_compatible, replacespaceswithunderscores, _2digitstracknum); int lastSlash = targetFilename.lastIndexOf("/", -1); if (lastSlash == -1) { emit error(i18n("Can't find path \"%1\".", targetFilename), i18n("Please check your path (write access?)")); return false; } QString targetPath = targetFilename.mid(0, lastSlash); QString targetStrippedFilename = targetFilename.mid(lastSlash + 1); target_dir = targetPath; if (!p_mkdir(targetPath)) return false; KDiskFreeSpaceInfo diskfreespace = KDiskFreeSpaceInfo::freeSpaceInfo(targetPath); quint64 free = diskfreespace.available() / 1024; if (free < 200 * 1024) { emit warning(i18n("Free space on \"%1\" is less than 200 MiB.", targetPath)); } auto *file = new QFile(targetFilename); if (file->exists()) { if (overwrite_existing_files) { emit warning(i18n("Warning! File \"%1\" already exists. Overwriting.", targetStrippedFilename)); } else { emit warning(i18n("Warning! File \"%1\" already exists. Skipping.", targetStrippedFilename)); cdda_model->setCustomDataPerTrack(trackno, "filename", targetFilename); cdda_model->setCustomDataPerTrack(trackno, "ripped", true); targetFilename.clear(); } } delete file; return true; } bool Audex::construct_target_filename_for_singlefile(QString &targetFilename, int cdno, int nooftracks, const QString &artist, const QString &title, const QString &date, const QString &genre, const QString &ext, const QString &basepath, bool overwrite_existing_files) { PatternParser patternparser; targetFilename = ((basepath.right(1) == "/") ? basepath : basepath + "/") + patternparser.parseSimplePattern(profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SF_NAME_INDEX)).toString(), cdno, nooftracks, artist, title, date, genre, ext, false); int lastSlash = targetFilename.lastIndexOf("/", -1); if (lastSlash == -1) { emit error(i18n("Can't find path \"%1\".", targetFilename), i18n("Please check your path (write access?)")); return false; } QString targetPath = targetFilename.mid(0, lastSlash); target_dir = targetPath; QString targetStrippedFilename = targetFilename.mid(lastSlash + 1); QDir *dir = new QDir(targetPath); if (!dir->exists()) { if (!dir->mkpath(targetPath)) { emit error(i18n("Unable to create folder \"%1\".", targetPath), i18n("Please check your path (write access?)")); return false; } else { emit info(i18n("Folder \"%1\" successfully created.", targetPath)); } } else { emit warning(i18n("Folder \"%1\" already exists.", targetPath)); } delete dir; KDiskFreeSpaceInfo diskfreespace = KDiskFreeSpaceInfo::freeSpaceInfo(targetPath); quint64 free = diskfreespace.available() / 1024; if (free < 800 * 1024) { emit warning(i18n("Free space on \"%1\" is less than 800 MiB.", targetPath)); } auto *file = new QFile(targetFilename); if (file->exists()) { if (overwrite_existing_files) { emit warning(i18n("Warning! File \"%1\" already exists. Overwriting.", targetStrippedFilename)); } else { emit warning(i18n("Warning! File \"%1\" already exists. Skipping.", targetStrippedFilename)); cdda_model->setCustomData("filename", targetFilename); targetFilename.clear(); } } delete file; return true; } bool Audex::check() { if (tmp_dir->error()) { slot_error(i18n("Temporary folder \"%1\" error.", tmp_dir->tmpPath()), i18n("Please check.")); return false; } quint64 free = tmp_dir->freeSpace() / 1024; if (free < 800 * 1024) { slot_warning(i18n("Free space on temporary folder \"%1\" is less than 800 MiB.", tmp_dir->tmpPathBase())); } else if (free < 200 * 1024) { slot_error(i18n("Temporary folder \"%1\" needs at least 200 MiB of free space.", tmp_dir->tmpPathBase()), i18n("Please free space or set another path.")); return false; } return true; } void Audex::request_finish(bool successful) { if (!_finished) { _finished = true; _finished_successful = successful; } else { return; } if (process_counter > 0) { encoder_wrapper->cancel(); cdda_extract_thread->cancel(); QTimer::singleShot(2000, this, SLOT(check_if_thread_still_running())); } else { execute_finish(); } } void Audex::execute_finish() { if (Preferences::ejectCDTray()) { emit info(i18n("Eject CD tray")); cdda_model->eject(); } bool overwrite = Preferences::overwriteExistingFiles(); QStringList target_filename_list; for (int i = 0; i < cdda_model->rowCount(); ++i) { if (!cdda_model->isAudioTrack(i + 1)) continue; if (!cdda_model->isTrackInSelection(i + 1)) continue; if (!cdda_model->getCustomDataPerTrack(i + 1, "ripped").toBool()) continue; target_filename_list.append(cdda_model->getCustomDataPerTrack(i + 1, "filename").toString()); } QString target_single_filename; if (p_single_file) { target_single_filename = cdda_model->customData("filename").toString(); } QString co; if ((_finished_successful) && (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_INDEX)).toBool())) { // store the cover if (!cdda_model->isCoverEmpty()) { QImage image(cdda_model->coverImage()); if (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_SCALE_INDEX)).toBool()) { QSize size = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_SIZE_INDEX)).toSize(); image = image.scaled(size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); qDebug() << QString("Cover scaled to %1x%2.").arg(size.width()).arg(size.height()); } QString pattern = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_NAME_INDEX)).toString(); QString format = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_SC_FORMAT_INDEX)).toString(); PatternParser patternparser; QString filename = patternparser.parseSimplePattern(pattern, cdda_model->cdNum(), cdda_model->numOfAudioTracks(), cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), format.toLower(), profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool()); if (p_prepare_dir(filename, target_dir, overwrite)) { - if (image.save(filename, format.toAscii().data())) { + if (image.save(filename, format.toLatin1().data())) { emit info(i18n("Cover \"%1\" successfully saved.", QFileInfo(filename).fileName())); co = filename; } else { emit error(i18n("Unable to save cover \"%1\".", QFileInfo(filename).fileName()), i18n("Please check your path and permissions")); } } } } QString pl; if ((_finished_successful) && (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PL_INDEX)).toBool()) && (target_filename_list.count() > 0) && (!p_single_file)) { // create the playlist Playlist playlist; QString pattern = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PL_NAME_INDEX)).toString(); QString format = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PL_FORMAT_INDEX)).toString(); bool is_absFilePath = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PL_ABS_FILE_PATH_INDEX)).toBool(); bool is_utf8 = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_PL_UTF8_INDEX)).toBool(); PatternParser patternparser; QString filename = patternparser.parseSimplePattern(pattern, cdda_model->cdNum(), cdda_model->numOfAudioTracks(), cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), format.toLower(), profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool()); if (p_prepare_dir(filename, target_dir, (overwrite && !cdda_model->isMultiCD() && (cdda_model->cdNum() < 1)))) { QFile file(filename); if (file.exists() && cdda_model->isMultiCD() && (cdda_model->cdNum() > 0)) { if (file.open(QFile::ReadOnly)) { QByteArray ba = file.readAll(); playlist.addPlaylist(ba); file.close(); } } if (file.open(QFile::WriteOnly | QFile::Truncate)) { for (int i = 0; i < cdda_model->rowCount(); ++i) { if (!cdda_model->isAudioTrack(i + 1)) continue; if (!cdda_model->isTrackInSelection(i + 1)) continue; if (!cdda_model->getCustomDataPerTrack(i + 1, "ripped").toBool()) continue; PlaylistItem item; item.setFilename(cdda_model->getCustomDataPerTrack(i + 1, "filename").toString()); item.setArtist(cdda_model->data(cdda_model->index(i, CDDA_MODEL_COLUMN_ARTIST_INDEX)).toString()); item.setTitle(cdda_model->data(cdda_model->index(i, CDDA_MODEL_COLUMN_TITLE_INDEX)).toString()); item.setLength(cdda_model->lengthOfTrack(i + 1)); playlist.appendItem(item); } QString relFilePath; if (!is_absFilePath) { relFilePath = QFileInfo(filename).absoluteDir().absolutePath(); } if (format == "M3U") { file.write(playlist.toM3U(relFilePath, is_utf8)); } else if (format == "PLS") { file.write(playlist.toPLS(relFilePath, is_utf8)); } else if (format == "XSPF") { file.write(playlist.toXSPF()); } file.close(); emit info(i18n("Playlist \"%1\" successfully created.", QFileInfo(filename).fileName())); pl = filename; } else { emit error(i18n("Unable to save playlist \"%1\".", QFileInfo(filename).fileName()), i18n("Please check your path and permissions")); } } } QString in; if ((_finished_successful) && (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_INF_INDEX)).toBool())) { PatternParser patternparser; QString filename = patternparser.parseSimplePattern(profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_INF_NAME_INDEX)).toString(), cdda_model->cdNum(), cdda_model->numOfAudioTracks(), cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_INF_SUFFIX_INDEX)).toString(), profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool()); if (p_prepare_dir(filename, target_dir, overwrite)) { QFile file(filename); if (file.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream out(&file); QStringList text = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_INF_TEXT_INDEX)).toStringList(); patternparser.parseInfoText(text, cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), DiscIDCalculator::FreeDBId(cdda_model->discSignature()), p_size_of_all_files(target_filename_list), cdda_model->lengthOfAudioTracksInSelection(), cdda_model->numOfAudioTracksInSelection()); out << text.join("\n"); file.close(); emit info(i18n("Info file \"%1\" successfully created.", QFileInfo(filename).fileName())); in = filename; } else { emit error(i18n("Unable to save info file \"%1\".", QFileInfo(filename).fileName()), i18n("Please check your path and permissions")); } } } QString hl; if ((_finished_successful) && (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_HL_INDEX)).toBool()) && (target_filename_list.count() > 0)) { QString pattern = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_HL_NAME_INDEX)).toString(); QString format = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_HL_FORMAT_INDEX)).toString(); PatternParser patternparser; QString filename = patternparser.parseSimplePattern(pattern, cdda_model->cdNum(), cdda_model->numOfAudioTracks(), cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), format.toLower(), profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool()); if (p_prepare_dir(filename, target_dir, (overwrite && !cdda_model->isMultiCD() && (cdda_model->cdNum() < 1)))) { QFile file(filename); bool fexists = file.exists() && cdda_model->isMultiCD() && (cdda_model->cdNum() > 0); bool success; if (fexists) success = file.open(QFile::WriteOnly | QFile::Append); else success = file.open(QFile::WriteOnly | QFile::Truncate); if (success) { QTextStream out(&file); if (fexists) out << "\n"; Hashlist hashlist; if (format == "SFV") { out << hashlist.getSFV(target_filename_list).join("\n"); } else if (format == "MD5") { out << hashlist.getMD5(target_filename_list).join("\n"); } file.close(); emit info(i18n("Hashlist \"%1\" successfully created.", QFileInfo(filename).fileName())); hl = filename; } else { emit error(i18n("Unable to save hashlist \"%1\".", QFileInfo(filename).fileName()), i18n("Please check your path and permissions")); } } } QString cs; if ((_finished_successful) && (profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_CUE_INDEX)).toBool()) && (((target_filename_list.count() > 0) && !p_single_file) || p_single_file)) { QString pattern = profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_CUE_NAME_INDEX)).toString(); PatternParser patternparser; QString filename = patternparser.parseSimplePattern(pattern, cdda_model->cdNum(), cdda_model->numOfAudioTracks(), cdda_model->artist(), cdda_model->title(), QString("%1").arg(cdda_model->year()), cdda_model->genre(), "cue", profile_model->data(profile_model->index(profile_model->currentProfileRow(), PROFILE_MODEL_COLUMN_FAT32COMPATIBLE_INDEX)).toBool()); if (p_prepare_dir(filename, target_dir, overwrite)) { QFile file(filename); bool success = file.open(QFile::WriteOnly | QFile::Truncate); if (success) { QTextStream out(&file); CueSheetWriter cuesheetwriter(cdda_model); if (p_single_file) { out << cuesheetwriter.cueSheet(target_single_filename).join("\n"); } else { out << cuesheetwriter.cueSheet(target_filename_list).join("\n"); } file.close(); emit info(i18n("Cue sheet \"%1\" successfully created.", QFileInfo(filename).fileName())); cs = filename; } else { emit error(i18n("Unable to save cue sheet \"%1\".", QFileInfo(filename).fileName()), i18n("Please check your path and permissions")); } } } if ((_finished_successful) && (Preferences::upload()) && (target_filename_list.count() > 0)) { QString targetpath = QFileInfo(target_filename_list.at(0)).absolutePath().mid(Preferences::basePath().length()); QStringList files_to_transfer = target_filename_list; if (!co.isEmpty()) files_to_transfer << co; if (!pl.isEmpty()) files_to_transfer << pl; if (!in.isEmpty()) files_to_transfer << in; if (!hl.isEmpty()) files_to_transfer << hl; if (!cs.isEmpty()) files_to_transfer << cs; Upload upload(Preferences::url(), this); connect(&upload, SIGNAL(info(const QString &)), this, SLOT(slot_info(const QString &))); connect(&upload, SIGNAL(error(const QString &, const QString &)), this, SLOT(slot_error(const QString &, const QString &))); upload.upload(targetpath, files_to_transfer); } // flush temporary path if (!tmp_path.isEmpty()) { QDir tmp(tmp_path); QStringList files = tmp.entryList(QStringList() << "*", QDir::Files | QDir::NoDotAndDotDot); for (int i = 0; i < files.count(); ++i) { QFile::remove(tmp_path + files[i]); qDebug() << "Deleted temporary file" << tmp_path + files[i]; } } emit finished(_finished_successful); } bool Audex::p_prepare_dir(QString &filename, const QString &targetDirIfRelative, const bool overwrite) { QString result; QFileInfo fileinfo(filename); if (fileinfo.isAbsolute()) { if (!p_mkdir(fileinfo.dir().absolutePath())) { return false; } else { result = filename; } } else { if (!targetDirIfRelative.isEmpty()) { QDir dir(targetDirIfRelative); if (!dir.isReadable()) { emit error(i18n("Unable to open folder \"%1\".", targetDirIfRelative), i18n("Please check your path and permissions")); return false; } result = targetDirIfRelative + '/' + filename; } else { result = filename; } } if (!overwrite) { QFileInfo info(result); if (info.exists()) { emit warning(i18n("Warning! File \"%1\" already exists. Skipping.", info.fileName())); return false; } } filename = result; return true; } bool Audex::p_mkdir(const QString &absoluteFilePath) { QDir dir(absoluteFilePath); if (dir.exists()) { if (!dir.isReadable()) { emit error(i18n("Unable to open folder \"%1\".", absoluteFilePath), i18n("Please check your path and permissions")); return false; } } else { if (!dir.mkpath(absoluteFilePath)) { emit error(i18n("Unable to create folder \"%1\".", absoluteFilePath), i18n("Please check your path (write access?)")); return false; } else { emit info(i18n("Folder \"%1\" successfully created.", absoluteFilePath)); } } return true; } qreal Audex::p_size_of_all_files(const QStringList &filenames) const { qreal size = .0f; for (int i = 0; i < filenames.count(); ++i) { QFileInfo info(filenames.at(i)); size += info.size(); } return size; } diff --git a/dialogs/cddaheaderdatadialog.cpp b/dialogs/cddaheaderdatadialog.cpp index 91839b0..df35a7a 100644 --- a/dialogs/cddaheaderdatadialog.cpp +++ b/dialogs/cddaheaderdatadialog.cpp @@ -1,322 +1,322 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cddaheaderdatadialog.h" #include #include #define GENRE_MAX 148 static const char *ID3_GENRES[GENRE_MAX] = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop"}; CDDAHeaderDataDialog::CDDAHeaderDataDialog(CDDAModel *cddaModel, QWidget *parent) : QDialog(parent) { Q_UNUSED(parent); cdda_model = cddaModel; if (!cdda_model) { qDebug() << "CDDAModel is NULL!"; return; } setWindowTitle(i18n("Edit Data")); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); applyButton = buttonBox->button(QDialogButtonBox::Apply); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &CDDAHeaderDataDialog::slotAccepted); connect(buttonBox, &QDialogButtonBox::rejected, this, &CDDAHeaderDataDialog::reject); connect(applyButton, &QPushButton::clicked, this, &CDDAHeaderDataDialog::slotApplied); QWidget *widget = new QWidget(this); mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); ui.setupUi(widget); QStringList genres; for (int i = 0; i < GENRE_MAX; ++i) - genres.append(QString().fromAscii(ID3_GENRES[i])); + genres.append(QString().fromLatin1(ID3_GENRES[i])); genres.sort(); KCompletion *comp = ui.kcombobox_genre->completionObject(); comp->insertItems(genres); ui.kcombobox_genre->addItems(genres); connect(ui.kcombobox_genre, SIGNAL(returnPressed(const QString &)), comp, SLOT(addItem(const QString &))); ui.checkBox_various->setChecked(cdda_model->isVarious()); connect(ui.checkBox_various, SIGNAL(toggled(bool)), this, SLOT(trigger_changed())); ui.checkBox_multicd->setChecked(cdda_model->isMultiCD()); connect(ui.checkBox_multicd, SIGNAL(toggled(bool)), this, SLOT(enable_checkbox_multicd(bool))); connect(ui.checkBox_multicd, SIGNAL(toggled(bool)), this, SLOT(trigger_changed())); ui.qlineedit_artist->setText(cdda_model->artist()); connect(ui.qlineedit_artist, SIGNAL(textEdited(const QString &)), this, SLOT(trigger_changed())); ui.qlineedit_title->setText(cdda_model->title()); connect(ui.qlineedit_title, SIGNAL(textEdited(const QString &)), this, SLOT(trigger_changed())); ui.kintspinbox_cdnum->setValue(cdda_model->cdNum()); connect(ui.kintspinbox_cdnum, SIGNAL(valueChanged(int)), this, SLOT(trigger_changed())); ui.kintspinbox_trackoffset->setValue(cdda_model->trackOffset()); connect(ui.kintspinbox_trackoffset, SIGNAL(valueChanged(int)), this, SLOT(trigger_changed())); ui.kcombobox_genre->lineEdit()->setText(cdda_model->genre()); connect(ui.kcombobox_genre->lineEdit(), SIGNAL(textEdited(const QString &)), this, SLOT(trigger_changed())); { bool ok; int year = cdda_model->year().toInt(&ok); if (ok) ui.kintspinbox_year->setValue(year); else ui.kintspinbox_year->setValue(QDate::currentDate().year()); } connect(ui.kintspinbox_year, SIGNAL(valueChanged(int)), this, SLOT(trigger_changed())); ui.ktextedit_extdata->setText(cdda_model->extendedData().join("\n")); connect(ui.ktextedit_extdata, SIGNAL(textChanged()), this, SLOT(trigger_changed())); ui.qlineedit_freedbdiscid->setText(QString("0x%1").arg(DiscIDCalculator::FreeDBId(cdda_model->discSignature()), 0, 16)); enable_checkbox_multicd(cdda_model->isMultiCD()); applyButton->setEnabled(false); } CDDAHeaderDataDialog::~CDDAHeaderDataDialog() { } void CDDAHeaderDataDialog::slotAccepted() { save(); accept(); } void CDDAHeaderDataDialog::slotApplied() { save(); } void CDDAHeaderDataDialog::save() { cdda_model->setVarious(ui.checkBox_various->isChecked()); cdda_model->setMultiCD(ui.checkBox_multicd->isChecked()); cdda_model->setArtist(ui.qlineedit_artist->text()); cdda_model->setTitle(ui.qlineedit_title->text()); cdda_model->setCDNum(ui.kintspinbox_cdnum->value()); cdda_model->setTrackOffset(ui.kintspinbox_trackoffset->value()); cdda_model->setGenre(ui.kcombobox_genre->lineEdit()->text()); cdda_model->setYear(QString("%1").arg(ui.kintspinbox_year->value())); cdda_model->setExtendedData(ui.ktextedit_extdata->toPlainText().split('\n')); applyButton->setEnabled(false); } void CDDAHeaderDataDialog::trigger_changed() { if (ui.checkBox_various->isChecked() != cdda_model->isVarious()) { applyButton->setEnabled(true); return; } if (ui.checkBox_multicd->isChecked() != cdda_model->isMultiCD()) { applyButton->setEnabled(true); return; } if (ui.qlineedit_artist->text() != cdda_model->artist()) { applyButton->setEnabled(true); return; } if (ui.qlineedit_title->text() != cdda_model->title()) { applyButton->setEnabled(true); return; } if (ui.checkBox_various->isChecked()) if (ui.kintspinbox_cdnum->value() != cdda_model->cdNum()) { applyButton->setEnabled(true); return; } if (ui.kintspinbox_trackoffset->value() != cdda_model->trackOffset()) { applyButton->setEnabled(true); return; } if (ui.kcombobox_genre->lineEdit()->text() != cdda_model->genre()) { applyButton->setEnabled(true); return; } if (ui.kintspinbox_year->value() != cdda_model->year().toInt()) { applyButton->setEnabled(true); return; } if (ui.ktextedit_extdata->toPlainText().split('\n') != cdda_model->extendedData()) { applyButton->setEnabled(true); return; } applyButton->setEnabled(false); } void CDDAHeaderDataDialog::enable_checkbox_multicd(bool enabled) { ui.kintspinbox_cdnum->setEnabled(enabled); ui.label_cdnum->setEnabled(enabled); } diff --git a/utils/cachedimage.cpp b/utils/cachedimage.cpp index 91ce6ad..10f65c5 100644 --- a/utils/cachedimage.cpp +++ b/utils/cachedimage.cpp @@ -1,257 +1,257 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cachedimage.h" #include #include #include #include CachedImage::CachedImage() { } CachedImage::CachedImage(const QByteArray &data) { load(data); } CachedImage::CachedImage(const QString &filename) { load(filename); } CachedImage::CachedImage(const CachedImage &other) { _data = other._data; _format = other._format; mime_cache = other.mime_cache; } CachedImage &CachedImage::operator=(const CachedImage &other) { _data = other._data; _format = other._format; mime_cache = other.mime_cache; return *this; } bool CachedImage::operator==(const CachedImage &other) const { return (this->checksum() == other.checksum()); } bool CachedImage::operator!=(const CachedImage &other) const { return (this->checksum() != other.checksum()); } CachedImage::~CachedImage() {}; Error CachedImage::lastError() const { return _error; } bool CachedImage::isEmpty() const { return (_data.size() == 0); } const QByteArray CachedImage::formatRaw() const { return _format; } int CachedImage::dataSize(const QByteArray &format, const QSize &size) { if ((compare_format(format, _format) || (format.isEmpty())) && ((size.isNull()) || (size == _size))) { return _data.size(); } else { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); _save(&buffer, format, size); buffer.close(); return ba.size(); } } const QByteArray CachedImage::coverRaw() const { return _data; } const QSize CachedImage::size() const { return _size; } const QImage CachedImage::coverImage() const { return QImage::fromData(_data); } quint16 CachedImage::checksum() const { return qChecksum(_data.data(), _data.size()); } const QString CachedImage::supportedMimeTypeList() { if (mime_cache.isEmpty()) { QList supp_list = QImageReader::supportedImageFormats(); QMap map; QMimeDatabase db; QMimeType mime = db.mimeTypeForData(QByteArray("")); for (int i = 0; i < supp_list.count(); ++i) { map[db.mimeTypeForUrl("dummy." + QString(supp_list[i]).toLower()).comment()].append("*." + QString(supp_list[i]).toLower()); } QString result = "*.jpg *.jpeg *.png *.gif|" + i18n("Common image formats") + " (*.jpg, *.jpeg, *.png, *.gif)"; QMap::const_iterator i = map.constBegin(); while (i != map.constEnd()) { if (i.key() == mime.comment()) { ++i; continue; } result += '\n'; QStringList extensions = i.value(); extensions.removeDuplicates(); result += extensions.join(' ') + '|' + i.key() + " (" + extensions.join(", ") + ')'; ++i; } mime_cache = result; } return mime_cache; } const QList CachedImage::supportedFormats() { return QImageReader::supportedImageFormats(); } bool CachedImage::load(const QByteArray &data) { QBuffer buffer((QByteArray *)&data); buffer.open(QIODevice::ReadOnly); QImageReader ir(&buffer); _format = ir.format(); _size = ir.size(); buffer.close(); qDebug() << "Load cover image from buffer (" << _format << ")"; if (!_format.isEmpty() && QImageReader::supportedImageFormats().contains(_format)) { _data = data; return true; } _format = ""; _size = QSize(); return false; } bool CachedImage::load(const QString &filename) { QFile file(filename); if (!file.exists()) { _error = Error(i18n("File does not exist."), i18n("Please check if the file really exists."), Error::ERROR); return false; } QImageReader ir(filename); _format = ir.format(); _size = ir.size(); qDebug() << "Load cover image from file (" << _format << ")"; if (!_format.isEmpty() && QImageReader::supportedImageFormats().contains(_format)) { if (!file.open(QIODevice::ReadOnly)) { _error = Error(i18n("Cannot open file."), i18n("Please check your file. Maybe you do not have proper permissions."), Error::ERROR); return false; } _data = file.readAll(); file.close(); } else { _error = Error(i18n("Unsupported image format"), i18n("Please check your file. Maybe it is corrupted. Otherwise try to convert your cover image with an external program to a common file format like JPEG."), Error::ERROR); _format = ""; _size = QSize(); return false; } _error = Error(); return true; } void CachedImage::clear() { _data.clear(); _format.clear(); } bool CachedImage::save(const QString &filename, const QSize &size) { QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { _error = Error(i18n("Cannot open file"), i18n("Please check your permissions."), Error::ERROR); return false; } QMimeDatabase db; - QByteArray format = db.suffixForFileName(filename).toLower().toAscii(); + QByteArray format = db.suffixForFileName(filename).toLower().toLatin1(); if ((compare_format(format, _format) || (format.isEmpty())) && ((size.isNull()) || (size == _size))) { qint64 r = file.write(_data); if ((r == -1) || (r < _data.size())) { _error = Error(i18n("Cannot save file"), i18n("Please check your permissions. Do you have enough space?"), Error::ERROR); file.close(); return false; } } else { if (!_save(&file, format, size)) { file.close(); return false; } } file.close(); _error = Error(); return true; } bool CachedImage::compare_format(const QByteArray &f1, const QByteArray &f2) const { if (((f1.toLower() == "jpg") || (f1.toLower() == "jpeg")) && ((f2.toLower() == "jpg") || (f2.toLower() == "jpeg"))) return true; if (((f1.toLower() == "tif") || (f1.toLower() == "tiff")) && ((f2.toLower() == "tif") || (f2.toLower() == "tiff"))) return true; return (f1.toLower() == f2.toLower()); } bool CachedImage::_save(QIODevice *device, const QByteArray &format, const QSize &size) { if ((!format.isEmpty()) && (!QImageReader::supportedImageFormats().contains(format))) { _error = Error(i18n("Unsupported image format"), i18n("Please use common image formats like JPEG, PNG or GIF as they are supported on almost all systems."), Error::ERROR); return false; } QImage image = QImage::fromData(_data); if ((size.isValid()) && (image.size() != size)) image = image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); bool ok; if (!format.isEmpty()) ok = image.save(device, format.data()); else ok = image.save(device); if (!ok) { _error = Error(i18n("Cannot write on device"), i18n("Please check your permissions. Do you have enough space on your device?"), Error::ERROR); return false; } _error = Error(); return true; } diff --git a/utils/cddaparanoia.cpp b/utils/cddaparanoia.cpp index 7c3515a..519385a 100644 --- a/utils/cddaparanoia.cpp +++ b/utils/cddaparanoia.cpp @@ -1,366 +1,366 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cddaparanoia.h" #include /* some of this code in here is based on k3b 0.8.x sourcecode */ CDDAParanoia::CDDAParanoia(QObject *parent) : QObject(parent) { Q_UNUSED(parent); paranoia = nullptr; paranoia_drive = nullptr; setNeverSkip(true); setMaxRetries(20); setParanoiaMode(3); } CDDAParanoia::~CDDAParanoia() { _paranoia_free(); } bool CDDAParanoia::setDevice(const QString &device) { if ((device.isEmpty() && (_device.isEmpty()))) _device = "/dev/cdrom"; if (!device.isEmpty()) _device = device; if (!_paranoia_init()) { qDebug() << "Internal device error."; emit error(i18n("Internal device error."), i18n("Check your device. Is it really \"%1\"? If so also check your permissions on \"%1\".", _device)); return false; } return true; } QString CDDAParanoia::device() const { return _device; } void CDDAParanoia::setParanoiaMode(int mode) { mutex.lock(); // from cdrdao 1.1.7 paranoia_mode = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP; switch (mode) { case 0: paranoia_mode = PARANOIA_MODE_DISABLE; break; case 1: paranoia_mode |= PARANOIA_MODE_OVERLAP; paranoia_mode &= ~PARANOIA_MODE_VERIFY; break; case 2: paranoia_mode &= ~(PARANOIA_MODE_SCRATCH | PARANOIA_MODE_REPAIR); break; } if (paranoia_never_skip) paranoia_mode |= PARANOIA_MODE_NEVERSKIP; if (paranoia) paranoia_modeset(paranoia, paranoia_mode); mutex.unlock(); } void CDDAParanoia::setNeverSkip(bool b) { paranoia_never_skip = b; setParanoiaMode(paranoia_mode); } void CDDAParanoia::setMaxRetries(int m) { paranoia_max_retries = m; } qint16 *CDDAParanoia::paranoiaRead(void (*callback)(long, int)) { if (paranoia) { mutex.lock(); int16_t *data = paranoia_read_limited(paranoia, callback, paranoia_max_retries); mutex.unlock(); return data; } return nullptr; } int CDDAParanoia::paranoiaSeek(long sector, int mode) { if (paranoia) { mutex.lock(); long pos = paranoia_seek(paranoia, sector, mode); mutex.unlock(); return pos; } return -1; } int CDDAParanoia::firstSectorOfTrack(int track) { if (paranoia_drive) { mutex.lock(); long first_sector = cdda_track_firstsector(paranoia_drive, track); mutex.unlock(); return first_sector; } return -1; } int CDDAParanoia::lastSectorOfTrack(int track) { if (paranoia_drive) { mutex.lock(); long last_sector = cdda_track_lastsector(paranoia_drive, track); mutex.unlock(); return last_sector; } return -1; } int CDDAParanoia::firstSectorOfDisc() { if (paranoia_drive) { mutex.lock(); long first_sector = cdda_disc_firstsector(paranoia_drive); mutex.unlock(); return first_sector; } return -1; } int CDDAParanoia::lastSectorOfDisc() { if (paranoia_drive) { mutex.lock(); long last_sector = cdda_disc_lastsector(paranoia_drive); mutex.unlock(); return last_sector; } return -1; } void CDDAParanoia::sampleOffset(const int offset) { int sample_offset = offset; // Hack from cdda paranoia if (paranoia_drive) { mutex.lock(); int toc_offset = 0; toc_offset += sample_offset / 588; sample_offset %= 588; if (sample_offset < 0) { sample_offset += 588; toc_offset--; } for (int i = 0; i < paranoia_drive->tracks + 1; ++i) paranoia_drive->disc_toc[i].dwStartSector += toc_offset; mutex.unlock(); } } int CDDAParanoia::numOfTracks() { if (paranoia_drive) return (paranoia_drive->tracks < 0) ? 0 : paranoia_drive->tracks; return 0; } int CDDAParanoia::numOfAudioTracks() { if (numOfTracks() > 0) { int j = 0; for (int i = 1; i <= numOfTracks(); i++) { if (isAudioTrack(i)) j++; } return j; } return 0; } int CDDAParanoia::length() { return numOfFrames() / 75; } int CDDAParanoia::numOfFrames() { if (numOfTracks() > 0) { if (paranoia_drive) return cdda_disc_lastsector(paranoia_drive); } return 0; } int CDDAParanoia::lengthOfAudioTracks() { return numOfFramesOfAudioTracks() / 75; } int CDDAParanoia::numOfFramesOfAudioTracks() { if (numOfTracks() > 0) { int frames = 0; for (int i = 1; i <= numOfTracks(); ++i) { if (isAudioTrack(i)) frames += numOfFramesOfTrack(i); } return frames; } return 0; } int CDDAParanoia::numOfSkippedFrames(int n) { if (numOfTracks() > 0) { if (n < 1) n = 1; if (n > numOfTracks()) n = numOfTracks(); int frames = 0; for (int i = 1; i < n; ++i) { if (!isAudioTrack(i)) frames += numOfFramesOfTrack(i); } return frames; } return 0; } int CDDAParanoia::lengthOfTrack(int n) { if (numOfTracks() > 0) { return numOfFramesOfTrack(n) / 75; } return 0; } int CDDAParanoia::numOfFramesOfTrack(int n) { if (numOfTracks() > 0) { if (n < 1) n = 1; if (n > numOfTracks()) n = numOfTracks(); if (n == numOfTracks()) { return numOfFrames() - paranoia_drive->disc_toc[n - 1].dwStartSector; } else { return paranoia_drive->disc_toc[n].dwStartSector - paranoia_drive->disc_toc[n - 1].dwStartSector; } } return 0; } double CDDAParanoia::sizeOfTrack(int n) { if (numOfTracks() > 0) { auto frame_size = (double)(numOfFramesOfTrack(n)); if (isAudioTrack(n)) { return (frame_size * 2352.0f) / (1024.0f * 1024.0f); } else { return (frame_size * 2048.0f) / (1024.0f * 1024.0f); } } return 0.0f; } int CDDAParanoia::frameOffsetOfTrack(int n) { if (numOfTracks() > 0) { return paranoia_drive->disc_toc[n - 1].dwStartSector; } return 0; } bool CDDAParanoia::isAudioTrack(int n) { if (paranoia_drive) return IS_AUDIO(paranoia_drive, n - 1); return true; } QList CDDAParanoia::discSignature(const qint32 pregap) { QList result; for (int i = 1; i <= numOfTracks() + 1; ++i) result.append(frameOffsetOfTrack(i) + pregap); return result; } void CDDAParanoia::reset() { _paranoia_init(); } bool CDDAParanoia::_paranoia_init() { mutex.lock(); _paranoia_free(); - paranoia_drive = cdda_identify(_device.toAscii().data(), 0, nullptr); + paranoia_drive = cdda_identify(_device.toLatin1().data(), 0, nullptr); if (paranoia_drive == nullptr) { mutex.unlock(); qDebug() << "Failed to find device."; return false; } // cdda_cdda_verbose_set(_drive, 1, 1); cdda_open(paranoia_drive); paranoia = paranoia_init(paranoia_drive); if (paranoia == nullptr) { _paranoia_free(); mutex.unlock(); qDebug() << "Failed to init device."; return false; } mutex.unlock(); return true; } void CDDAParanoia::_paranoia_free() { // mutex.lock(); if (paranoia) { paranoia_free(paranoia); paranoia = nullptr; } if (paranoia_drive) { cdda_close(paranoia_drive); paranoia_drive = nullptr; } // mutex.unlock(); } diff --git a/utils/coverfetcher.cpp b/utils/coverfetcher.cpp index 2b8475c..5dbb782 100644 --- a/utils/coverfetcher.cpp +++ b/utils/coverfetcher.cpp @@ -1,257 +1,257 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Some of this code is inspired by amarok 1.4.7 * (C) 2004 Mark Kretschmann * (C) 2004 Stefan Bogner * (C) 2004 Max Howell */ #include "coverfetcher.h" #include #include #include #include CoverFetcher::CoverFetcher(QObject *parent) : QObject(parent) { Q_UNUSED(parent); _status = NOS; f_i = 0; } CoverFetcher::~CoverFetcher() { clear(); } void CoverFetcher::fetched_external_ip(KJob *job) { qDebug() << "got IP..."; if (!job) { qDebug() << "no job error ..."; emit nothingFetched(); return; } else if (job && job->error()) { qDebug() << "reply error ..."; emit nothingFetched(); return; } // http://www.telize.com/ip returns plaintext ip address auto *const storedJob = static_cast(job); external_ip = ((QString)storedJob->data()).trimmed(); qDebug() << "IP " << external_ip; // Max images per request on Google API is 8, thus the std::min QString url; url = QString("https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=%1&rsz=%2&userip=%3") .arg(QUrl::toPercentEncoding(search_string, "/").constData()) .arg(std::min(fetch_no, 8)) .arg(QUrl::toPercentEncoding(external_ip).constData()); qDebug() << "searching covers (" << url << ")..."; _status = SEARCHING; emit statusChanged(SEARCHING); job = KIO::storedGet(url); connect(job, SIGNAL(result(KJob *)), SLOT(fetched_html_data(KJob *))); } void CoverFetcher::startFetchThumbnails(const QString &searchstring, const int fetchNo) { qDebug() << "Fetch Thumbs ..."; if (_status != NOS || fetchNo == 0) { emit nothingFetched(); return; } fetch_no = fetchNo; search_string = searchstring; search_string.remove('&'); // Google requires the user IP QString url("http://www.telize.com/ip"); job = KIO::storedGet(url); connect(job, SIGNAL(result(KJob *)), SLOT(fetched_external_ip(KJob *))); } void CoverFetcher::stopFetchThumbnails() { if ((_status != FETCHING_THUMBNAIL) && (_status != SEARCHING)) return; if (job) job->kill(); _status = NOS; emit statusChanged(NOS); } void CoverFetcher::startFetchCover(const int no) { if (_status != NOS) return; if ((cover_urls.count() == 0) || (no >= cover_urls.count()) || (no < 0)) { emit nothingFetched(); return; } qDebug() << "fetching cover..."; _status = FETCHING_COVER; emit statusChanged(FETCHING_COVER); job = KIO::storedGet(QUrl(cover_urls[no])); connect(job, SIGNAL(result(KJob *)), SLOT(fetched_html_data(KJob *))); } const QByteArray CoverFetcher::thumbnail(int index) { if ((index < 0) || (index >= cover_thumbnails.count())) return QByteArray(); return cover_thumbnails[index]; } const QString CoverFetcher::caption(int index) { if ((index < 0) || (index >= cover_names.count())) return QString(); return cover_names[index]; } void CoverFetcher::fetched_html_data(KJob *job) { QByteArray buffer; if (job && job->error()) { qDebug() << "There was an error communicating with Google. " << job->errorString(); emit error(i18n("There was an error communicating with Google."), i18n("Try again later. Otherwise make a bug report.")); _status = NOS; emit statusChanged(NOS); emit nothingFetched(); return; } if (job) { auto *const storedJob = static_cast(job); buffer = storedJob->data(); } if (buffer.count() == 0) { qDebug() << "Google server: empty response"; emit error(i18n("Google server: Empty response."), i18n("Try again later. Make a bug report.")); _status = NOS; emit statusChanged(NOS); return; } switch (_status) { case SEARCHING: { qDebug() << "searching finished."; // qDebug() << QString::fromUtf8(buffer.data()); parse_html_response(QString::fromUtf8(buffer.data())); _status = NOS; emit statusChanged(NOS); fetch_cover_thumbnail(); } break; case FETCHING_THUMBNAIL: { qDebug() << "cover thumbnail fetched."; cover_thumbnails.append(buffer); emit fetchedThumbnail(buffer, cover_names[f_i], f_i + 1); ++f_i; if (((fetch_no > -1) && (f_i == fetch_no)) || (cover_urls_thumbnails.count() == 0)) { _status = NOS; emit statusChanged(NOS); f_i = 0; emit allCoverThumbnailsFetched(); } else { fetch_cover_thumbnail(); } } break; case FETCHING_COVER: { qDebug() << "cover fetched."; _status = NOS; emit statusChanged(NOS); emit fetchedCover(buffer); } break; case NOS: break; } } void CoverFetcher::parse_html_response(const QString &xml) { cover_urls_thumbnails.clear(); cover_urls.clear(); cover_names.clear(); cover_tbnids.clear(); cover_thumbnails.clear(); QScriptValue responseData; QScriptEngine engine; responseData = engine.evaluate('(' + xml + ')'); QScriptValue resultsData = responseData.property("responseData").property("results"); if (resultsData.isArray()) { QScriptValueIterator it(resultsData); while (it.hasNext()) { it.next(); if (it.flags() & QScriptValue::SkipInEnumeration) continue; QScriptValue entry = it.value(); - QString link = QUrl::fromPercentEncoding(entry.property("url").toString().toAscii()); - QString thumbUrl = QUrl::fromPercentEncoding(entry.property("tbUrl").toString().toAscii()); + QString link = QUrl::fromPercentEncoding(entry.property("url").toString().toLatin1()); + QString thumbUrl = QUrl::fromPercentEncoding(entry.property("tbUrl").toString().toLatin1()); QString w = entry.property("width").toString(); QString h = entry.property("height").toString(); cover_urls << link; cover_names << i18n("%1x%2", w, h); cover_urls_thumbnails << thumbUrl; qDebug() << "URL " << link << "- " << thumbUrl << " -" << cover_names; } } } bool CoverFetcher::fetch_cover_thumbnail() { if (cover_urls_thumbnails.count() == 0) { qDebug() << "nothing fetched."; emit nothingFetched(); return false; } _status = FETCHING_THUMBNAIL; emit statusChanged(FETCHING_THUMBNAIL); job = KIO::storedGet(QUrl(cover_urls_thumbnails.takeFirst())); connect(job, SIGNAL(result(KJob *)), SLOT(fetched_html_data(KJob *))); return true; } diff --git a/utils/patternparser.cpp b/utils/patternparser.cpp index 7183b3b..506c730 100644 --- a/utils/patternparser.cpp +++ b/utils/patternparser.cpp @@ -1,779 +1,779 @@ /* AUDEX CDDA EXTRACTOR * Copyright (C) 2007-2015 Marco Nelles (audex@maniatek.com) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "patternparser.h" #include #include SaxHandler::SaxHandler() : QXmlDefaultHandler() { trackno = 1; cdno = 0; trackoffset = 0; fat32compatible = false; discid = 0; size = 0; length = 0; nooftracks = 0; is_filename_pattern = false; is_command_pattern = false; is_simple_pattern = false; is_text_pattern = false; cover = nullptr; /*TEMP*/ found_suffix = false; } SaxHandler::~SaxHandler() { } bool SaxHandler::startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) { Q_UNUSED(namespaceURI); Q_UNUSED(localName); if (qName == VAR_FILENAME_PATTERN) { is_filename_pattern = true; return true; } if (qName == VAR_COMMAND_PATTERN) { is_command_pattern = true; return true; } if (qName == VAR_SIMPLE_PATTERN) { is_simple_pattern = true; return true; } if (qName == VAR_TEXT_PATTERN) { is_text_pattern = true; return true; } p_element.clear(); if (qName == VAR_ALBUM_ARTIST) { if ((is_filename_pattern) || (is_simple_pattern)) { QString s = artist; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); // int QString::toInt(bool *ok, int base) const // If a conversion error occurs, *\a{ok} is set to \c false; otherwise // *\a{ok} is set to \c true. // http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qstring.cpp#n6418 bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(artist); } else { p_element += artist; } } if (qName == VAR_ALBUM_TITLE) { if ((is_filename_pattern) || (is_simple_pattern)) { QString s = title; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(title); } else { p_element += title; } } if (qName == VAR_DATE) { if ((is_filename_pattern) || (is_simple_pattern)) { QString s = date; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(date); } else { p_element += date; } } if (qName == VAR_GENRE) { if ((is_filename_pattern) || (is_simple_pattern)) { QString s = genre; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(genre); } else { p_element += genre; } } if (qName == VAR_ENCODER) { p_element += encoder; } if ((is_filename_pattern) || (is_command_pattern) || (is_simple_pattern)) { if (qName == VAR_CD_NO) { if (cdno > 0) { bool ok; int l = atts.value("length").toInt(&ok); QChar fc = '0'; if (!atts.value("fillchar").isEmpty()) fc = atts.value("fillchar").at(0); if (ok) p_element += QString("%1").arg(cdno, l, 10, fc); else p_element += QString("%1").arg(cdno); } } } if ((is_filename_pattern) || (is_command_pattern)) { if (qName == VAR_TRACK_ARTIST) { if (is_filename_pattern) { QString s = tartist; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(tartist); } else { p_element += tartist; } } if (qName == VAR_TRACK_TITLE) { if (is_filename_pattern) { QString s = ttitle; if ((fat32compatible) || (IS_TRUE(atts.value("fat32compatible")))) s = make_fat32_compatible(s); else s = make_compatible(s); if ((replacespaceswithunderscores) || (IS_TRUE(atts.value("underscores")))) s = replace_spaces_with_underscores(s); bool ok; int left = atts.value("left").toInt(&ok); if ((ok) && (left > 0)) s = s.left(left); if (IS_TRUE(atts.value("replace_char_list"))) s = replace_char_list(atts, s); if (IS_TRUE(atts.value("lowercase"))) s = s.toLower(); else if (IS_TRUE(atts.value("uppercase"))) s = s.toUpper(); p_element += s; } else if (is_command_pattern) { p_element += make_compatible_2(ttitle); } else { p_element += ttitle; } } if (qName == VAR_TRACK_NO) { int t; if (trackoffset > 1) t = trackno + trackoffset; else t = trackno; bool ok; int l = atts.value("length").toInt(&ok); QChar fc = '0'; if (!atts.value("fillchar").isEmpty()) fc = atts.value("fillchar").at(0); if (ok) { p_element += QString("%1").arg(t, l, 10, fc); } else { if (_2digitstracknum) { p_element += QString("%1").arg(t, 2, 10, QChar('0')); } else { p_element += QString("%1").arg(t); } } } } if ((is_filename_pattern) || (is_simple_pattern)) { if (qName == VAR_SUFFIX) { /*TEMP*/ found_suffix = true; p_element += suffix; } } if (is_command_pattern) { if (qName == VAR_INPUT_FILE) p_element += "\"" + input + "\""; if (qName == VAR_OUTPUT_FILE) p_element += "\"" + output + "\""; if (qName == VAR_COVER_FILE) { QString format = STANDARD_EMBED_COVER_FORMAT; if (!atts.value("format").isEmpty()) format = atts.value("format"); // cover set by setCover - if ((cover) && (!cover->supportedFormats().contains(format.toAscii().toLower()))) + if ((cover) && (!cover->supportedFormats().contains(format.toLatin1().toLower()))) format = STANDARD_EMBED_COVER_FORMAT; QString filename; bool stop = false; if (demomode) { filename = tmppath + "audexcover.123." + format.toLower(); } else { int x = -1; int y = -1; bool ok; if (!atts.value("x").isEmpty()) { // when *ok is false, QString::toInt() often return 0 x = atts.value("x").toInt(&ok); if (!ok) x = -1; } if (!atts.value("y").isEmpty()) { y = atts.value("y").toInt(&ok); if (!ok) y = -1; } QByteArray ba = QCryptographicHash::hash(QString(artist + title + date + QString("%1").arg(x * y) + format).toUtf8(), QCryptographicHash::Md5); QString mda5 = ba.toHex(); if (!stop) filename = tmppath + "cover." + QString("%1").arg(mda5) + '.' + format.toLower(); QFileInfo finfo(filename); if ((!finfo.exists()) && (!stop)) { bool success; if ((!cover) || ((cover) && (cover->isEmpty()))) { if (IS_TRUE(atts.value("usenocover"))) { QImage c = QImage(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("audex/images/nocover.png"))); if ((x != -1) && (y != -1)) { c = c.scaled(x, y, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } - success = c.save(filename, format.toAscii()); + success = c.save(filename, format.toLatin1()); } else { stop = true; } } else { success = cover->save(filename, QSize(x, y)); } if (!stop) { if (!success) { qDebug() << "WARNING! Could not create temporary cover file" << filename; } else { qDebug() << "Successfully created temporary cover file" << filename << "(" << QFile(filename).size() / 1024 << "KiB)"; } } } } if (!stop) { p_element = "\"" + filename + "\""; } } } if (is_text_pattern) { if (qName == VAR_CD_SIZE) { QChar iec; if (!atts.value("iec").isEmpty()) iec = atts.value("iec").at(0); if ((iec != 'b') && (iec != 'k') && (iec != 'm') && (iec != 'g')) iec = 'm'; bool ok; int p = atts.value("precision").toInt(&ok); if (!ok) p = 2; if (iec == 'b') p_element += QString("%1").arg(size, 0, 'f', p); else if (iec == 'k') p_element += QString("%1").arg(size / 1024.0f, 0, 'f', p); else if (iec == 'm') p_element += QString("%1").arg(size / (1024.0f * 1024.0f), 0, 'f', p); else if (iec == 'g') p_element += QString("%1").arg(size / (1024.0f * 1024.0f * 1024.0f), 0, 'f', p); } if (qName == VAR_CD_LENGTH) p_element += QString("%1:%2").arg(length / 60, 2, 10, QChar('0')).arg(length % 60, 2, 10, QChar('0')); if (qName == VAR_TODAY) { QString format; if (!atts.value("format").isEmpty()) format = atts.value("format"); if (format.isEmpty()) { p_element += QString("%1").arg(QDate::currentDate().toString()); } else { p_element += QString("%1").arg(QDate::currentDate().toString(format)); } } if (qName == VAR_NOW) { QString format; if (!atts.value("format").isEmpty()) format = atts.value("format"); if (format.isEmpty()) { p_element += QString("%1").arg(QDateTime::currentDateTime().toString()); } else { p_element += QString("%1").arg(QDateTime::currentDateTime().toString(format)); } } if (qName == VAR_LINEBREAK) p_element += '\n'; if (qName == VAR_DISCID) { bool ok; int base = atts.value("base").toInt(&ok); if (!ok) base = 16; p_element += QString("%1").arg(discid, 0, base); } } if (qName == VAR_NO_OF_TRACKS) p_element += QString("%1").arg(nooftracks); if (qName == VAR_AUDEX) p_element += QString("Audex Version %1").arg(AUDEX_VERSION); if ((!p_element.isEmpty()) && (is_command_pattern)) { QString pre = atts.value("pre"); QString post = atts.value("post"); p_text += pre + p_element + post; } else { p_text += p_element; } return true; } bool SaxHandler::endElement(const QString &namespaceURI, const QString &localName, const QString &qName) { Q_UNUSED(namespaceURI); Q_UNUSED(localName); if (qName == VAR_FILENAME_PATTERN) { is_filename_pattern = false; p_text.replace("//", "/"); p_text = p_text.simplified(); return true; } if (qName == VAR_COMMAND_PATTERN) { is_command_pattern = false; p_text.replace("//", "/"); p_text = p_text.simplified(); return true; } if (qName == VAR_SIMPLE_PATTERN) { is_simple_pattern = false; p_text.replace("//", "/"); p_text = p_text.simplified(); return true; } if (qName == VAR_TEXT_PATTERN) { is_text_pattern = false; return true; } return true; } bool SaxHandler::characters(const QString &ch) { p_text += ch; return true; } bool SaxHandler::fatalError(const QXmlParseException &exception) { qDebug() << QString("XML pattern parse error: Column %1 (%2)").arg(exception.columnNumber()).arg(exception.message()); return false; } const QString SaxHandler::make_compatible(const QString &string) { QString s = string; for (int i = 0; i < s.size(); i++) { switch (s[i].toLatin1()) { case '/': case '\\': s[i] = '_'; break; case '"': s[i] = '\''; break; default: break; } } return s; } const QString SaxHandler::make_compatible_2(const QString &string) { QString s = string; s.replace('"', "\\\""); return s; } // remove \ / : * ? " < > | const QString SaxHandler::make_fat32_compatible(const QString &string) { QString s = string; for (int i = 0; i < s.size(); i++) { switch (s[i].toLatin1()) { case '\\': case '/': case ':': case '*': case '?': case '"': case '<': case '>': case '|': s[i] = '_'; break; default: break; } } return s; } const QString SaxHandler::replace_spaces_with_underscores(const QString &string) { QString s = string; s.replace(' ', '_'); return s; } const QString SaxHandler::replace_char_list(const QXmlAttributes &atts, const QString &string) { int i; QString from, to, result; qDebug() << "starting replacement for: " << string; from = atts.value("replace_char_list_from"); to = atts.value("replace_char_list_to"); if (from.count() != to.count()) { qDebug() << "Could not replace if list length are not equal"; return string; } result = string; for (i = 0; i < from.count(); i++) { result.replace(from.at(i), to.at(i)); } qDebug() << "finished: " << result; return result; } PatternParser::PatternParser(QObject *parent) : QObject(parent) { Q_UNUSED(parent); } PatternParser::~PatternParser() { } const QString PatternParser::parseFilenamePattern(const QString &pattern, int trackno, int cdno, int trackoffset, int nooftracks, const QString &artist, const QString &title, const QString &tartist, const QString &ttitle, const QString &date, const QString &genre, const QString &suffix, bool fat32compatible, bool replacespaceswithunderscores, bool _2digitstracknum) { SaxHandler handler; handler.setTrackNo(trackno); handler.setCDNo(cdno); handler.setTrackOffset(trackoffset); handler.setNoOfTracks(nooftracks); handler.setArtist(artist); handler.setTitle(title); handler.setTrackArtist(tartist); handler.setTrackTitle(ttitle); handler.setDate(date); handler.setGenre(genre); handler.setSuffix(suffix); handler.setFAT32Compatible(fat32compatible); handler.setReplaceSpacesWithUnderscores(replacespaceswithunderscores); handler.set2DigitsTrackNum(_2digitstracknum); QXmlInputSource inputSource; inputSource.setData("" + p_xmlize_pattern(pattern) + ""); QXmlSimpleReader reader; reader.setContentHandler(&handler); reader.setErrorHandler(&handler); reader.parse(inputSource); return handler.text(); } const QString PatternParser::parseCommandPattern(const QString &pattern, const QString &input, const QString &output, int trackno, int cdno, int trackoffset, int nooftracks, const QString &artist, const QString &title, const QString &tartist, const QString &ttitle, const QString &date, const QString &genre, const QString &suffix, CachedImage *cover, bool fatcompatible, const QString &tmppath, const QString &encoder, const bool demomode) { SaxHandler handler; handler.setInputFile(input); handler.setOutputFile(output); handler.setTrackNo(trackno); handler.setCDNo(cdno); handler.setTrackOffset(trackoffset); handler.setNoOfTracks(nooftracks); handler.setArtist(artist); handler.setTitle(title); handler.setTrackArtist(tartist); handler.setTrackTitle(ttitle); handler.setDate(date); handler.setGenre(genre); handler.setSuffix(suffix); // cover is initialized! handler.setCover(cover); handler.setFAT32Compatible(fatcompatible); handler.setTMPPath(tmppath); handler.setDemoMode(demomode); handler.set2DigitsTrackNum(false); handler.setEncoder(encoder); QXmlInputSource inputSource; inputSource.setData("" + p_xmlize_pattern(pattern) + ""); QXmlSimpleReader reader; reader.setContentHandler(&handler); reader.setErrorHandler(&handler); reader.parse(inputSource); return handler.text(); } const QString PatternParser::parseSimplePattern(const QString &pattern, int cdno, const int nooftracks, const QString &artist, const QString &title, const QString &date, const QString &genre, const QString &suffix, bool fat32compatible) { SaxHandler handler; handler.setCDNo(cdno); handler.setNoOfTracks(nooftracks); handler.setArtist(artist); handler.setTitle(title); handler.setDate(date); handler.setGenre(genre); handler.setSuffix(suffix); handler.setFAT32Compatible(fat32compatible); handler.setReplaceSpacesWithUnderscores(false); handler.set2DigitsTrackNum(false); QXmlInputSource inputSource; inputSource.setData("" + p_xmlize_pattern(pattern) + ""); QXmlSimpleReader reader; reader.setContentHandler(&handler); reader.setErrorHandler(&handler); reader.parse(inputSource); return handler.text(); } void PatternParser::parseInfoText(QStringList &text, const QString &artist, const QString &title, const QString &date, const QString &genre, const quint32 discid, const qreal size, const int length, const int nooftracks) { SaxHandler handler; handler.setArtist(artist); handler.setTitle(title); handler.setDate(date); handler.setGenre(genre); handler.setDiscid(discid); handler.setSize(size); handler.setLength(length); handler.setNoOfTracks(nooftracks); handler.set2DigitsTrackNum(false); QXmlInputSource inputSource; inputSource.setData("" + p_xmlize_pattern(text.join("\n")) + ""); QXmlSimpleReader reader; reader.setContentHandler(&handler); reader.setErrorHandler(&handler); reader.parse(inputSource); text = handler.text().split('\n'); } const QString PatternParser::p_xmlize_pattern(const QString &pattern) { QString newpattern; QString name; int s = 0; for (int i = 0; i < pattern.length(); ++i) { if (pattern[i] == '&') { newpattern += "&"; continue; } switch (s) { // outside var case 0: if (pattern[i] == '$') { name.clear(); s = 1; continue; } break; // inside var case 1: if (pattern[i] == '{') { s = 3; } else if (pattern[i] == '$') { newpattern += '$'; s = 0; } else { s = 2; name += pattern[i]; } continue; // inside simple var case 2: if (!pattern[i].isLetter()) { if (!name.trimmed().isEmpty()) newpattern += '<' + name + " />"; name.clear(); s = 0; if (pattern[i] == '$') { name.clear(); s = 1; continue; } else { newpattern += pattern[i]; } continue; } name += pattern[i]; continue; // inside extended var case 3: if (pattern[i] == '}') { if (!name.trimmed().isEmpty()) newpattern += '<' + name + " />"; name.clear(); s = 0; continue; } name += pattern[i]; continue; } newpattern += pattern[i]; } // rest at the end? if ((s == 2) && (!name.trimmed().isEmpty())) newpattern += '<' + name + " />"; return newpattern; }