File indexing completed on 2024-05-19 04:55:55

0001 /**
0002  * \file importconfig.cpp
0003  * Configuration for import dialog.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 17 Sep 2003
0008  *
0009  * Copyright (C) 2003-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "importconfig.h"
0028 #include <QString>
0029 #include "isettings.h"
0030 #include "config.h"
0031 
0032 namespace {
0033 
0034 /**
0035  * Convert tag version to import destination value in configuration.
0036  * @param tagVersion tag version
0037  * @return value used in configuration, kept for backwards compatibility.
0038  */
0039 int tagVersionToImportDestCfg(Frame::TagVersion tagVersion) {
0040   return static_cast<int>(tagVersion) - 1;
0041 }
0042 
0043 /**
0044  * Convert import destination value in configuration to tag version.
0045  * @param importDest value used in configuration, kept for backwards
0046  *                   compatibility.
0047  * @return tag version.
0048  */
0049 Frame::TagVersion importDestCfgToTagVersion(int importDest) {
0050   return Frame::tagVersionCast(importDest + 1);
0051 }
0052 
0053 }
0054 
0055 int ImportConfig::s_index = -1;
0056 
0057 /**
0058  * Constructor.
0059  */
0060 ImportConfig::ImportConfig()
0061   : StoredConfig(QLatin1String("Import")), m_importServer(0),
0062     m_importDest(Frame::TagV1), m_importFormatIdx(0),
0063     m_maxTimeDifference(3),
0064     m_importVisibleColumns(0x2000000000ULL),
0065     m_importTagsIdx(0),
0066     m_pictureSourceIdx(0),
0067     m_enableTimeDifferenceCheck(true)
0068 {
0069   /**
0070    * Preset import format regular expressions.
0071    * The following codes are used before the () expressions.
0072    * %s %{title} title (song)
0073    * %l %{album} album
0074    * %a %{artist} artist
0075    * %c %{comment} comment
0076    * %y %{year} year
0077    * %t %{track} track, at least two digits
0078    * %T %{tracknumber} track number
0079    * %g %{genre} genre
0080    * %d %{duration} duration mm:ss
0081    * %D %{seconds} duration in seconds
0082    * %f %{file} file name
0083    * %p %{filepath} absolute file path
0084    * %u %{url} URL
0085    * %n %{tracks} number of tracks
0086    */
0087   m_importFormatNames.append(QLatin1String("CSV unquoted"));
0088   m_importFormatHeaders.append(QLatin1String(""));
0089   m_importFormatTracks.append(QLatin1String(
0090     R"(%{track}([^\r\n\t]*)\t%{title}([^\r\n\t]*)\t%{artist}([^\r\n\t]*)\t)"
0091     R"(%{album}([^\r\n\t]*)\t%{year}([^\r\n\t]*)\t%{genre}([^\r\n\t]*)\t)"
0092     R"(%{comment}([^\r\n\t]*)\t(?:\d+:)?%{duration}(\d+:\d+))"));
0093 
0094   m_importFormatNames.append(QLatin1String("CSV quoted"));
0095   m_importFormatHeaders.append(QLatin1String(""));
0096   // Using a raw string literal here causes clang to issue
0097   // "Unbalanced opening parenthesis in C++ code".
0098   m_importFormatTracks.append(QLatin1String(
0099     "\"?%{track}([^\\r\\n\\t\"]*)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t\"?"
0100     "%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?"
0101     "\\t\"?%{year}([^\\r\\n\\t\"]*)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?"
0102     "\\t\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t\"?(?:\\d+:)?%{duration}(\\d+:\\d+)"));
0103 
0104   m_importFormatNames.append(QLatin1String("CSV more unquoted"));
0105   m_importFormatHeaders.append(QLatin1String(""));
0106   m_importFormatTracks.append(QLatin1String(
0107     "%{track}([^\\r\\n\\t]*)\\t%{title}([^\\r\\n\\t]*)\\t%{artist}([^\\r\\n\\t]*)\\t"
0108     "%{album}([^\\r\\n\\t]*)\\t%{year}([^\\r\\n\\t]*)\\t%{genre}([^\\r\\n\\t]*)\\"
0109     "t%{comment}([^\\r\\n\\t]*)\\t(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\\t"
0110     "%{album artist}([^\\r\\n\\t]*)\\t%{arranger}([^\\r\\n\\t]*)\\t"
0111     "%{author}([^\\r\\n\\t]*)\\t%{bpm}([^\\r\\n\\t]*)\\t"
0112     "%{composer}([^\\r\\n\\t]*)\\t%{conductor}([^\\r\\n\\t]*)\\t"
0113     "%{copyright}([^\\r\\n\\t]*)\\t%{disc number}([^\\r\\n\\t]*)\\t"
0114     "%{encoded-by}([^\\r\\n\\t]*)\\t%{grouping}([^\\r\\n\\t]*)\\t%{isrc}([^\\r\\n\\t]*)\\t"
0115     "%{language}([^\\r\\n\\t]*)\\t%{lyricist}([^\\r\\n\\t]*)\\t%{lyrics}([^\\r\\n\\t]*)\\t"
0116     "%{media}([^\\r\\n\\t]*)\\t%{original album}([^\\r\\n\\t]*)\\t"
0117     "%{original artist}([^\\r\\n\\t]*)\\t%{original date}([^\\r\\n\\t]*)\\t"
0118     "%{part}([^\\r\\n\\t]*)\\t%{performer}([^\\r\\n\\t]*)\\t"
0119     "%{publisher}([^\\r\\n\\t]*)\\t%{remixer}([^\\r\\n\\t]*)\\t"
0120     "%{subtitle}([^\\r\\n\\t]*)\\t%{website}([^\\r\\n\\t]*)"));
0121 
0122   m_importFormatNames.append(QLatin1String("CSV more quoted"));
0123   m_importFormatHeaders.append(QLatin1String(""));
0124   m_importFormatTracks.append(QLatin1String(
0125     "\"?%{track}([^\\r\\n\\t\"]*)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t"
0126     "\"?%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?\\t"
0127     "\"?%{year}([^\\r\\n\\t\"]*)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?\\t"
0128     "\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t"
0129     "\"?(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\"?\\t"
0130     "\"?%{album artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{arranger}([^\\r\\n\\t\"]*)\"?\\t"
0131     "\"?%{author}([^\\r\\n\\t\"]*)\"?\\t\"?%{bpm}([^\\r\\n\\t\"]*)\"?\\t"
0132     "\"?%{composer}([^\\r\\n\\t\"]*)\"?\\t\"?%{conductor}([^\\r\\n\\t\"]*)\"?\\t"
0133     "\"?%{copyright}([^\\r\\n\\t\"]*)\"?\\t\"?%{disc number}([^\\r\\n\\t\"]*)\"?\\t"
0134     "\"?%{encoded-by}([^\\r\\n\\t\"]*)\"?\\t\"?%{grouping}([^\\r\\n\\t\"]*)\"?\\t"
0135     "\"?%{isrc}([^\\r\\n\\t\"]*)\"?\\t"
0136     "\"?%{language}([^\\r\\n\\t\"]*)\"?\\t\"?%{lyricist}([^\\r\\n\\t\"]*)\"?\\t"
0137     "\"?%{lyrics}([^\\r\\n\\t\"]*)\"?\\t"
0138     "\"?%{media}([^\\r\\n\\t\"]*)\"?\\t\"?%{original album}([^\\r\\n\\t\"]*)\"?\\t"
0139     "\"?%{original artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{original date}([^\\r\\n\\t\"]*)\"?\\t"
0140     "\"?%{part}([^\\r\\n\\t\"]*)\"?\\t\"?%{performer}([^\\r\\n\\t\"]*)\"?\\t"
0141     "\"?%{publisher}([^\\r\\n\\t\"]*)\"?\\t\"?%{remixer}([^\\r\\n\\t\"]*)\"?\\t"
0142     "\"?%{subtitle}([^\\r\\n\\t\"]*)\"?\\t\"?%{website}([^\\r\\n\\t\"]*)"));
0143 
0144   m_importFormatNames.append(QLatin1String("freedb HTML text"));
0145   m_importFormatHeaders.append(QLatin1String(
0146     R"(%{artist}(\S[^\r\n/]*\S)\s*/\s*%{album}(\S[^\r\n]*\S)[\r\n]+\s*)"
0147     R"(tracks:\s+\d+.*year:\s*%{year}([^\r\n\t]*)?.*)"
0148     R"(genre:\s*%{genre}(\S[^\r\n]*\S)?[\r\n])"));
0149   m_importFormatTracks.append(QLatin1String(
0150     R"([\r\n]%{track}(\d+)[\.\s]+%{duration}(\d+:\d+)\s+%{title}(\S[^\r\n]*\S))"));
0151 
0152   m_importFormatNames.append(QLatin1String("freedb HTML source"));
0153   m_importFormatHeaders.append(QLatin1String(
0154     R"(<[^>]+>%{artist}([^<\s][^\r\n/]*\S)\s*/\s*)"
0155     R"(%{album}(\S[^\r\n]*[^\s>])<[^>]+>[\r\n]+\s*)"
0156     R"(tracks:\s+\d+.*year:\s*%{year}([^\r\n\t]*)?.*)"
0157     R"(genre:\s*%{genre}(\S[^\r\n>]*\S)?<[^>]+>[\r\n])"));
0158   m_importFormatTracks.append(QLatin1String(
0159     R"(<td[^>]*>\s*%{track}(\d+).</td><td[^>]*>\s*%{duration}(\d+:\d+)</td>)"
0160     R"(<td[^>]*>(?:<[^>]+>)?%{title}([^<\r\n]+))"));
0161 
0162   m_importFormatNames.append(QLatin1String("Title"));
0163   m_importFormatHeaders.append(QLatin1String(""));
0164   m_importFormatTracks.append(QLatin1String(R"(\s*%{title}(\S[^\r\n]*\S)\s*)"));
0165 
0166   m_importFormatNames.append(QLatin1String("Track Title"));
0167   m_importFormatHeaders.append(QLatin1String(""));
0168   m_importFormatTracks.append(QLatin1String(
0169     R"(\s*%{track}(\d+)[\.\s]+%{title}(\S[^\r\n]*\S)\s*)"));
0170 
0171   m_importFormatNames.append(QLatin1String("Track Title Time"));
0172   m_importFormatHeaders.append(QLatin1String(""));
0173   m_importFormatTracks.append(QLatin1String(
0174     R"(\s*%{track}(\d+)[\.\s]+%{title}(\S[^\r\n]*\S)\s+%{duration}(\d+:\d+)\s*)"));
0175 
0176   m_importFormatNames.append(QLatin1String("Custom Format"));
0177   m_importFormatHeaders.append(QLatin1String(""));
0178   m_importFormatTracks.append(QLatin1String(""));
0179 
0180   m_importTagsNames.append(QLatin1String("Artist to Album Artist"));
0181   m_importTagsSources.append(QLatin1String("%{artist}"));
0182   m_importTagsExtractions.append(QLatin1String("%{albumartist}(.+)"));
0183 
0184   m_importTagsNames.append(QLatin1String("Album Artist to Artist"));
0185   m_importTagsSources.append(QLatin1String("%{albumartist}"));
0186   m_importTagsExtractions.append(QLatin1String("%{artist}(.+)"));
0187 
0188   m_importTagsNames.append(QLatin1String("Artist to Composer"));
0189   m_importTagsSources.append(QLatin1String("%{artist}"));
0190   m_importTagsExtractions.append(QLatin1String("%{composer}(.+)"));
0191 
0192   m_importTagsNames.append(QLatin1String("Artist to Conductor"));
0193   m_importTagsSources.append(QLatin1String("%{artist}"));
0194   m_importTagsExtractions.append(QLatin1String("%{conductor}(.+)"));
0195 
0196   m_importTagsNames.append(QLatin1String("Track Number from Title"));
0197   m_importTagsSources.append(QLatin1String("%{title}"));
0198   m_importTagsExtractions.append(
0199         QLatin1String(R"(\s*%{track}(\d+)[\.\s]+%{title}(\S.*\S)\s*)"));
0200 
0201   m_importTagsNames.append(QLatin1String("Track Number to Title"));
0202   m_importTagsSources.append(QLatin1String("%{track} %{title}"));
0203   m_importTagsExtractions.append(QLatin1String("%{title}(.+)"));
0204 
0205   m_importTagsNames.append(QLatin1String("Subtitle from Title"));
0206   m_importTagsSources.append(QLatin1String("%{title}"));
0207   m_importTagsExtractions.append(QLatin1String("%{subtitle}(.+) - "));
0208 
0209   m_importTagsNames.append(QLatin1String("Title Annotation to Comment"));
0210   m_importTagsSources.append(QLatin1String("%{title}"));
0211   m_importTagsExtractions.append(
0212         QLatin1String("%{title}(.+) +\\(%{comment}((?:Bonus|Remix)[^)]*)\\)"));
0213 
0214   m_importTagsNames.append(QLatin1String("Modification Date to Date"));
0215   m_importTagsSources.append(QLatin1String("%{modificationdate}"));
0216   m_importTagsExtractions.append(QLatin1String(R"(%{date}(\d\d\d\d-\d\d-\d\d))"));
0217 
0218   m_importTagsNames.append(QLatin1String("Filename to Original Filename"));
0219   m_importTagsSources.append(QLatin1String("%{file}"));
0220   m_importTagsExtractions.append(QLatin1String("%{TOFN}(.+)"));
0221 
0222   m_importTagsNames.append(QLatin1String("Custom Format"));
0223   m_importTagsSources.append(QLatin1String(""));
0224   m_importTagsExtractions.append(QLatin1String(""));
0225 
0226   m_pictureSourceNames.append(QLatin1String("Google Images"));
0227   m_pictureSourceUrls.append(QLatin1String(
0228     "http://www.google.com/search?tbm=isch&q=%u{artist}%20%u{album}"));
0229   m_pictureSourceNames.append(QLatin1String("Yahoo Images"));
0230   m_pictureSourceUrls.append(QLatin1String(
0231     "http://images.search.yahoo.com/search/images?ei=UTF-8&p=%u{artist}%20%u{album}"));
0232   m_pictureSourceNames.append(QLatin1String("Amazon"));
0233   m_pictureSourceUrls.append(QLatin1String(
0234     "http://www.amazon.com/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
0235   m_pictureSourceNames.append(QLatin1String("Amazon.co.uk"));
0236   m_pictureSourceUrls.append(QLatin1String(
0237     "http://www.amazon.co.uk/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
0238   m_pictureSourceNames.append(QLatin1String("Amazon.de"));
0239   m_pictureSourceUrls.append(QLatin1String(
0240     "http://www.amazon.de/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
0241   m_pictureSourceNames.append(QLatin1String("Amazon.fr"));
0242   m_pictureSourceUrls.append(QLatin1String(
0243     "http://www.amazon.fr/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
0244   m_pictureSourceNames.append(QLatin1String("MusicBrainz"));
0245   m_pictureSourceUrls.append(QLatin1String(
0246     "http://musicbrainz.org/search/textsearch.html?query=%u{artist}+%u{album}&type=release"));
0247   m_pictureSourceNames.append(QLatin1String("Discogs"));
0248   m_pictureSourceUrls.append(QLatin1String(
0249     "http://www.discogs.com/search?q=%u{artist}+%u{album}"));
0250   m_pictureSourceNames.append(QLatin1String("CD Universe"));
0251   m_pictureSourceUrls.append(QLatin1String(
0252     "http://www.cduniverse.com/sresult.asp?HT_Search_Info=%u{artist}+%u{album}"));
0253   m_pictureSourceNames.append(QLatin1String("Coveralia"));
0254   m_pictureSourceUrls.append(QLatin1String(
0255     "http://www.coveralia.com/mostrar.php?bus=%u{artist}%20%u{album}&bust=2"));
0256   m_pictureSourceNames.append(QLatin1String("SlothRadio"));
0257   m_pictureSourceUrls.append(QLatin1String(
0258     "http://www.slothradio.com/covers/?artist=%u{artist}&album=%u{album}"));
0259   m_pictureSourceNames.append(QLatin1String("Albumart"));
0260   m_pictureSourceUrls.append(QLatin1String(
0261     "http://www.albumart.org/index.php?srchkey=%u{artist}+%u{album}&searchindex=Music"));
0262   m_pictureSourceNames.append(QLatin1String("Custom Source"));
0263   m_pictureSourceUrls.append(QLatin1String(""));
0264 
0265   m_matchPictureUrlMap.append({
0266     {QLatin1String("https?://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*"),
0267      QLatin1String("\\1")},
0268     {QLatin1String("http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*"),
0269      QLatin1String("http%3A%2F%2F\\1")},
0270     {QLatin1String(
0271      "http://(?:www.)?amazon.(?:com|co.uk|de|fr).*/(?:dp|ASIN|images|product|-)/([A-Z0-9]+).*"),
0272      QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg")},
0273     {QLatin1String(
0274      "http://musicbrainz.org/misc/redirects/.*&asin=([A-Z0-9]+).*"),
0275      QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg")},
0276     {QLatin1String("(http://.*4shared.com/img/.*)"),
0277      QLatin1String("\\1.jpg")}
0278   });
0279 }
0280 
0281 /**
0282  * Persist configuration.
0283  *
0284  * @param config configuration
0285  */
0286 void ImportConfig::writeToConfig(ISettings* config) const
0287 {
0288   config->beginGroup(m_group);
0289   config->setValue(QLatin1String("ImportServer"),
0290                    QVariant(m_importServer));
0291   config->setValue(QLatin1String("ImportDestination"),
0292                    QVariant(tagVersionToImportDestCfg(m_importDest)));
0293   config->setValue(QLatin1String("ImportFormatNames"),
0294                    QVariant(m_importFormatNames));
0295   config->setValue(QLatin1String("ImportFormatHeaders"),
0296                    QVariant(m_importFormatHeaders));
0297   config->setValue(QLatin1String("ImportFormatTracks"),
0298                    QVariant(m_importFormatTracks));
0299   config->setValue(QLatin1String("ImportFormatIdx"),
0300                    QVariant(m_importFormatIdx));
0301   config->setValue(QLatin1String("EnableTimeDifferenceCheck"),
0302                    QVariant(m_enableTimeDifferenceCheck));
0303   config->setValue(QLatin1String("MaxTimeDifference"),
0304                    QVariant(m_maxTimeDifference));
0305 #ifdef Q_OS_MAC
0306   // Convince Mac OS X to store a 64-bit value.
0307   config->setValue(QLatin1String("ImportVisibleColumns"),
0308                    QVariant(m_importVisibleColumns | (Q_UINT64_C(1) << 63)));
0309 #else
0310   config->setValue(QLatin1String("ImportVisibleColumns"),
0311                    QVariant(m_importVisibleColumns));
0312 #endif
0313   config->setValue(QLatin1String("ImportTagsNames"),
0314                    QVariant(m_importTagsNames));
0315   config->setValue(QLatin1String("ImportTagsSources"),
0316                    QVariant(m_importTagsSources));
0317   config->setValue(QLatin1String("ImportTagsExtractions"),
0318                    QVariant(m_importTagsExtractions));
0319   config->setValue(QLatin1String("ImportTagsIdx"),
0320                    QVariant(m_importTagsIdx));
0321 
0322   config->setValue(QLatin1String("PictureSourceNames"),
0323                    QVariant(m_pictureSourceNames));
0324   config->setValue(QLatin1String("PictureSourceUrls"),
0325                    QVariant(m_pictureSourceUrls));
0326   config->setValue(QLatin1String("PictureSourceIdx"),
0327                    QVariant(m_pictureSourceIdx));
0328   QStringList keys, values;
0329   for (auto it = m_matchPictureUrlMap.constBegin();
0330        it != m_matchPictureUrlMap.constEnd();
0331        ++it) {
0332     keys.append(it->first);
0333     values.append(it->second);
0334   }
0335   config->setValue(QLatin1String("MatchPictureUrlMapKeys"), QVariant(keys));
0336   config->setValue(QLatin1String("MatchPictureUrlMapValues"), QVariant(values));
0337 
0338   config->setValue(QLatin1String("DisabledPlugins"),
0339                    QVariant(m_disabledPlugins));
0340   config->endGroup();
0341   config->beginGroup(m_group, true);
0342   config->setValue(QLatin1String("BrowseCoverArtWindowGeometry"),
0343                    QVariant(m_browseCoverArtWindowGeometry));
0344   config->setValue(QLatin1String("ImportWindowGeometry"),
0345                    QVariant(m_importWindowGeometry));
0346   config->endGroup();
0347 }
0348 
0349 /**
0350  * Read persisted configuration.
0351  *
0352  * @param config configuration
0353  */
0354 void ImportConfig::readFromConfig(ISettings* config)
0355 {
0356   QStringList tagsNames, tagsSources, tagsExtractions;
0357   QStringList picNames, picUrls;
0358 
0359   config->beginGroup(m_group);
0360   m_importServer = config->value(QLatin1String("ImportServer"),
0361                                  m_importServer).toInt();
0362   m_importDest = importDestCfgToTagVersion(
0363     config->value(QLatin1String("ImportDestination"),
0364                   tagVersionToImportDestCfg(m_importDest)).toInt());
0365   QStringList names = config->value(QLatin1String("ImportFormatNames"),
0366                                     m_importFormatNames).toStringList();
0367   QStringList headers = config->value(QLatin1String("ImportFormatHeaders"),
0368                                       m_importFormatHeaders).toStringList();
0369   QStringList tracks = config->value(QLatin1String("ImportFormatTracks"),
0370                                      m_importFormatTracks).toStringList();
0371   m_importFormatIdx = config->value(QLatin1String("ImportFormatIdx"),
0372                                     m_importFormatIdx).toInt();
0373   m_enableTimeDifferenceCheck =
0374       config->value(QLatin1String("EnableTimeDifferenceCheck"),
0375                     m_enableTimeDifferenceCheck).toBool();
0376   m_maxTimeDifference = config->value(QLatin1String("MaxTimeDifference"),
0377                                       m_maxTimeDifference).toInt();
0378   m_importVisibleColumns = config->value(QLatin1String("ImportVisibleColumns"),
0379                                          m_importVisibleColumns).toULongLong();
0380 #ifdef Q_OS_MAC
0381   m_importVisibleColumns &= ~(Q_UINT64_C(1) << 63);
0382 #endif
0383   tagsNames = config->value(QLatin1String("ImportTagsNames"),
0384                             m_importTagsNames).toStringList();
0385   tagsSources = config->value(QLatin1String("ImportTagsSources"),
0386                               m_importTagsSources).toStringList();
0387   tagsExtractions = config->value(QLatin1String("ImportTagsExtractions"),
0388                                   m_importTagsExtractions).toStringList();
0389   m_importTagsIdx = config->value(QLatin1String("ImportTagsIdx"),
0390                                   m_importTagsIdx).toInt();
0391 
0392   picNames = config->value(QLatin1String("PictureSourceNames"),
0393                            m_pictureSourceNames).toStringList();
0394   picUrls = config->value(QLatin1String("PictureSourceUrls"),
0395                           m_pictureSourceUrls).toStringList();
0396   m_pictureSourceIdx = config->value(
0397     QLatin1String("PictureSourceIdx"), m_pictureSourceIdx).toInt();
0398   QStringList keys = config->value(QLatin1String("MatchPictureUrlMapKeys"),
0399                                    QStringList()).toStringList();
0400   if (QStringList values = config->value(
0401         QLatin1String("MatchPictureUrlMapValues"), QStringList()).toStringList();
0402       !keys.empty() && !values.empty()) {
0403     m_matchPictureUrlMap.clear();
0404     for (auto itk = keys.constBegin(), itv = values.constBegin();
0405          itk != keys.constEnd() && itv != values.constEnd();
0406          ++itk, ++itv) {
0407       m_matchPictureUrlMap.append({*itk, *itv});
0408     }
0409   }
0410 
0411   m_disabledPlugins = config->value(QLatin1String("DisabledPlugins"),
0412                                  m_disabledPlugins).toStringList();
0413   config->endGroup();
0414   config->beginGroup(m_group, true);
0415   m_browseCoverArtWindowGeometry = config->value(
0416         QLatin1String("BrowseCoverArtWindowGeometry"),
0417         m_browseCoverArtWindowGeometry).toByteArray();
0418   m_importWindowGeometry = config->value(QLatin1String("ImportWindowGeometry"),
0419                                          m_importWindowGeometry).toByteArray();
0420   config->endGroup();
0421 
0422   // KConfig seems to strip empty entries from the end of the string lists,
0423   // so we have to append them again.
0424   const int numNames = names.size();
0425   while (headers.size() < numNames) headers.append(QLatin1String(""));
0426   while (tracks.size() < numNames) tracks.append(QLatin1String(""));
0427   const int numPicNames = picNames.size();
0428   while (picUrls.size() < numPicNames) picUrls.append(QLatin1String(""));
0429 
0430   /* Use defaults if no configuration found */
0431   for (auto namesIt = names.constBegin(), headersIt = headers.constBegin(),
0432          tracksIt = tracks.constBegin();
0433        namesIt != names.constEnd() && headersIt != headers.constEnd() &&
0434          tracksIt != tracks.constEnd();
0435        ++namesIt, ++headersIt, ++tracksIt) {
0436     if (int idx = m_importFormatNames.indexOf(*namesIt); idx >= 0) {
0437       m_importFormatHeaders[idx] = *headersIt;
0438       m_importFormatTracks[idx] = *tracksIt;
0439     } else if (!namesIt->isEmpty()) {
0440       m_importFormatNames.append(*namesIt);
0441       m_importFormatHeaders.append(*headersIt);
0442       m_importFormatTracks.append(*tracksIt);
0443     }
0444   }
0445 
0446   for (auto tagsNamesIt = tagsNames.constBegin(), sourcesIt = tagsSources.constBegin(),
0447          extractionsIt = tagsExtractions.constBegin();
0448        tagsNamesIt != tagsNames.constEnd() && sourcesIt != tagsSources.constEnd() &&
0449          extractionsIt != tagsExtractions.constEnd();
0450        ++tagsNamesIt, ++sourcesIt, ++extractionsIt) {
0451     if (int idx = m_importTagsNames.indexOf(*tagsNamesIt); idx >= 0) {
0452       m_importTagsSources[idx] = *sourcesIt;
0453       m_importTagsExtractions[idx] = *extractionsIt;
0454     } else if (!tagsNamesIt->isEmpty()) {
0455       m_importTagsNames.append(*tagsNamesIt);
0456       m_importTagsSources.append(*sourcesIt);
0457       m_importTagsExtractions.append(*extractionsIt);
0458     }
0459   }
0460 
0461   for (auto picNamesIt = picNames.constBegin(), picUrlsIt = picUrls.constBegin();
0462        picNamesIt != picNames.constEnd() && picUrlsIt != picUrls.constEnd();
0463        ++picNamesIt, ++picUrlsIt) {
0464     if (int idx = m_pictureSourceNames.indexOf(*picNamesIt); idx >= 0) {
0465       m_pictureSourceUrls[idx] = *picUrlsIt;
0466     } else if (!picNamesIt->isEmpty()) {
0467       m_pictureSourceNames.append(*picNamesIt);
0468       m_pictureSourceUrls.append(*picUrlsIt);
0469     }
0470   }
0471 
0472   if (m_importFormatIdx >= m_importFormatNames.size())
0473     m_importFormatIdx = 0;
0474   if (m_importTagsIdx >= m_importTagsNames.size())
0475     m_importTagsIdx = 0;
0476   if (m_pictureSourceIdx >=  m_pictureSourceNames.size())
0477     m_pictureSourceIdx = 0;
0478 
0479   // Replace mappings which do no longer work.
0480   if (m_pictureSourceUrls.removeOne(QLatin1String(
0481       "http://cdbaby.com/found?artist=%u{artist}&album=%u{album}"))) {
0482     m_pictureSourceNames.removeOne(QLatin1String("CD Baby"));
0483   }
0484   if (m_pictureSourceUrls.removeOne(QLatin1String(
0485       "http://www.jamendo.com/en/search/all/%u{artist}%20%u{album}"))) {
0486     m_pictureSourceNames.removeOne(QLatin1String("Jamendo"));
0487   }
0488   m_pictureSourceUrls.replaceInStrings(
0489         QLatin1String("http://images.google.com/images?q=%u{artist}%20%u{album}"),
0490         QLatin1String("http://www.google.com/search?tbm=isch&q=%u{artist}%20%u{album}"));
0491   for (auto it = m_matchPictureUrlMap.begin(); it != m_matchPictureUrlMap.end();) {
0492     if (it->first == QLatin1String(
0493           "http://images.google.com/.*imgurl=([^&]+)&.*") ||
0494         it->first == QLatin1String(
0495           "http://www.google.com/.*imgurl=([^&]+)&.*") ||
0496         it->first == QLatin1String(
0497           "http://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*")) {
0498       *it = {QLatin1String("https?://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*"),
0499              QLatin1String("\\1")};
0500     } else if (it->first == QLatin1String(
0501                  "http://rds.yahoo.com/.*&imgurl=([^&]+)&.*")) {
0502       *it = {QLatin1String(
0503              "http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*"),
0504              QLatin1String("http%3A%2F%2F\\1")};
0505     } else if (it->first == QLatin1String(
0506                  "http://rds.yahoo.com/.*%26imgurl=((?:[^%]|%(?!26))+).*") ||
0507                it->first == QLatin1String(
0508                  R"(http://cdbaby.com/cd/(\w)(\w)(\w+))") ||
0509                it->first == QLatin1String(
0510                  "http://www.jamendo.com/en/album/(\\d+)")) {
0511       it = m_matchPictureUrlMap.erase(it);
0512       continue;
0513     }
0514     ++it;
0515   }
0516 }
0517 
0518 void ImportConfig::setAvailablePlugins(const QStringList& availablePlugins)
0519 {
0520   if (m_availablePlugins != availablePlugins) {
0521     m_availablePlugins = availablePlugins;
0522     emit availablePluginsChanged(m_availablePlugins);
0523   }
0524 }
0525 
0526 void ImportConfig::setImportServer(int importServer)
0527 {
0528   if (m_importServer != importServer) {
0529     m_importServer = importServer;
0530     emit importServerChanged(m_importServer);
0531   }
0532 }
0533 
0534 void ImportConfig::setImportDest(Frame::TagVersion importDest)
0535 {
0536   if (m_importDest != importDest) {
0537     m_importDest = importDest;
0538     emit importDestChanged(m_importDest);
0539   }
0540 }
0541 
0542 void ImportConfig::setImportFormatNames(const QStringList& importFormatNames)
0543 {
0544   if (m_importFormatNames != importFormatNames) {
0545     m_importFormatNames = importFormatNames;
0546     emit importFormatNamesChanged(m_importFormatNames);
0547   }
0548 }
0549 
0550 void ImportConfig::setImportFormatHeaders(const QStringList& importFormatHeaders)
0551 {
0552   if (m_importFormatHeaders != importFormatHeaders) {
0553     m_importFormatHeaders = importFormatHeaders;
0554     emit importFormatHeadersChanged(m_importFormatHeaders);
0555   }
0556 }
0557 
0558 void ImportConfig::setImportFormatTracks(const QStringList& importFormatTracks)
0559 {
0560   if (m_importFormatTracks != importFormatTracks) {
0561     m_importFormatTracks = importFormatTracks;
0562     emit importFormatTracksChanged(m_importFormatTracks);
0563   }
0564 }
0565 
0566 void ImportConfig::setImportFormatIndex(int importFormatIndex)
0567 {
0568   if (m_importFormatIdx != importFormatIndex) {
0569     m_importFormatIdx = importFormatIndex;
0570     emit importFormatIndexChanged(m_importFormatIdx);
0571   }
0572 }
0573 
0574 void ImportConfig::setMaxTimeDifference(int maxTimeDifference)
0575 {
0576   if (m_maxTimeDifference != maxTimeDifference) {
0577     m_maxTimeDifference = maxTimeDifference;
0578     emit maxTimeDifferenceChanged(m_maxTimeDifference);
0579   }
0580 }
0581 
0582 void ImportConfig::setImportVisibleColumns(quint64 importVisibleColumns)
0583 {
0584   if (m_importVisibleColumns != importVisibleColumns) {
0585     m_importVisibleColumns = importVisibleColumns;
0586     emit importVisibleColumnsChanged(m_importVisibleColumns);
0587   }
0588 }
0589 
0590 void ImportConfig::setImportWindowGeometry(const QByteArray& importWindowGeometry)
0591 {
0592   if (m_importWindowGeometry != importWindowGeometry) {
0593     m_importWindowGeometry = importWindowGeometry;
0594     emit importWindowGeometryChanged(m_importWindowGeometry);
0595   }
0596 }
0597 
0598 void ImportConfig::setImportTagsNames(const QStringList& importTagsNames)
0599 {
0600   if (m_importTagsNames != importTagsNames) {
0601     m_importTagsNames = importTagsNames;
0602     emit importTagsNamesChanged(m_importTagsNames);
0603   }
0604 }
0605 
0606 void ImportConfig::setImportTagsSources(const QStringList& importTagsSources)
0607 {
0608   if (m_importTagsSources != importTagsSources) {
0609     m_importTagsSources = importTagsSources;
0610     emit importTagsSourcesChanged(m_importTagsSources);
0611   }
0612 }
0613 
0614 void ImportConfig::setImportTagsExtractions(const QStringList& importTagsExtractions)
0615 {
0616   if (m_importTagsExtractions != importTagsExtractions) {
0617     m_importTagsExtractions = importTagsExtractions;
0618     emit importTagsExtractionsChanged(m_importTagsExtractions);
0619   }
0620 }
0621 
0622 void ImportConfig::setImportTagsIndex(int importTagsIndex)
0623 {
0624   if (m_importTagsIdx != importTagsIndex) {
0625     m_importTagsIdx = importTagsIndex;
0626     emit importTagsIndexChanged(m_importTagsIdx);
0627   }
0628 }
0629 
0630 void ImportConfig::setPictureSourceNames(const QStringList& pictureSourceNames)
0631 {
0632   if (m_pictureSourceNames != pictureSourceNames) {
0633     m_pictureSourceNames = pictureSourceNames;
0634     emit pictureSourceNamesChanged(m_pictureSourceNames);
0635   }
0636 }
0637 
0638 void ImportConfig::setPictureSourceUrls(const QStringList& pictureSourceUrls)
0639 {
0640   if (m_pictureSourceUrls != pictureSourceUrls) {
0641     m_pictureSourceUrls = pictureSourceUrls;
0642     emit pictureSourceUrlsChanged(m_pictureSourceUrls);
0643   }
0644 }
0645 
0646 
0647 void ImportConfig::setPictureSourceIndex(int pictureSourceIndex)
0648 {
0649   if (m_pictureSourceIdx != pictureSourceIndex) {
0650     m_pictureSourceIdx = pictureSourceIndex;
0651     emit pictureSourceIndexChanged(m_pictureSourceIdx);
0652   }
0653 }
0654 
0655 void ImportConfig::setBrowseCoverArtWindowGeometry(const QByteArray& browseCoverArtWindowGeometry)
0656 {
0657   if (m_browseCoverArtWindowGeometry != browseCoverArtWindowGeometry) {
0658     m_browseCoverArtWindowGeometry = browseCoverArtWindowGeometry;
0659     emit browseCoverArtWindowGeometryChanged(m_browseCoverArtWindowGeometry);
0660   }
0661 }
0662 
0663 void ImportConfig::setMatchPictureUrlMap(const QList<QPair<QString, QString>>& matchPictureUrlMap)
0664 {
0665   if (m_matchPictureUrlMap != matchPictureUrlMap) {
0666     m_matchPictureUrlMap = matchPictureUrlMap;
0667     emit matchPictureUrlMapChanged(m_matchPictureUrlMap);
0668   }
0669 }
0670 
0671 QStringList ImportConfig::matchPictureUrlStringList() const
0672 {
0673   QStringList lst;
0674   QList<QPair<QString, QString>> urlMap = matchPictureUrlMap();
0675   for (auto it = urlMap.constBegin(); it != urlMap.constEnd(); ++it) {
0676     lst.append(it->first);
0677     lst.append(it->second);
0678   }
0679   return lst;
0680 }
0681 
0682 void ImportConfig::setMatchPictureUrlStringList(const QStringList& lst)
0683 {
0684   QList<QPair<QString, QString>> urlMap;
0685   auto it = lst.constBegin();
0686   while (it != lst.constEnd()) {
0687     QString key = *it++;
0688     if (it != lst.constEnd()) {
0689       urlMap.append({key, *it++});
0690     }
0691   }
0692   setMatchPictureUrlMap(urlMap);
0693 }
0694 
0695 void ImportConfig::setImportDir(const QString& importDir)
0696 {
0697   if (m_importDir != importDir) {
0698     m_importDir = importDir;
0699     emit importDirChanged(m_importDir);
0700   }
0701 }
0702 
0703 void ImportConfig::setDisabledPlugins(const QStringList& disabledPlugins)
0704 {
0705   if (m_disabledPlugins != disabledPlugins) {
0706     m_disabledPlugins = disabledPlugins;
0707     emit disabledPluginsChanged(m_disabledPlugins);
0708   }
0709 }
0710 
0711 void ImportConfig::setEnableTimeDifferenceCheck(bool enableTimeDifferenceCheck)
0712 {
0713   if (m_enableTimeDifferenceCheck != enableTimeDifferenceCheck) {
0714     m_enableTimeDifferenceCheck = enableTimeDifferenceCheck;
0715     emit enableTimeDifferenceCheckChanged(m_enableTimeDifferenceCheck);
0716   }
0717 }