2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QObject>
|
|
|
|
#include <QIODevice>
|
|
|
|
#include <QDir>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <QFileInfo>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QDateTime>
|
|
|
|
#include <QList>
|
|
|
|
#include <QString>
|
|
|
|
#include <QStringList>
|
2020-07-18 04:05:07 +02:00
|
|
|
#include <QRegularExpression>
|
2020-07-20 00:57:42 +02:00
|
|
|
#include <QRegularExpressionMatch>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <QTextStream>
|
|
|
|
#include <QtDebug>
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "core/logging.h"
|
|
|
|
#include "core/timeconstants.h"
|
2022-05-13 23:14:56 +02:00
|
|
|
#include "settings/playlistsettingspage.h"
|
|
|
|
#include "parserbase.h"
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "cueparser.h"
|
|
|
|
|
|
|
|
class CollectionBackendInterface;
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
const char *CueParser::kFileLineRegExp = "(\\S+)\\s+(?:\"([^\"]+)\"|(\\S+))\\s*(?:\"([^\"]+)\"|(\\S+))?";
|
2021-11-27 18:15:03 +01:00
|
|
|
const char *CueParser::kIndexRegExp = "(\\d{1,3}):(\\d{2}):(\\d{2})";
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
const char *CueParser::kPerformer = "performer";
|
|
|
|
const char *CueParser::kTitle = "title";
|
|
|
|
const char *CueParser::kSongWriter = "songwriter";
|
|
|
|
const char *CueParser::kFile = "file";
|
|
|
|
const char *CueParser::kTrack = "track";
|
|
|
|
const char *CueParser::kIndex = "index";
|
|
|
|
const char *CueParser::kAudioTrackType = "audio";
|
|
|
|
const char *CueParser::kRem = "rem";
|
|
|
|
const char *CueParser::kGenre = "genre";
|
|
|
|
const char *CueParser::kDate = "date";
|
|
|
|
const char *CueParser::kDisc = "discnumber";
|
|
|
|
|
|
|
|
CueParser::CueParser(CollectionBackendInterface *collection, QObject *parent)
|
|
|
|
: ParserBase(collection, parent) {}
|
|
|
|
|
2021-04-25 21:16:44 +02:00
|
|
|
SongList CueParser::Load(QIODevice *device, const QString &playlist_path, const QDir &dir, const bool collection_search) const {
|
2019-01-06 00:32:58 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
SongList ret;
|
|
|
|
|
|
|
|
QTextStream text_stream(device);
|
|
|
|
|
|
|
|
QString dir_path = dir.absolutePath();
|
|
|
|
// read the first line already
|
|
|
|
QString line = text_stream.readLine();
|
|
|
|
|
2021-03-21 06:26:48 +01:00
|
|
|
QList<CueEntry> entries;
|
2018-02-27 18:06:05 +01:00
|
|
|
int files = 0;
|
|
|
|
|
|
|
|
// -- whole file
|
|
|
|
while (!text_stream.atEnd()) {
|
|
|
|
|
|
|
|
QString album_artist;
|
|
|
|
QString album;
|
|
|
|
QString album_composer;
|
|
|
|
QString file;
|
|
|
|
QString file_type;
|
2020-04-25 13:47:25 +02:00
|
|
|
QString album_genre;
|
|
|
|
QString album_date;
|
2018-02-27 18:06:05 +01:00
|
|
|
QString disc;
|
|
|
|
|
|
|
|
// -- FILE section
|
|
|
|
do {
|
|
|
|
QStringList splitted = SplitCueLine(line);
|
|
|
|
|
|
|
|
// uninteresting or incorrect line
|
|
|
|
if (splitted.size() < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-13 23:25:44 +02:00
|
|
|
const QString &line_name = splitted[0];
|
|
|
|
const QString &line_value = splitted[1];
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-07-13 23:18:12 +02:00
|
|
|
if (line_name.compare(kPerformer, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
album_artist = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kTitle, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
album = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kSongWriter, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
album_composer = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kFile, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
file = QDir::isAbsolutePath(line_value) ? line_value : dir.absoluteFilePath(line_value);
|
|
|
|
if (splitted.size() > 2) {
|
|
|
|
file_type = splitted[2];
|
|
|
|
}
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kRem, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
if (splitted.size() < 3) {
|
|
|
|
break;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
if (line_value.compare(kGenre, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
album_genre = splitted[2];
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_value.compare(kDate, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
album_date = splitted[2];
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_value.compare(kDisc, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
disc = splitted[2];
|
|
|
|
}
|
2020-04-25 13:47:25 +02:00
|
|
|
}
|
|
|
|
// end of the header -> go into the track mode
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kTrack, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
files++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// just ignore the rest of possible field types for now...
|
2021-07-11 09:49:38 +02:00
|
|
|
} while (!(line = text_stream.readLine()).isNull());
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-07-11 09:49:38 +02:00
|
|
|
if (line.isNull()) {
|
2018-02-27 18:06:05 +01:00
|
|
|
qLog(Warning) << "the .cue file from " << dir_path << " defines no tracks!";
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is a data file, all of it's tracks will be ignored
|
2021-06-22 13:54:58 +02:00
|
|
|
bool valid_file = file_type.compare("BINARY", Qt::CaseInsensitive) != 0 && file_type.compare("MOTOROLA", Qt::CaseInsensitive) != 0;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QString track_type;
|
|
|
|
QString index;
|
|
|
|
QString artist;
|
|
|
|
QString composer;
|
|
|
|
QString title;
|
2020-08-13 21:14:12 +02:00
|
|
|
QString date;
|
|
|
|
QString genre;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// TRACK section
|
|
|
|
do {
|
|
|
|
QStringList splitted = SplitCueLine(line);
|
|
|
|
|
|
|
|
// uninteresting or incorrect line
|
|
|
|
if (splitted.size() < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-13 23:25:44 +02:00
|
|
|
const QString &line_name = splitted[0];
|
|
|
|
const QString &line_value = splitted[1];
|
2018-02-27 18:06:05 +01:00
|
|
|
QString line_additional = splitted.size() > 2 ? splitted[2].toLower() : "";
|
|
|
|
|
2021-07-13 23:18:12 +02:00
|
|
|
if (line_name.compare(kTrack, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// the beginning of another track's definition - we're saving the current one for later (if it's valid of course)
|
2018-02-27 18:06:05 +01:00
|
|
|
// please note that the same code is repeated just after this 'do-while' loop
|
2021-07-13 23:18:12 +02:00
|
|
|
if (valid_file && !index.isEmpty() && (track_type.isEmpty() || track_type.compare(kAudioTrackType, Qt::CaseInsensitive) == 0)) {
|
2020-08-13 21:14:12 +02:00
|
|
|
entries.append(CueEntry(file, index, title, artist, album_artist, album, composer, album_composer, (genre.isEmpty() ? album_genre : genre), (date.isEmpty() ? album_date : date), disc));
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// clear the state
|
2020-04-25 13:47:25 +02:00
|
|
|
track_type = index = artist = composer = title = date = genre = "";
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!line_additional.isEmpty()) {
|
|
|
|
track_type = line_additional;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kIndex, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// We need the index's position field
|
2018-02-27 18:06:05 +01:00
|
|
|
if (!line_additional.isEmpty()) {
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// If there's none "01" index, we'll just take the first one also, we'll take the "01" index even if it's the last one
|
2018-02-27 18:06:05 +01:00
|
|
|
if (line_value == "01" || index.isEmpty()) {
|
|
|
|
|
|
|
|
index = line_additional;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kTitle, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
title = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kDate, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
date = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kPerformer, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
artist = line_value;
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kSongWriter, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
composer = line_value;
|
2018-05-01 00:41:33 +02:00
|
|
|
// End of track's for the current file -> parse next one
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kRem, Qt::CaseInsensitive) == 0 && splitted.size() >= 3) {
|
|
|
|
if (line_value.compare(kGenre, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
genre = splitted[2];
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_value.compare(kDate, Qt::CaseInsensitive) == 0) {
|
2020-04-25 13:47:25 +02:00
|
|
|
date = splitted[2];
|
|
|
|
}
|
|
|
|
}
|
2021-07-13 23:18:12 +02:00
|
|
|
else if (line_name.compare(kFile, Qt::CaseInsensitive) == 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Just ignore the rest of possible field types for now...
|
2021-07-11 09:49:38 +02:00
|
|
|
} while (!(line = text_stream.readLine()).isNull());
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// We didn't add the last song yet...
|
2021-07-13 23:18:12 +02:00
|
|
|
if (valid_file && !index.isEmpty() && (track_type.isEmpty() || track_type.compare(kAudioTrackType, Qt::CaseInsensitive) == 0)) {
|
2020-04-25 13:47:25 +02:00
|
|
|
entries.append(CueEntry(file, index, title, artist, album_artist, album, composer, album_composer, (genre.isEmpty() ? album_genre : genre), (date.isEmpty() ? album_date : date), disc));
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime cue_mtime = QFileInfo(playlist_path).lastModified();
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Finalize parsing songs
|
2018-02-27 18:06:05 +01:00
|
|
|
for (int i = 0; i < entries.length(); i++) {
|
|
|
|
CueEntry entry = entries.at(i);
|
|
|
|
|
2021-04-25 21:16:44 +02:00
|
|
|
Song song = LoadSong(entry.file, IndexToMarker(entry.index), dir, collection_search);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Cue song has mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
|
2018-02-27 18:06:05 +01:00
|
|
|
if (cue_mtime.isValid()) {
|
2020-08-13 21:09:06 +02:00
|
|
|
song.set_mtime(qMax(cue_mtime.toSecsSinceEpoch(), song.mtime()));
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
song.set_cue_path(playlist_path);
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Overwrite the stuff, we may have read from the file or collection, using the current .cue metadata
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Set track number only in single-file mode
|
2018-02-27 18:06:05 +01:00
|
|
|
if (files == 1) {
|
|
|
|
song.set_track(i + 1);
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// The last TRACK for every FILE gets it's 'end' marker from the media file's length
|
2021-07-11 09:49:38 +02:00
|
|
|
if (i + 1 < entries.size() && entries.at(i).file == entries.at(i + 1).file) {
|
2018-02-27 18:06:05 +01:00
|
|
|
// incorrect indices?
|
|
|
|
if (!UpdateSong(entry, entries.at(i + 1).index, &song)) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-04-25 13:47:25 +02:00
|
|
|
}
|
|
|
|
else {
|
2018-02-27 18:06:05 +01:00
|
|
|
// incorrect index?
|
|
|
|
if (!UpdateLastSong(entry, &song)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret << song;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This and the kFileLineRegExp do most of the "dirty" work, namely: splitting the raw .cue
|
|
|
|
// line into logical parts and getting rid of all the unnecessary whitespaces and quoting.
|
2021-06-22 13:41:38 +02:00
|
|
|
QStringList CueParser::SplitCueLine(const QString &line) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2020-07-20 00:57:42 +02:00
|
|
|
QRegularExpression line_regexp(kFileLineRegExp);
|
|
|
|
QRegularExpressionMatch re_match = line_regexp.match(line.trimmed());
|
|
|
|
if (!re_match.hasMatch()) {
|
2018-02-27 18:06:05 +01:00
|
|
|
return QStringList();
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Let's remove the empty entries while we're at it
|
2020-12-07 22:43:00 +01:00
|
|
|
return re_match.capturedTexts().filter(QRegularExpression(".+")).mid(1, -1).replaceInStrings(QRegularExpression("^\"\"$"), "");
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Updates the song with data from the .cue entry. This one mustn't be used for the last song in the .cue file.
|
2021-06-22 13:41:38 +02:00
|
|
|
bool CueParser::UpdateSong(const CueEntry &entry, const QString &next_index, Song *song) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
qint64 beginning = IndexToMarker(entry.index);
|
|
|
|
qint64 end = IndexToMarker(next_index);
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Incorrect indices (we won't be able to calculate beginning or end)
|
2018-02-27 18:06:05 +01:00
|
|
|
if (beginning == -1 || end == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Believe the CUE: Init() forces validity
|
2018-02-27 18:06:05 +01:00
|
|
|
song->Init(entry.title, entry.PrettyArtist(), entry.album, beginning, end);
|
|
|
|
song->set_albumartist(entry.album_artist);
|
|
|
|
song->set_composer(entry.PrettyComposer());
|
|
|
|
song->set_genre(entry.genre);
|
2020-12-07 21:57:15 +01:00
|
|
|
|
|
|
|
int year = entry.date.toInt();
|
|
|
|
if (year > 0) song->set_year(year);
|
|
|
|
int disc = entry.disc.toInt();
|
|
|
|
if (disc > 0) song->set_disc(disc);
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Updates the song with data from the .cue entry. This one must be used only for the last song in the .cue file.
|
2021-06-22 13:41:38 +02:00
|
|
|
bool CueParser::UpdateLastSong(const CueEntry &entry, Song *song) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
qint64 beginning = IndexToMarker(entry.index);
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Incorrect index (we won't be able to calculate beginning)
|
2018-02-27 18:06:05 +01:00
|
|
|
if (beginning == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Believe the CUE and force validity (like UpdateSong() does)
|
2018-02-27 18:06:05 +01:00
|
|
|
song->set_valid(true);
|
|
|
|
|
|
|
|
song->set_title(entry.title);
|
|
|
|
song->set_artist(entry.PrettyArtist());
|
|
|
|
song->set_album(entry.album);
|
|
|
|
song->set_albumartist(entry.album_artist);
|
|
|
|
song->set_genre(entry.genre);
|
|
|
|
song->set_composer(entry.PrettyComposer());
|
2020-12-07 21:57:15 +01:00
|
|
|
|
|
|
|
int year = entry.date.toInt();
|
|
|
|
if (year > 0) song->set_year(year);
|
|
|
|
int disc = entry.disc.toInt();
|
|
|
|
if (disc > 0) song->set_disc(disc);
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// We don't do anything with the end here because it's already set to the end of the media file (if it exists)
|
2018-02-27 18:06:05 +01:00
|
|
|
song->set_beginning_nanosec(beginning);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-06-22 13:41:38 +02:00
|
|
|
qint64 CueParser::IndexToMarker(const QString &index) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2020-07-20 00:57:42 +02:00
|
|
|
QRegularExpression index_regexp(kIndexRegExp);
|
|
|
|
QRegularExpressionMatch re_match = index_regexp.match(index);
|
|
|
|
if (!re_match.hasMatch()) {
|
2018-02-27 18:06:05 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-20 00:57:42 +02:00
|
|
|
QStringList splitted = re_match.capturedTexts().mid(1, -1);
|
2021-03-21 18:53:02 +01:00
|
|
|
qint64 frames = splitted.at(0).toLongLong() * 60 * 75 + splitted.at(1).toLongLong() * 75 + splitted.at(2).toLongLong();
|
2018-02-27 18:06:05 +01:00
|
|
|
return (frames * kNsecPerSec) / 75;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-05-13 23:14:56 +02:00
|
|
|
void CueParser::Save(const SongList &songs, QIODevice *device, const QDir &dir, const PlaylistSettingsPage::PathType path_type) const {
|
2019-09-15 20:27:32 +02:00
|
|
|
|
|
|
|
Q_UNUSED(songs);
|
|
|
|
Q_UNUSED(device);
|
|
|
|
Q_UNUSED(dir);
|
|
|
|
Q_UNUSED(path_type);
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
// TODO
|
2019-09-15 20:27:32 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Looks for a track starting with one of the .cue's keywords.
|
|
|
|
bool CueParser::TryMagic(const QByteArray &data) const {
|
|
|
|
|
|
|
|
QStringList splitted = QString::fromUtf8(data.constData()).split('\n');
|
|
|
|
|
|
|
|
for (int i = 0; i < splitted.length(); i++) {
|
|
|
|
QString line = splitted.at(i).trimmed();
|
|
|
|
if (line.startsWith(kPerformer, Qt::CaseInsensitive) ||
|
|
|
|
line.startsWith(kTitle, Qt::CaseInsensitive) ||
|
|
|
|
line.startsWith(kFile, Qt::CaseInsensitive) ||
|
|
|
|
line.startsWith(kTrack, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
2021-11-27 20:28:00 +01:00
|
|
|
|
|
|
|
QString CueParser::FindCueFilename(const QString &filename) {
|
|
|
|
|
|
|
|
QStringList cue_files = QStringList() << filename + ".cue"
|
|
|
|
<< filename.section('.', 0, -2) + ".cue";
|
|
|
|
|
|
|
|
for (const QString &cuefile : cue_files) {
|
|
|
|
if (QFileInfo::exists(cuefile)) return cuefile;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
}
|