File indexing completed on 2024-04-28 16:06:19

0001 /* AUDEX CDDA EXTRACTOR
0002  * SPDX-FileCopyrightText: Copyright (C) 2007 Marco Nelles
0003  * <https://userbase.kde.org/Audex>
0004  *
0005  * SPDX-License-Identifier: GPL-3.0-or-later
0006  */
0007 
0008 #ifndef PLACEHOLDERPARSER_H
0009 #define PLACEHOLDERPARSER_H
0010 
0011 #include <QDate>
0012 #include <QDateTime>
0013 #include <QDesktopServices>
0014 #include <QDialog>
0015 #include <QDir>
0016 #include <QFileInfo>
0017 #include <QImage>
0018 #include <QImageReader>
0019 #include <QLocale>
0020 #include <QMetaType>
0021 #include <QObject>
0022 #include <QTemporaryFile>
0023 
0024 #include <KLocalizedString>
0025 
0026 #include "config.h"
0027 #include "utils/parameters.h"
0028 
0029 #define VAR_ALBUM_ARTIST "artist"
0030 #define VAR_ALBUM_TITLE "title"
0031 #define VAR_TRACK_ARTIST "tartist"
0032 #define VAR_TRACK_TITLE "ttitle"
0033 #define VAR_TRACK_NO "trackno"
0034 #define VAR_CD_NO "cdno"
0035 #define VAR_DATE "date"
0036 #define VAR_GENRE "genre"
0037 #define VAR_ISRC "isrc"
0038 #define VAR_SUFFIX "suffix"
0039 #define VAR_ENCODER "encoder"
0040 
0041 #define VAR_INPUT_FILE "i"
0042 #define VAR_OUTPUT_FILE "o"
0043 #define VAR_COVER_FILE "cover"
0044 
0045 #define VAR_MCN "mcn"
0046 #define VAR_DISCID "discid"
0047 #define VAR_CD_SIZE "size"
0048 #define VAR_CD_LENGTH "length"
0049 #define VAR_TODAY "today"
0050 #define VAR_NOW "now"
0051 #define VAR_LINEBREAK "br"
0052 
0053 #define VAR_AUDEX "audex"
0054 #define VAR_NO_OF_TRACKS "nooftracks"
0055 
0056 #define STANDARD_EMBED_COVER_FORMAT "jpeg"
0057 #define STANDARD_EMBED_COVER_WIDTH 600
0058 
0059 typedef QMap<QString, QVariant> Placeholders;
0060 typedef QMap<QString, Parameters> PlaceholdersParameters;
0061 
0062 class SchemeParser : public QObject
0063 {
0064     Q_OBJECT
0065 public:
0066     explicit SchemeParser(QObject *parent = nullptr);
0067     ~SchemeParser() override;
0068 
0069     // placeholders_parameters: return the actually found placeholders with their parameters as QMap
0070     const QString parseScheme(const QString &scheme, const Placeholders &placeholders, PlaceholdersParameters *placeholders_parameters = nullptr);
0071 
0072     const QString parsePerTrackFilenameScheme(const QString &scheme,
0073                                               const int trackno,
0074                                               const int cdno,
0075                                               const int trackoffset,
0076                                               const int nooftracks,
0077                                               const QString &artist,
0078                                               const QString &title,
0079                                               const QString &tartist,
0080                                               const QString &ttitle,
0081                                               const QString &date,
0082                                               const QString &genre,
0083                                               const QString &isrc,
0084                                               const QString &suffix,
0085                                               const bool fat32compatible,
0086                                               const bool replacespaceswithunderscores,
0087                                               const bool twodigitstracknum);
0088 
0089     const QString parsePerTrackCommandScheme(const QString &scheme,
0090                                              const QString &input,
0091                                              const QString &output,
0092                                              const int trackno,
0093                                              const int cdno,
0094                                              const int trackoffset,
0095                                              const int nooftracks,
0096                                              const QString &artist,
0097                                              const QString &title,
0098                                              const QString &tartist,
0099                                              const QString &ttitle,
0100                                              const QString &date,
0101                                              const QString &genre,
0102                                              const QString &isrc,
0103                                              const QString &suffix,
0104                                              const QImage &cover,
0105                                              const QString &tmppath,
0106                                              const QString &encoder,
0107                                              const bool demomode = false);
0108 
0109     const QString parseFilenameScheme(const QString &scheme,
0110                                       const int cdno,
0111                                       const int nooftracks,
0112                                       const QString &artist,
0113                                       const QString &title,
0114                                       const QString &date,
0115                                       const QString &genre,
0116                                       const QString &suffix,
0117                                       const bool fat32compatible);
0118 
0119     void parseInfoTextScheme(QStringList &text,
0120                              const QString &artist,
0121                              const QString &title,
0122                              const QString &date,
0123                              const QString &genre,
0124                              const QString &mcn,
0125                              const quint32 discid,
0126                              const qreal size,
0127                              const int length,
0128                              const int nooftracks);
0129 
0130     inline bool error() const
0131     {
0132         return !p_error_string.isEmpty();
0133     }
0134 
0135     inline const QString errorString() const
0136     {
0137         return p_error_string;
0138     }
0139 
0140     // scheme: 1 == PerTrackFilename, 2 = PerTrackCommand, 3 == Filename, 4 == InfoText
0141     static const QString helpHTMLDoc(const int scheme)
0142     {
0143         QString result =
0144             "<html>"
0145             "<head>"
0146             "<style type=\"text/css\">"
0147             "* { font-size: 0.8em; }"
0148             "table { margin: 8px 0 8px; }"
0149             "table tr th { padding: 4px 6px; border-bottom: 1px solid; }"
0150             "table tr td { padding: 2px 2px; border-bottom: 1px dotted; }"
0151             "</style>"
0152             "</head>"
0153             "<body>";
0154 
0155         result.append(
0156             i18n("<p>The following placeholders will be replaced with their particular meaning:</p>"
0157                  "<table>"
0158                  "<tr><th>Placeholder</th><th>Description</th></tr>"
0159                  "<tr><td><tt>$artist</tt></td><td>The artist of the CD. If your CD is a compilation then this tag represents the title in most "
0160                  "cases.</td></tr>"
0161                  "<tr><td><tt>$title</tt></td><td>The title of the CD. If your CD is a compilation then this tag represents the subtitle in most "
0162                  "cases.</td></tr>"
0163                  "<tr><td><tt>$date</tt></td><td>The release date of the CD. In almost all cases this is the year.</td></tr>"
0164                  "<tr><td><tt>$genre</tt></td><td>The genre of the CD.</td></tr>"
0165                  "<tr><td><tt>$cdno</tt></td><td>The CD number of a multi-CD album. Often compilations consist of several CDs. <i>Note:</i> If the "
0166                  "multi-CD flag is <b>not</b> set for the current CD then this value will be empty.</td></tr>"
0167                  "<tr><td><tt>$nooftracks</tt></td><td>The total number of audio tracks of the CD.</td></tr>"
0168                  "<tr><td><tt>$encoder</tt></td><td>Encoder name and version.</td></tr>"
0169                  "<tr><td><tt>$audex</tt></td><td>Audex name and version.</td></tr>"
0170                  "</table>"));
0171 
0172         result.append(i18n(
0173             "<p>Placeholders in Audex can have parameters in the form key=value. E.g. <tt>${title lowercase=true}</tt>. In this example "
0174             "the title will be lowercased. The following general parameters can be used with placeholders:</p>"
0175             "<table>"
0176             "<tr><th>Key</th><th>Value</th><th>Description</th></tr>"
0177             "<tr><td><tt>lowercase</tt></td><td>true/false</td><td>The placeholder value will be lowercased.</td></tr>"
0178             "<tr><td><tt>uppercase</tt></td><td>true/false</td><td>The placeholder value will be uppercased.</td></tr>"
0179             "<tr><td><tt>underscores</tt></td><td>true/false</td><td>Replace the spaces of the placeholder value with underscores.</td></tr>"
0180             "<tr><td><tt>fat32compatible</tt></td><td>true/false</td><td>Replace illegal filename characters of a FAT32 filesystem with underscores.</td></tr>"
0181             "<tr><td><tt>replace_chars</tt></td><td>true/false</td><td>Replace characters. This parameter needs two additional keys of the same value size:"
0182             "<tt>replace_char_list_from</tt>, <tt>replace_char_list_to</tt>. These are lists of characters. The first character of"
0183             "<tt>replace_char_list_from</tt> will be replaced by the first character of <tt>replace_char_list_to</tt> and so on.</td></tr>"
0184             "<tr><td><tt>left</tt></td><td>Number</td><td>Take nth characters from the left of the placeholder value.</td></tr>"
0185             "<tr><td><tt>length</tt></td><td>Number</td><td>If the placeholder value is a number expand it to the given length. Furthermore you can define "
0186             "a fill character with the additional key <tt>fillchar</tt>. Default fillchar is '0'.</td></tr>"
0187             "<tr><td><tt>pre</tt></td><td>String</td><td>A string which will be placed <b>before</b> the value. Especially useful for command line parameters "
0188             "which should not be set at all if the value is empty.</td></tr>"
0189             "<tr><td><tt>post</tt></td><td>String</td><td>A string which will be placed <b>after</b> the value.</td></tr>"
0190             "</table>"));
0191 
0192         if (scheme == 1 || scheme == 2) {
0193             result.append(
0194                 i18n("<table>"
0195                      "<tr><th>Placeholder</th><th>Description</th></tr>"
0196                      "<tr><td>$tartist</td><td>This is the artist of every track. It is especially useful on compilation CDs.</td></tr>"
0197                      "<tr><td>$ttitle</td><td>The track title. Normally each track on a CD has its own title, which is the name of the song.</td></tr>"
0198                      "<tr><td>$trackno</td><td>The track number. First track is 1.</td></tr>"
0199                      "<tr><td><tt>$isrc</tt></td><td>The International Standard Recording Code (ISRC) of the track (only available if supported by your "
0200                      "device).</td></tr>"
0201                      "</table>"));
0202         }
0203 
0204         if (scheme == 2) {
0205             result.append(
0206                 i18n("<table>"
0207                      "<tr><th>Placeholder</th><th>Specific parameter</th><th>Description</th></tr>"
0208                      "<tr><td><tt>$i</tt></td><td></td><td>The temporary WAVE file (RIFF WAVE) created by Audex from CD audio track. This is the "
0209                      "input file for your command line encoder.</td></tr>"
0210                      "<tr><td><tt>$o</tt></td><td></td><td>The full output filename and path. Use it as the output for your command line encoder.</td></tr>"
0211                      "<tr><td><tt>$cover</tt></td><td><tt>format,x,y</tt></td><td>Filename of the cover file. If no cover is set, this will be empty. Mostly "
0212                      "useful in conjunction with the <tt>pre</tt> parameter. Image can be scaled with the <tt>x</tt> and <tt>y</tt> parameters. Possible image "
0213                      "<tt>formats</tt> are JPEG, PNG, GIF or BMP (Default: JPEG). <i><b>Note:</b> "
0214                      "LAME discards cover files larger than 128 KiB.</i></td></tr>"
0215                      "</table>"));
0216         }
0217 
0218         if (scheme == 4) {
0219             result.append(i18n(
0220                 "<table>"
0221                 "<tr><th>Placeholder</th><th>Specific parameter</th><th>Description</th></tr>"
0222                 "<tr><td>$size</td><td><tt>iec,precision</tt></td><td>Prints the overall size of all extracted (encoded) music files (incl. the cover image "
0223                 "file). With <tt>iec</tt> calculate (k)ibi, (m)ebi or (g)ibi. The additional parameter <tt>precision</tt> can define the number "
0224                 "of decimal places.</td></tr>"
0225                 "<tr><td>$length</td><td></td><td>Prints the relevant overall length of all extracted tracks. The format is min:sec.</td></tr>"
0226                 "<tr><td>$nooftracks</td><td></td><td>Prints the total number of extracted tracks.</td></tr>"
0227                 "<tr><td>$discid</td><td>base</td><td>Prints the CDDB discid in hexadecimal format.</td></tr>"
0228                 "<tr><td><tt>$mcn</tt></td><td></td><td>The Media Catalog Number (MCN) of the CD (only available if supported by your device).</td></tr>"
0229                 "<tr><td>$now</td><td><tt>format,locale</tt></td><td>Prints the current date and/or time. The parameter format specifies the output. "
0230                 "Please consult official qt documentation (<tt>https://doc.qt.io/qt-5/qtime.html#toString</tt>, "
0231                 "<tt>https://doc.qt.io/qt-6/qdate.html#toString</tt>) for the supported specifiers within the format string. With the additional key "
0232                 "<tt>locale</tt> you can specify a language setting. By default your "
0233                 "system locales are used. The locale has the format <tt>language_TERRITORY</tt>, e.g. <tt>en_EN</tt>. <tt>language</tt> is a lowercase, "
0234                 "two-letter, ISO 639 language code and <tt>TERRITORY</tt> an uppercase, two-letter, ISO 3166 territory code.</td></tr>"
0235                 "<tr><td>$br</td><td></td><td>Prints a linebreak.</td></tr>"
0236                 "</table>"));
0237         }
0238 
0239         result.append(
0240             "</body>"
0241             "</html>");
0242 
0243         return result;
0244     }
0245 
0246 Q_SIGNALS:
0247     void error(const QString &message, const QString &details = QString());
0248 
0249 private:
0250     const Parameters parse_keyvalue_stringlist(const QStringList &keyvaluepairs);
0251 
0252     QString p_error_string;
0253 
0254     inline const QString
0255     customize_placeholder_value(const QString &string, bool fat32compatible = false, bool compatible2 = true, bool replacespaceswithunderscores = false)
0256     {
0257         QString tmp = string;
0258         if (fat32compatible)
0259             tmp = make_fat32_compatible(tmp);
0260         else if (compatible2)
0261             tmp = make_compatible_2(tmp);
0262         else
0263             tmp = make_compatible(tmp);
0264         if (replacespaceswithunderscores)
0265             tmp = replace_spaces_with_underscores(tmp);
0266         return tmp;
0267     }
0268 
0269     const QString make_compatible(const QString &string);
0270     const QString make_compatible_2(const QString &string);
0271     const QString make_fat32_compatible(const QString &string);
0272     const QString replace_spaces_with_underscores(const QString &string);
0273     const QString replace_char_list(const QString &from, const QString &to, const QString &string);
0274 };
0275 
0276 #endif