File indexing completed on 2024-05-12 05:10:11
0001 /*************************************************************************** 0002 Copyright (C) 2005-2022 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "gcstarimporter.h" 0026 #include "../collections/videocollection.h" 0027 #include "../utils/string_utils.h" 0028 #include "../utils/datafileregistry.h" 0029 #include "../images/imagefactory.h" 0030 #include "../borrower.h" 0031 #include "../fieldformat.h" 0032 #include "xslthandler.h" 0033 #include "tellicoimporter.h" 0034 0035 #include <KLocalizedString> 0036 0037 #include <QTextCodec> 0038 #include <QTextStream> 0039 0040 #define CHECKLIMITS(n) if(values.count() <= n) continue 0041 0042 using Tellico::Import::GCstarImporter; 0043 0044 GCstarImporter::GCstarImporter(const QUrl& url_) : TextImporter(url_, true), m_cancelled(false), m_relativeImageLinks(false) { 0045 } 0046 0047 GCstarImporter::GCstarImporter(const QString& text_) : TextImporter(text_), m_cancelled(false), m_relativeImageLinks(false) { 0048 } 0049 0050 bool GCstarImporter::canImport(int type) const { 0051 return type == Data::Collection::Video 0052 || type == Data::Collection::Book 0053 || type == Data::Collection::ComicBook 0054 || type == Data::Collection::Album 0055 || type == Data::Collection::Game 0056 || type == Data::Collection::Wine 0057 || type == Data::Collection::Coin 0058 || type == Data::Collection::BoardGame; 0059 } 0060 0061 Tellico::Data::CollPtr GCstarImporter::collection() { 0062 if(m_coll) { 0063 return m_coll; 0064 } 0065 0066 emit signalTotalSteps(this, 100); 0067 0068 QString str = text(); 0069 QTextStream t(&str); 0070 QString line = t.readLine(); 0071 if(line.startsWith(QLatin1String("GCfilms"))) { 0072 readGCfilms(str); 0073 } else { 0074 // need to reparse the string if it's in utf-8 0075 if(line.toLower().indexOf(QLatin1String("utf-8")) > 0) { 0076 str = QString::fromUtf8(str.toLocal8Bit()); 0077 } 0078 readGCstar(str); 0079 } 0080 return m_coll; 0081 } 0082 0083 void GCstarImporter::readGCfilms(const QString& text_) { 0084 m_coll = new Data::VideoCollection(true); 0085 bool hasURL = false; 0086 if(m_coll->hasField(QStringLiteral("url"))) { 0087 hasURL = m_coll->fieldByName(QStringLiteral("url"))->type() == Data::Field::URL; 0088 } else { 0089 Data::FieldPtr field(new Data::Field(QStringLiteral("url"), i18n("URL"), Data::Field::URL)); 0090 field->setCategory(i18n("General")); 0091 m_coll->addField(field); 0092 hasURL = true; 0093 } 0094 0095 bool convertUTF8 = false; 0096 QHash<QString, Data::BorrowerPtr> borrowers; 0097 static const QRegularExpression rx(QLatin1String("\\s*,\\s*")); 0098 static const QRegularExpression year(QLatin1String("\\d{4}")); 0099 static const QRegularExpression runTimeHr(QLatin1String("(\\d+)\\s?hr?")); 0100 static const QRegularExpression runTimeMin(QLatin1String("(\\d+)\\s?mi?n?")); 0101 0102 bool gotFirstLine = false; 0103 0104 QString tmp = text_; 0105 QTextStream t(&tmp); 0106 0107 const uint length = text_.length(); 0108 const uint stepSize = qMax(s_stepSize, length/100); 0109 const bool showProgress = options() & ImportProgress; 0110 0111 emit signalTotalSteps(this, length); 0112 0113 uint j = 0; 0114 for(QString line = t.readLine(); !m_cancelled && !line.isNull(); line = t.readLine(), j += line.length()) { 0115 // string was wrongly converted 0116 if(convertUTF8) { 0117 line = QString::fromUtf8(line.toLocal8Bit()); 0118 } 0119 QStringList values = line.split(QLatin1Char('|')); 0120 if(values.empty()) { 0121 continue; 0122 } 0123 0124 if(!gotFirstLine) { 0125 if(values[0] != QLatin1String("GCfilms")) { 0126 setStatusMessage(i18n("<qt>The file is not a valid GCstar data file.</qt>")); 0127 m_coll = nullptr; 0128 return; 0129 } 0130 if(values.size() > 2 && values[2] == QLatin1String("UTF8")) { 0131 // if locale encoding isn't utf8, need to do a reconversion 0132 QTextCodec* codec = QTextCodec::codecForLocale(); 0133 if(codec->name().toLower().indexOf("utf-8") == -1) { 0134 convertUTF8 = true; 0135 } 0136 } 0137 gotFirstLine = true; 0138 continue; 0139 } 0140 0141 bool ok; 0142 0143 Data::EntryPtr entry(new Data::Entry(m_coll)); 0144 entry->setId(Tellico::toUInt(values[0], &ok)); 0145 entry->setField(QStringLiteral("title"), values[1]); 0146 QRegularExpressionMatch yearMatch = year.match(values[2]); 0147 if(yearMatch.hasMatch()) { 0148 entry->setField(QStringLiteral("year"), yearMatch.captured()); 0149 } 0150 0151 uint time = 0; 0152 QRegularExpressionMatch runTimeMatch = runTimeHr.match(values[3]); 0153 if(runTimeMatch.hasMatch()) { 0154 time = Tellico::toUInt(runTimeMatch.captured(1), &ok) * 60; 0155 } 0156 runTimeMatch = runTimeMin.match(values[3]); 0157 if(runTimeMatch.hasMatch()) { 0158 time += Tellico::toUInt(runTimeMatch.captured(1), &ok); 0159 } 0160 if(time > 0) { 0161 entry->setField(QStringLiteral("running-time"), QString::number(time)); 0162 } 0163 0164 entry->setField(QStringLiteral("director"), splitJoin(rx, values[4])); 0165 entry->setField(QStringLiteral("nationality"), splitJoin(rx, values[5])); 0166 entry->setField(QStringLiteral("genre"), splitJoin(rx, values[6])); 0167 QUrl u = QUrl(values[7]); 0168 if(!u.isEmpty()) { 0169 QString id = ImageFactory::addImage(u, true /* quiet */); 0170 if(!id.isEmpty()) { 0171 entry->setField(QStringLiteral("cover"), id); 0172 } 0173 } 0174 entry->setField(QStringLiteral("cast"), splitJoin(rx, values[8])); 0175 // values[9] is the original title 0176 entry->setField(QStringLiteral("plot"), values[10]); 0177 if(hasURL) { 0178 entry->setField(QStringLiteral("url"), values[11]); 0179 } 0180 0181 CHECKLIMITS(12); 0182 0183 // values[12] is whether the film has been viewed or not 0184 entry->setField(QStringLiteral("medium"), values[13]); 0185 // values[14] is number of DVDS? 0186 // values[15] is place? 0187 // gcfilms's ratings go 0-10, just divide by two 0188 entry->setField(QStringLiteral("rating"), QString::number(int(Tellico::toUInt(values[16], &ok)/2))); 0189 entry->setField(QStringLiteral("comments"), values[17]); 0190 0191 CHECKLIMITS(18); 0192 0193 QStringList s = values[18].split(QLatin1Char(',')); 0194 QStringList tracks, langs; 0195 foreach(const QString& value, s) { 0196 langs << value.section(QLatin1Char(';'), 0, 0); 0197 tracks << value.section(QLatin1Char(';'), 1, 1); 0198 } 0199 entry->setField(QStringLiteral("language"), langs.join(FieldFormat::delimiterString())); 0200 entry->setField(QStringLiteral("audio-track"), tracks.join(FieldFormat::delimiterString())); 0201 0202 entry->setField(QStringLiteral("subtitle"), splitJoin(rx, values[19])); 0203 0204 CHECKLIMITS(20); 0205 0206 // values[20] is borrower name 0207 if(!values[20].isEmpty()) { 0208 QString tmp = values[20]; 0209 Data::BorrowerPtr b = borrowers[tmp]; 0210 if(!b) { 0211 b = new Data::Borrower(tmp, QString()); 0212 borrowers.insert(tmp, b); 0213 } 0214 // values[21] is loan date 0215 if(!values[21].isEmpty()) { 0216 tmp = values[21]; // assume date is dd/mm/yyyy 0217 int d = Tellico::toUInt(tmp.section(QLatin1Char('/'), 0, 0), &ok); 0218 int m = Tellico::toUInt(tmp.section(QLatin1Char('/'), 1, 1), &ok); 0219 int y = Tellico::toUInt(tmp.section(QLatin1Char('/'), 2, 2), &ok); 0220 b->addLoan(Data::LoanPtr(new Data::Loan(entry, QDate(y, m, d), QDate(), QString()))); 0221 entry->setField(QStringLiteral("loaned"), QStringLiteral("true")); 0222 } 0223 } 0224 // values[22] is history ? 0225 // for certification, only thing we can do is assume default american ratings 0226 // they're not translated one for one 0227 CHECKLIMITS(23); 0228 0229 int age = Tellico::toUInt(values[23], &ok); 0230 if(age < 2) { 0231 entry->setField(QStringLiteral("certification"), QStringLiteral("U (USA)")); 0232 } else if(age < 3) { 0233 entry->setField(QStringLiteral("certification"), QStringLiteral("G (USA)")); 0234 } else if(age < 6) { 0235 entry->setField(QStringLiteral("certification"), QStringLiteral("PG (USA)")); 0236 } else if(age < 14) { 0237 entry->setField(QStringLiteral("certification"), QStringLiteral("PG-13 (USA)")); 0238 } else { 0239 entry->setField(QStringLiteral("certification"), QStringLiteral("R (USA)")); 0240 } 0241 0242 m_coll->addEntries(entry); 0243 0244 if(showProgress && j%stepSize == 0) { 0245 emit signalProgress(this, j); 0246 } 0247 } 0248 0249 if(m_cancelled) { 0250 m_coll = nullptr; 0251 return; 0252 } 0253 0254 for(QHash<QString, Data::BorrowerPtr>::Iterator it = borrowers.begin(); it != borrowers.end(); ++it) { 0255 if(!it.value()->isEmpty()) { 0256 m_coll->addBorrower(it.value()); 0257 } 0258 } 0259 } 0260 0261 void GCstarImporter::readGCstar(const QString& text_) { 0262 QString xsltFile = DataFileRegistry::self()->locate(QStringLiteral("gcstar2tellico.xsl")); 0263 XSLTHandler handler(QUrl::fromLocalFile(xsltFile)); 0264 if(!handler.isValid()) { 0265 setStatusMessage(i18n("Tellico encountered an error in XSLT processing.")); 0266 return; 0267 } 0268 0269 const QString str = handler.applyStylesheet(text_); 0270 0271 if(str.isEmpty()) { 0272 setStatusMessage(i18n("<qt>The file is not a valid GCstar data file.</qt>")); 0273 return; 0274 } 0275 0276 Import::TellicoImporter imp(str); 0277 if(m_relativeImageLinks) { 0278 imp.setBaseUrl(url()); /// empty base url is ok 0279 } 0280 m_coll = imp.collection(); 0281 setStatusMessage(imp.statusMessage()); 0282 } 0283 0284 inline 0285 QString GCstarImporter::splitJoin(const QRegularExpression& rx, const QString& s) { 0286 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) 0287 return s.split(rx, QString::SkipEmptyParts).join(FieldFormat::delimiterString()); 0288 #else 0289 return s.split(rx, Qt::SkipEmptyParts).join(FieldFormat::delimiterString()); 0290 #endif 0291 } 0292 0293 void GCstarImporter::slotCancel() { 0294 m_cancelled = true; 0295 } 0296 0297 #undef CHECKLIMITS