1 /**
2  * \file importconfig.cpp
3  * Configuration for import dialog.
4  *
5  * \b Project: Kid3
6  * \author Urs Fleisch
7  * \date 17 Sep 2003
8  *
9  * Copyright (C) 2003-2018  Urs Fleisch
10  *
11  * This file is part of Kid3.
12  *
13  * Kid3 is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * Kid3 is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include "importconfig.h"
28 #include <QString>
29 #include "isettings.h"
30 #include "config.h"
31 
32 namespace {
33 
34 /**
35  * Convert tag version to import destination value in configuration.
36  * @param tagVersion tag version
37  * @return value used in configuration, kept for backwards compatibility.
38  */
tagVersionToImportDestCfg(Frame::TagVersion tagVersion)39 inline int tagVersionToImportDestCfg(Frame::TagVersion tagVersion) {
40   return static_cast<int>(tagVersion) - 1;
41 }
42 
43 /**
44  * Convert import destination value in configuration to tag version.
45  * @param importDest value used in configuration, kept for backwards
46  *                   compatibility.
47  * @return tag version.
48  */
importDestCfgToTagVersion(int importDest)49 inline Frame::TagVersion importDestCfgToTagVersion(int importDest) {
50   return Frame::tagVersionCast(importDest + 1);
51 }
52 
53 }
54 
55 int ImportConfig::s_index = -1;
56 
57 /**
58  * Constructor.
59  */
ImportConfig()60 ImportConfig::ImportConfig()
61   : StoredConfig<ImportConfig>(QLatin1String("Import")), m_importServer(0),
62     m_importDest(Frame::TagV1), m_importFormatIdx(0),
63     m_maxTimeDifference(3),
64     m_importVisibleColumns(0x2000000000ULL),
65     m_importTagsIdx(0),
66     m_pictureSourceIdx(0),
67     m_enableTimeDifferenceCheck(true)
68 {
69   /**
70    * Preset import format regular expressions.
71    * The following codes are used before the () expressions.
72    * %s %{title} title (song)
73    * %l %{album} album
74    * %a %{artist} artist
75    * %c %{comment} comment
76    * %y %{year} year
77    * %t %{track} track, at least two digits
78    * %T %{tracknumber} track number
79    * %g %{genre} genre
80    * %d %{duration} duration mm:ss
81    * %D %{seconds} duration in seconds
82    * %f %{file} file name
83    * %p %{filepath} absolute file path
84    * %u %{url} URL
85    * %n %{tracks} number of tracks
86    */
87   m_importFormatNames.append(QLatin1String("CSV unquoted"));
88   m_importFormatHeaders.append(QLatin1String(""));
89   m_importFormatTracks.append(QLatin1String(
90     R"(%{track}([^\r\n\t]*)\t%{title}([^\r\n\t]*)\t%{artist}([^\r\n\t]*)\t)"
91     R"(%{album}([^\r\n\t]*)\t%{year}([^\r\n\t]*)\t%{genre}([^\r\n\t]*)\t)"
92     R"(%{comment}([^\r\n\t]*)\t(?:\d+:)?%{duration}(\d+:\d+))"));
93 
94   m_importFormatNames.append(QLatin1String("CSV quoted"));
95   m_importFormatHeaders.append(QLatin1String(""));
96   // Using a raw string literal here causes clang to issue
97   // "Unbalanced opening parenthesis in C++ code".
98   m_importFormatTracks.append(QLatin1String(
99     "\"?%{track}([^\\r\\n\\t\"]*)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t\"?"
100     "%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?"
101     "\\t\"?%{year}([^\\r\\n\\t\"]*)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?"
102     "\\t\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t\"?(?:\\d+:)?%{duration}(\\d+:\\d+)"));
103 
104   m_importFormatNames.append(QLatin1String("CSV more unquoted"));
105   m_importFormatHeaders.append(QLatin1String(""));
106   m_importFormatTracks.append(QLatin1String(
107     "%{track}([^\\r\\n\\t]*)\\t%{title}([^\\r\\n\\t]*)\\t%{artist}([^\\r\\n\\t]*)\\t"
108     "%{album}([^\\r\\n\\t]*)\\t%{year}([^\\r\\n\\t]*)\\t%{genre}([^\\r\\n\\t]*)\\"
109     "t%{comment}([^\\r\\n\\t]*)\\t(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\\t"
110     "%{album artist}([^\\r\\n\\t]*)\\t%{arranger}([^\\r\\n\\t]*)\\t"
111     "%{author}([^\\r\\n\\t]*)\\t%{bpm}([^\\r\\n\\t]*)\\t"
112     "%{composer}([^\\r\\n\\t]*)\\t%{conductor}([^\\r\\n\\t]*)\\t"
113     "%{copyright}([^\\r\\n\\t]*)\\t%{disc number}([^\\r\\n\\t]*)\\t"
114     "%{encoded-by}([^\\r\\n\\t]*)\\t%{grouping}([^\\r\\n\\t]*)\\t%{isrc}([^\\r\\n\\t]*)\\t"
115     "%{language}([^\\r\\n\\t]*)\\t%{lyricist}([^\\r\\n\\t]*)\\t%{lyrics}([^\\r\\n\\t]*)\\t"
116     "%{media}([^\\r\\n\\t]*)\\t%{original album}([^\\r\\n\\t]*)\\t"
117     "%{original artist}([^\\r\\n\\t]*)\\t%{original date}([^\\r\\n\\t]*)\\t"
118     "%{part}([^\\r\\n\\t]*)\\t%{performer}([^\\r\\n\\t]*)\\t"
119     "%{publisher}([^\\r\\n\\t]*)\\t%{remixer}([^\\r\\n\\t]*)\\t"
120     "%{subtitle}([^\\r\\n\\t]*)\\t%{website}([^\\r\\n\\t]*)"));
121 
122   m_importFormatNames.append(QLatin1String("CSV more quoted"));
123   m_importFormatHeaders.append(QLatin1String(""));
124   m_importFormatTracks.append(QLatin1String(
125     "\"?%{track}([^\\r\\n\\t\"]*)\"?\\t\"?%{title}([^\\r\\n\\t\"]*)\"?\\t"
126     "\"?%{artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{album}([^\\r\\n\\t\"]*)\"?\\t"
127     "\"?%{year}([^\\r\\n\\t\"]*)\"?\\t\"?%{genre}([^\\r\\n\\t\"]*)\"?\\t"
128     "\"?%{comment}([^\\r\\n\\t\"]*)\"?\\t"
129     "\"?(?:\\d+:)?%{duration}(\\d+:\\d+)(?:\\.\\d+)?\"?\\t"
130     "\"?%{album artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{arranger}([^\\r\\n\\t\"]*)\"?\\t"
131     "\"?%{author}([^\\r\\n\\t\"]*)\"?\\t\"?%{bpm}([^\\r\\n\\t\"]*)\"?\\t"
132     "\"?%{composer}([^\\r\\n\\t\"]*)\"?\\t\"?%{conductor}([^\\r\\n\\t\"]*)\"?\\t"
133     "\"?%{copyright}([^\\r\\n\\t\"]*)\"?\\t\"?%{disc number}([^\\r\\n\\t\"]*)\"?\\t"
134     "\"?%{encoded-by}([^\\r\\n\\t\"]*)\"?\\t\"?%{grouping}([^\\r\\n\\t\"]*)\"?\\t"
135     "\"?%{isrc}([^\\r\\n\\t\"]*)\"?\\t"
136     "\"?%{language}([^\\r\\n\\t\"]*)\"?\\t\"?%{lyricist}([^\\r\\n\\t\"]*)\"?\\t"
137     "\"?%{lyrics}([^\\r\\n\\t\"]*)\"?\\t"
138     "\"?%{media}([^\\r\\n\\t\"]*)\"?\\t\"?%{original album}([^\\r\\n\\t\"]*)\"?\\t"
139     "\"?%{original artist}([^\\r\\n\\t\"]*)\"?\\t\"?%{original date}([^\\r\\n\\t\"]*)\"?\\t"
140     "\"?%{part}([^\\r\\n\\t\"]*)\"?\\t\"?%{performer}([^\\r\\n\\t\"]*)\"?\\t"
141     "\"?%{publisher}([^\\r\\n\\t\"]*)\"?\\t\"?%{remixer}([^\\r\\n\\t\"]*)\"?\\t"
142     "\"?%{subtitle}([^\\r\\n\\t\"]*)\"?\\t\"?%{website}([^\\r\\n\\t\"]*)"));
143 
144   m_importFormatNames.append(QLatin1String("freedb HTML text"));
145   m_importFormatHeaders.append(QLatin1String(
146     R"(%{artist}(\S[^\r\n/]*\S)\s*/\s*%{album}(\S[^\r\n]*\S)[\r\n]+\s*)"
147     R"(tracks:\s+\d+.*year:\s*%{year}([^\r\n\t]*)?.*)"
148     R"(genre:\s*%{genre}(\S[^\r\n]*\S)?[\r\n])"));
149   m_importFormatTracks.append(QLatin1String(
150     R"([\r\n]%{track}(\d+)[\.\s]+%{duration}(\d+:\d+)\s+%{title}(\S[^\r\n]*\S))"));
151 
152   m_importFormatNames.append(QLatin1String("freedb HTML source"));
153   m_importFormatHeaders.append(QLatin1String(
154     R"(<[^>]+>%{artist}([^<\s][^\r\n/]*\S)\s*/\s*)"
155     R"(%{album}(\S[^\r\n]*[^\s>])<[^>]+>[\r\n]+\s*)"
156     R"(tracks:\s+\d+.*year:\s*%{year}([^\r\n\t]*)?.*)"
157     R"(genre:\s*%{genre}(\S[^\r\n>]*\S)?<[^>]+>[\r\n])"));
158   m_importFormatTracks.append(QLatin1String(
159     R"(<td[^>]*>\s*%{track}(\d+).</td><td[^>]*>\s*%{duration}(\d+:\d+)</td>)"
160     R"(<td[^>]*>(?:<[^>]+>)?%{title}([^<\r\n]+))"));
161 
162   m_importFormatNames.append(QLatin1String("Title"));
163   m_importFormatHeaders.append(QLatin1String(""));
164   m_importFormatTracks.append(QLatin1String(R"(\s*%{title}(\S[^\r\n]*\S)\s*)"));
165 
166   m_importFormatNames.append(QLatin1String("Track Title"));
167   m_importFormatHeaders.append(QLatin1String(""));
168   m_importFormatTracks.append(QLatin1String(
169     R"(\s*%{track}(\d+)[\.\s]+%{title}(\S[^\r\n]*\S)\s*)"));
170 
171   m_importFormatNames.append(QLatin1String("Track Title Time"));
172   m_importFormatHeaders.append(QLatin1String(""));
173   m_importFormatTracks.append(QLatin1String(
174     R"(\s*%{track}(\d+)[\.\s]+%{title}(\S[^\r\n]*\S)\s+%{duration}(\d+:\d+)\s*)"));
175 
176   m_importFormatNames.append(QLatin1String("Custom Format"));
177   m_importFormatHeaders.append(QLatin1String(""));
178   m_importFormatTracks.append(QLatin1String(""));
179 
180   m_importTagsNames.append(QLatin1String("Artist to Album Artist"));
181   m_importTagsSources.append(QLatin1String("%{artist}"));
182   m_importTagsExtractions.append(QLatin1String("%{albumartist}(.+)"));
183 
184   m_importTagsNames.append(QLatin1String("Album Artist to Artist"));
185   m_importTagsSources.append(QLatin1String("%{albumartist}"));
186   m_importTagsExtractions.append(QLatin1String("%{artist}(.+)"));
187 
188   m_importTagsNames.append(QLatin1String("Artist to Composer"));
189   m_importTagsSources.append(QLatin1String("%{artist}"));
190   m_importTagsExtractions.append(QLatin1String("%{composer}(.+)"));
191 
192   m_importTagsNames.append(QLatin1String("Artist to Conductor"));
193   m_importTagsSources.append(QLatin1String("%{artist}"));
194   m_importTagsExtractions.append(QLatin1String("%{conductor}(.+)"));
195 
196   m_importTagsNames.append(QLatin1String("Track Number from Title"));
197   m_importTagsSources.append(QLatin1String("%{title}"));
198   m_importTagsExtractions.append(
199         QLatin1String(R"(\s*%{track}(\d+)[\.\s]+%{title}(\S.*\S)\s*)"));
200 
201   m_importTagsNames.append(QLatin1String("Track Number to Title"));
202   m_importTagsSources.append(QLatin1String("%{track} %{title}"));
203   m_importTagsExtractions.append(QLatin1String("%{title}(.+)"));
204 
205   m_importTagsNames.append(QLatin1String("Subtitle from Title"));
206   m_importTagsSources.append(QLatin1String("%{title}"));
207   m_importTagsExtractions.append(QLatin1String("%{subtitle}(.+) - "));
208 
209   m_importTagsNames.append(QLatin1String("Title Annotation to Comment"));
210   m_importTagsSources.append(QLatin1String("%{title}"));
211   m_importTagsExtractions.append(
212         QLatin1String("%{title}(.+) +\\(%{comment}((?:Bonus|Remix)[^)]*)\\)"));
213 
214   m_importTagsNames.append(QLatin1String("Modification Date to Date"));
215   m_importTagsSources.append(QLatin1String("%{modificationdate}"));
216   m_importTagsExtractions.append(QLatin1String(R"(%{date}(\d\d\d\d-\d\d-\d\d))"));
217 
218   m_importTagsNames.append(QLatin1String("Filename to Original Filename"));
219   m_importTagsSources.append(QLatin1String("%{file}"));
220   m_importTagsExtractions.append(QLatin1String("%{TOFN}(.+)"));
221 
222   m_importTagsNames.append(QLatin1String("Custom Format"));
223   m_importTagsSources.append(QLatin1String(""));
224   m_importTagsExtractions.append(QLatin1String(""));
225 
226   m_pictureSourceNames.append(QLatin1String("Google Images"));
227   m_pictureSourceUrls.append(QLatin1String(
228     "http://www.google.com/search?tbm=isch&q=%u{artist}%20%u{album}"));
229   m_pictureSourceNames.append(QLatin1String("Yahoo Images"));
230   m_pictureSourceUrls.append(QLatin1String(
231     "http://images.search.yahoo.com/search/images?ei=UTF-8&p=%u{artist}%20%u{album}"));
232   m_pictureSourceNames.append(QLatin1String("Amazon"));
233   m_pictureSourceUrls.append(QLatin1String(
234     "http://www.amazon.com/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
235   m_pictureSourceNames.append(QLatin1String("Amazon.co.uk"));
236   m_pictureSourceUrls.append(QLatin1String(
237     "http://www.amazon.co.uk/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
238   m_pictureSourceNames.append(QLatin1String("Amazon.de"));
239   m_pictureSourceUrls.append(QLatin1String(
240     "http://www.amazon.de/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
241   m_pictureSourceNames.append(QLatin1String("Amazon.fr"));
242   m_pictureSourceUrls.append(QLatin1String(
243     "http://www.amazon.fr/s?search-alias=aps&field-keywords=%u{artist}+%u{album}"));
244   m_pictureSourceNames.append(QLatin1String("MusicBrainz"));
245   m_pictureSourceUrls.append(QLatin1String(
246     "http://musicbrainz.org/search/textsearch.html?query=%u{artist}+%u{album}&type=release"));
247   m_pictureSourceNames.append(QLatin1String("Discogs"));
248   m_pictureSourceUrls.append(QLatin1String(
249     "http://www.discogs.com/search?q=%u{artist}+%u{album}"));
250   m_pictureSourceNames.append(QLatin1String("CD Universe"));
251   m_pictureSourceUrls.append(QLatin1String(
252     "http://www.cduniverse.com/sresult.asp?HT_Search_Info=%u{artist}+%u{album}"));
253   m_pictureSourceNames.append(QLatin1String("Coveralia"));
254   m_pictureSourceUrls.append(QLatin1String(
255     "http://www.coveralia.com/mostrar.php?bus=%u{artist}%20%u{album}&bust=2"));
256   m_pictureSourceNames.append(QLatin1String("SlothRadio"));
257   m_pictureSourceUrls.append(QLatin1String(
258     "http://www.slothradio.com/covers/?artist=%u{artist}&album=%u{album}"));
259   m_pictureSourceNames.append(QLatin1String("Albumart"));
260   m_pictureSourceUrls.append(QLatin1String(
261     "http://www.albumart.org/index.php?srchkey=%u{artist}+%u{album}&searchindex=Music"));
262   m_pictureSourceNames.append(QLatin1String("Custom Source"));
263   m_pictureSourceUrls.append(QLatin1String(""));
264 
265   m_matchPictureUrlMap.append({
266     {QLatin1String("https?://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*"),
267      QLatin1String("\\1")},
268     {QLatin1String("http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*"),
269      QLatin1String("http%3A%2F%2F\\1")},
270     {QLatin1String(
271      "http://(?:www.)?amazon.(?:com|co.uk|de|fr).*/(?:dp|ASIN|images|product|-)/([A-Z0-9]+).*"),
272      QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg")},
273     {QLatin1String(
274      "http://musicbrainz.org/misc/redirects/.*&asin=([A-Z0-9]+).*"),
275      QLatin1String("http://images.amazon.com/images/P/\\1.01._SCLZZZZZZZ_.jpg")},
276     {QLatin1String("(http://.*4shared.com/img/.*)"),
277      QLatin1String("\\1.jpg")}
278   });
279 }
280 
281 /**
282  * Persist configuration.
283  *
284  * @param config configuration
285  */
writeToConfig(ISettings * config) const286 void ImportConfig::writeToConfig(ISettings* config) const
287 {
288   config->beginGroup(m_group);
289   config->setValue(QLatin1String("ImportServer"),
290                    QVariant(m_importServer));
291   config->setValue(QLatin1String("ImportDestination"),
292                    QVariant(tagVersionToImportDestCfg(m_importDest)));
293   config->setValue(QLatin1String("ImportFormatNames"),
294                    QVariant(m_importFormatNames));
295   config->setValue(QLatin1String("ImportFormatHeaders"),
296                    QVariant(m_importFormatHeaders));
297   config->setValue(QLatin1String("ImportFormatTracks"),
298                    QVariant(m_importFormatTracks));
299   config->setValue(QLatin1String("ImportFormatIdx"),
300                    QVariant(m_importFormatIdx));
301   config->setValue(QLatin1String("EnableTimeDifferenceCheck"),
302                    QVariant(m_enableTimeDifferenceCheck));
303   config->setValue(QLatin1String("MaxTimeDifference"),
304                    QVariant(m_maxTimeDifference));
305 #ifdef Q_OS_MAC
306   // Convince Mac OS X to store a 64-bit value.
307   config->setValue(QLatin1String("ImportVisibleColumns"),
308                    QVariant(m_importVisibleColumns | (Q_UINT64_C(1) << 63)));
309 #else
310   config->setValue(QLatin1String("ImportVisibleColumns"),
311                    QVariant(m_importVisibleColumns));
312 #endif
313   config->setValue(QLatin1String("ImportWindowGeometry"),
314                    QVariant(m_importWindowGeometry));
315 
316   config->setValue(QLatin1String("ImportTagsNames"),
317                    QVariant(m_importTagsNames));
318   config->setValue(QLatin1String("ImportTagsSources"),
319                    QVariant(m_importTagsSources));
320   config->setValue(QLatin1String("ImportTagsExtractions"),
321                    QVariant(m_importTagsExtractions));
322   config->setValue(QLatin1String("ImportTagsIdx"),
323                    QVariant(m_importTagsIdx));
324 
325   config->setValue(QLatin1String("PictureSourceNames"),
326                    QVariant(m_pictureSourceNames));
327   config->setValue(QLatin1String("PictureSourceUrls"),
328                    QVariant(m_pictureSourceUrls));
329   config->setValue(QLatin1String("PictureSourceIdx"),
330                    QVariant(m_pictureSourceIdx));
331   QStringList keys, values;
332   for (auto it = m_matchPictureUrlMap.constBegin();
333        it != m_matchPictureUrlMap.constEnd();
334        ++it) {
335     keys.append(it->first);
336     values.append(it->second);
337   }
338   config->setValue(QLatin1String("MatchPictureUrlMapKeys"), QVariant(keys));
339   config->setValue(QLatin1String("MatchPictureUrlMapValues"), QVariant(values));
340   config->setValue(QLatin1String("BrowseCoverArtWindowGeometry"),
341                    QVariant(m_browseCoverArtWindowGeometry));
342 
343   config->setValue(QLatin1String("DisabledPlugins"),
344                    QVariant(m_disabledPlugins));
345   config->endGroup();
346 }
347 
348 /**
349  * Read persisted configuration.
350  *
351  * @param config configuration
352  */
readFromConfig(ISettings * config)353 void ImportConfig::readFromConfig(ISettings* config)
354 {
355   QStringList names, headers, tracks;
356   QStringList tagsNames, tagsSources, tagsExtractions;
357   QStringList picNames, picUrls;
358 
359   config->beginGroup(m_group);
360   m_importServer = config->value(QLatin1String("ImportServer"),
361                                  m_importServer).toInt();
362   m_importDest = importDestCfgToTagVersion(
363     config->value(QLatin1String("ImportDestination"),
364                   tagVersionToImportDestCfg(m_importDest)).toInt());
365   names = config->value(QLatin1String("ImportFormatNames"),
366                         m_importFormatNames).toStringList();
367   headers = config->value(QLatin1String("ImportFormatHeaders"),
368                           m_importFormatHeaders).toStringList();
369   tracks = config->value(QLatin1String("ImportFormatTracks"),
370                          m_importFormatTracks).toStringList();
371   m_importFormatIdx = config->value(QLatin1String("ImportFormatIdx"),
372                                     m_importFormatIdx).toInt();
373   m_enableTimeDifferenceCheck =
374       config->value(QLatin1String("EnableTimeDifferenceCheck"),
375                     m_enableTimeDifferenceCheck).toBool();
376   m_maxTimeDifference = config->value(QLatin1String("MaxTimeDifference"),
377                                       m_maxTimeDifference).toInt();
378   m_importVisibleColumns = config->value(QLatin1String("ImportVisibleColumns"),
379                                          m_importVisibleColumns).toULongLong();
380 #ifdef Q_OS_MAC
381   m_importVisibleColumns &= ~(Q_UINT64_C(1) << 63);
382 #endif
383   m_importWindowGeometry = config->value(QLatin1String("ImportWindowGeometry"),
384                                          m_importWindowGeometry).toByteArray();
385 
386   tagsNames = config->value(QLatin1String("ImportTagsNames"),
387                             m_importTagsNames).toStringList();
388   tagsSources = config->value(QLatin1String("ImportTagsSources"),
389                               m_importTagsSources).toStringList();
390   tagsExtractions = config->value(QLatin1String("ImportTagsExtractions"),
391                                   m_importTagsExtractions).toStringList();
392   m_importTagsIdx = config->value(QLatin1String("ImportTagsIdx"),
393                                   m_importTagsIdx).toInt();
394 
395   picNames = config->value(QLatin1String("PictureSourceNames"),
396                            m_pictureSourceNames).toStringList();
397   picUrls = config->value(QLatin1String("PictureSourceUrls"),
398                           m_pictureSourceUrls).toStringList();
399   m_pictureSourceIdx = config->value(
400     QLatin1String("PictureSourceIdx"), m_pictureSourceIdx).toInt();
401   QStringList keys = config->value(QLatin1String("MatchPictureUrlMapKeys"),
402                                    QStringList()).toStringList();
403   QStringList values = config->value(QLatin1String("MatchPictureUrlMapValues"),
404                                      QStringList()).toStringList();
405   if (!keys.empty() && !values.empty()) {
406     m_matchPictureUrlMap.clear();
407     for (auto itk = keys.constBegin(), itv = values.constBegin();
408          itk != keys.constEnd() && itv != values.constEnd();
409          ++itk, ++itv) {
410       m_matchPictureUrlMap.append({*itk, *itv});
411     }
412   }
413   m_browseCoverArtWindowGeometry = config->value(
414         QLatin1String("BrowseCoverArtWindowGeometry"),
415         m_browseCoverArtWindowGeometry).toByteArray();
416 
417   m_disabledPlugins = config->value(QLatin1String("DisabledPlugins"),
418                                  m_disabledPlugins).toStringList();
419   config->endGroup();
420 
421   // KConfig seems to strip empty entries from the end of the string lists,
422   // so we have to append them again.
423   const int numNames = names.size();
424   while (headers.size() < numNames) headers.append(QLatin1String(""));
425   while (tracks.size() < numNames) tracks.append(QLatin1String(""));
426   const int numPicNames = picNames.size();
427   while (picUrls.size() < numPicNames) picUrls.append(QLatin1String(""));
428 
429   /* Use defaults if no configuration found */
430   for (auto namesIt = names.constBegin(), headersIt = headers.constBegin(),
431          tracksIt = tracks.constBegin();
432        namesIt != names.constEnd() && headersIt != headers.constEnd() &&
433          tracksIt != tracks.constEnd();
434        ++namesIt, ++headersIt, ++tracksIt) {
435     int idx = m_importFormatNames.indexOf(*namesIt);
436     if (idx >= 0) {
437       m_importFormatHeaders[idx] = *headersIt;
438       m_importFormatTracks[idx] = *tracksIt;
439     } else if (!(*namesIt).isEmpty()) {
440       m_importFormatNames.append(*namesIt);
441       m_importFormatHeaders.append(*headersIt);
442       m_importFormatTracks.append(*tracksIt);
443     }
444   }
445 
446   for (auto tagsNamesIt = tagsNames.constBegin(), sourcesIt = tagsSources.constBegin(),
447          extractionsIt = tagsExtractions.constBegin();
448        tagsNamesIt != tagsNames.constEnd() && sourcesIt != tagsSources.constEnd() &&
449          extractionsIt != tagsExtractions.constEnd();
450        ++tagsNamesIt, ++sourcesIt, ++extractionsIt) {
451     int idx = m_importTagsNames.indexOf(*tagsNamesIt);
452     if (idx >= 0) {
453       m_importTagsSources[idx] = *sourcesIt;
454       m_importTagsExtractions[idx] = *extractionsIt;
455     } else if (!(*tagsNamesIt).isEmpty()) {
456       m_importTagsNames.append(*tagsNamesIt);
457       m_importTagsSources.append(*sourcesIt);
458       m_importTagsExtractions.append(*extractionsIt);
459     }
460   }
461 
462   for (auto picNamesIt = picNames.constBegin(), picUrlsIt = picUrls.constBegin();
463        picNamesIt != picNames.constEnd() && picUrlsIt != picUrls.constEnd();
464        ++picNamesIt, ++picUrlsIt) {
465     int idx = m_pictureSourceNames.indexOf(*picNamesIt);
466     if (idx >= 0) {
467       m_pictureSourceUrls[idx] = *picUrlsIt;
468     } else if (!(*picNamesIt).isEmpty()) {
469       m_pictureSourceNames.append(*picNamesIt);
470       m_pictureSourceUrls.append(*picUrlsIt);
471     }
472   }
473 
474   if (m_importFormatIdx >= static_cast<int>(m_importFormatNames.size()))
475     m_importFormatIdx = 0;
476   if (m_importTagsIdx >= static_cast<int>(m_importTagsNames.size()))
477     m_importTagsIdx = 0;
478   if (m_pictureSourceIdx >=  static_cast<int>(m_pictureSourceNames.size()))
479     m_pictureSourceIdx = 0;
480 
481   // Replace mappings which do no longer work.
482   if (m_pictureSourceUrls.removeOne(QLatin1String(
483       "http://cdbaby.com/found?artist=%u{artist}&album=%u{album}"))) {
484     m_pictureSourceNames.removeOne(QLatin1String("CD Baby"));
485   }
486   if (m_pictureSourceUrls.removeOne(QLatin1String(
487       "http://www.jamendo.com/en/search/all/%u{artist}%20%u{album}"))) {
488     m_pictureSourceNames.removeOne(QLatin1String("Jamendo"));
489   }
490   m_pictureSourceUrls.replaceInStrings(
491         QLatin1String("http://images.google.com/images?q=%u{artist}%20%u{album}"),
492         QLatin1String("http://www.google.com/search?tbm=isch&q=%u{artist}%20%u{album}"));
493   for (auto it = m_matchPictureUrlMap.begin(); it != m_matchPictureUrlMap.end();) {
494     if (it->first == QLatin1String(
495           "http://images.google.com/.*imgurl=([^&]+)&.*") ||
496         it->first == QLatin1String(
497           "http://www.google.com/.*imgurl=([^&]+)&.*") ||
498         it->first == QLatin1String(
499           "http://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*")) {
500       *it = {QLatin1String("https?://www.google.(?:[^/]+)/.*imgurl=([^&]+)&.*"),
501              QLatin1String("\\1")};
502     } else if (it->first == QLatin1String(
503                  "http://rds.yahoo.com/.*&imgurl=([^&]+)&.*")) {
504       *it = {QLatin1String(
505              "http://images.search.yahoo.com/.*&imgurl=([^&]+)&.*"),
506              QLatin1String("http%3A%2F%2F\\1")};
507     } else if (it->first == QLatin1String(
508                  "http://rds.yahoo.com/.*%26imgurl=((?:[^%]|%(?!26))+).*") ||
509                it->first == QLatin1String(
510                  R"(http://cdbaby.com/cd/(\w)(\w)(\w+))") ||
511                it->first == QLatin1String(
512                  "http://www.jamendo.com/en/album/(\\d+)")) {
513       it = m_matchPictureUrlMap.erase(it);
514       continue;
515     }
516     ++it;
517   }
518 }
519 
setAvailablePlugins(const QStringList & availablePlugins)520 void ImportConfig::setAvailablePlugins(const QStringList& availablePlugins)
521 {
522   if (m_availablePlugins != availablePlugins) {
523     m_availablePlugins = availablePlugins;
524     emit availablePluginsChanged(m_availablePlugins);
525   }
526 }
527 
setImportServer(int importServer)528 void ImportConfig::setImportServer(int importServer)
529 {
530   if (m_importServer != importServer) {
531     m_importServer = importServer;
532     emit importServerChanged(m_importServer);
533   }
534 }
535 
setImportDest(Frame::TagVersion importDest)536 void ImportConfig::setImportDest(Frame::TagVersion importDest)
537 {
538   if (m_importDest != importDest) {
539     m_importDest = importDest;
540     emit importDestChanged(m_importDest);
541   }
542 }
543 
setImportFormatNames(const QStringList & importFormatNames)544 void ImportConfig::setImportFormatNames(const QStringList& importFormatNames)
545 {
546   if (m_importFormatNames != importFormatNames) {
547     m_importFormatNames = importFormatNames;
548     emit importFormatNamesChanged(m_importFormatNames);
549   }
550 }
551 
setImportFormatHeaders(const QStringList & importFormatHeaders)552 void ImportConfig::setImportFormatHeaders(const QStringList& importFormatHeaders)
553 {
554   if (m_importFormatHeaders != importFormatHeaders) {
555     m_importFormatHeaders = importFormatHeaders;
556     emit importFormatHeadersChanged(m_importFormatHeaders);
557   }
558 }
559 
setImportFormatTracks(const QStringList & importFormatTracks)560 void ImportConfig::setImportFormatTracks(const QStringList& importFormatTracks)
561 {
562   if (m_importFormatTracks != importFormatTracks) {
563     m_importFormatTracks = importFormatTracks;
564     emit importFormatTracksChanged(m_importFormatTracks);
565   }
566 }
567 
setImportFormatIndex(int importFormatIdx)568 void ImportConfig::setImportFormatIndex(int importFormatIdx)
569 {
570   if (m_importFormatIdx != importFormatIdx) {
571     m_importFormatIdx = importFormatIdx;
572     emit importFormatIndexChanged(m_importFormatIdx);
573   }
574 }
575 
setMaxTimeDifference(int maxTimeDifference)576 void ImportConfig::setMaxTimeDifference(int maxTimeDifference)
577 {
578   if (m_maxTimeDifference != maxTimeDifference) {
579     m_maxTimeDifference = maxTimeDifference;
580     emit maxTimeDifferenceChanged(m_maxTimeDifference);
581   }
582 }
583 
setImportVisibleColumns(quint64 importVisibleColumns)584 void ImportConfig::setImportVisibleColumns(quint64 importVisibleColumns)
585 {
586   if (m_importVisibleColumns != importVisibleColumns) {
587     m_importVisibleColumns = importVisibleColumns;
588     emit importVisibleColumnsChanged(m_importVisibleColumns);
589   }
590 }
591 
setImportWindowGeometry(const QByteArray & importWindowGeometry)592 void ImportConfig::setImportWindowGeometry(const QByteArray& importWindowGeometry)
593 {
594   if (m_importWindowGeometry != importWindowGeometry) {
595     m_importWindowGeometry = importWindowGeometry;
596     emit importWindowGeometryChanged(m_importWindowGeometry);
597   }
598 }
599 
setImportTagsNames(const QStringList & importTagsNames)600 void ImportConfig::setImportTagsNames(const QStringList& importTagsNames)
601 {
602   if (m_importTagsNames != importTagsNames) {
603     m_importTagsNames = importTagsNames;
604     emit importTagsNamesChanged(m_importTagsNames);
605   }
606 }
607 
setImportTagsSources(const QStringList & importTagsSources)608 void ImportConfig::setImportTagsSources(const QStringList& importTagsSources)
609 {
610   if (m_importTagsSources != importTagsSources) {
611     m_importTagsSources = importTagsSources;
612     emit importTagsSourcesChanged(m_importTagsSources);
613   }
614 }
615 
setImportTagsExtractions(const QStringList & importTagsExtractions)616 void ImportConfig::setImportTagsExtractions(const QStringList& importTagsExtractions)
617 {
618   if (m_importTagsExtractions != importTagsExtractions) {
619     m_importTagsExtractions = importTagsExtractions;
620     emit importTagsExtractionsChanged(m_importTagsExtractions);
621   }
622 }
623 
setImportTagsIndex(int importTagsIdx)624 void ImportConfig::setImportTagsIndex(int importTagsIdx)
625 {
626   if (m_importTagsIdx != importTagsIdx) {
627     m_importTagsIdx = importTagsIdx;
628     emit importTagsIndexChanged(m_importTagsIdx);
629   }
630 }
631 
setPictureSourceNames(const QStringList & pictureSourceNames)632 void ImportConfig::setPictureSourceNames(const QStringList& pictureSourceNames)
633 {
634   if (m_pictureSourceNames != pictureSourceNames) {
635     m_pictureSourceNames = pictureSourceNames;
636     emit pictureSourceNamesChanged(m_pictureSourceNames);
637   }
638 }
639 
setPictureSourceUrls(const QStringList & pictureSourceUrls)640 void ImportConfig::setPictureSourceUrls(const QStringList& pictureSourceUrls)
641 {
642   if (m_pictureSourceUrls != pictureSourceUrls) {
643     m_pictureSourceUrls = pictureSourceUrls;
644     emit pictureSourceUrlsChanged(m_pictureSourceUrls);
645   }
646 }
647 
648 
setPictureSourceIndex(int pictureSourceIdx)649 void ImportConfig::setPictureSourceIndex(int pictureSourceIdx)
650 {
651   if (m_pictureSourceIdx != pictureSourceIdx) {
652     m_pictureSourceIdx = pictureSourceIdx;
653     emit pictureSourceIndexChanged(m_pictureSourceIdx);
654   }
655 }
656 
setBrowseCoverArtWindowGeometry(const QByteArray & browseCoverArtWindowGeometry)657 void ImportConfig::setBrowseCoverArtWindowGeometry(const QByteArray& browseCoverArtWindowGeometry)
658 {
659   if (m_browseCoverArtWindowGeometry != browseCoverArtWindowGeometry) {
660     m_browseCoverArtWindowGeometry = browseCoverArtWindowGeometry;
661     emit browseCoverArtWindowGeometryChanged(m_browseCoverArtWindowGeometry);
662   }
663 }
664 
setMatchPictureUrlMap(const QList<QPair<QString,QString>> & matchPictureUrlMap)665 void ImportConfig::setMatchPictureUrlMap(const QList<QPair<QString, QString>>& matchPictureUrlMap)
666 {
667   if (m_matchPictureUrlMap != matchPictureUrlMap) {
668     m_matchPictureUrlMap = matchPictureUrlMap;
669     emit matchPictureUrlMapChanged(m_matchPictureUrlMap);
670   }
671 }
672 
matchPictureUrlStringList() const673 QStringList ImportConfig::matchPictureUrlStringList() const
674 {
675   QStringList lst;
676   QList<QPair<QString, QString>> urlMap = matchPictureUrlMap();
677   for (auto it = urlMap.constBegin(); it != urlMap.constEnd(); ++it) {
678     lst.append(it->first);
679     lst.append(it->second);
680   }
681   return lst;
682 }
683 
setMatchPictureUrlStringList(const QStringList & lst)684 void ImportConfig::setMatchPictureUrlStringList(const QStringList& lst)
685 {
686   QList<QPair<QString, QString>> urlMap;
687   auto it = lst.constBegin();
688   while (it != lst.constEnd()) {
689     QString key = *it++;
690     if (it != lst.constEnd()) {
691       urlMap.append({key, *it++});
692     }
693   }
694   setMatchPictureUrlMap(urlMap);
695 }
696 
setImportDir(const QString & importDir)697 void ImportConfig::setImportDir(const QString& importDir)
698 {
699   if (m_importDir != importDir) {
700     m_importDir = importDir;
701     emit importDirChanged(m_importDir);
702   }
703 }
704 
setDisabledPlugins(const QStringList & disabledPlugins)705 void ImportConfig::setDisabledPlugins(const QStringList& disabledPlugins)
706 {
707   if (m_disabledPlugins != disabledPlugins) {
708     m_disabledPlugins = disabledPlugins;
709     emit disabledPluginsChanged(m_disabledPlugins);
710   }
711 }
712 
setEnableTimeDifferenceCheck(bool enableTimeDifferenceCheck)713 void ImportConfig::setEnableTimeDifferenceCheck(bool enableTimeDifferenceCheck)
714 {
715   if (m_enableTimeDifferenceCheck != enableTimeDifferenceCheck) {
716     m_enableTimeDifferenceCheck = enableTimeDifferenceCheck;
717     emit enableTimeDifferenceCheckChanged(m_enableTimeDifferenceCheck);
718   }
719 }
720