diff --git a/0001-Remove-Allocine-data-source.patch b/0001-Remove-Allocine-data-source.patch deleted file mode 100644 index a5dcdd9..0000000 --- a/0001-Remove-Allocine-data-source.patch +++ /dev/null @@ -1,1638 +0,0 @@ -From 606ec8e018811d2e93d86037dcc688fb77c61ff7 Mon Sep 17 00:00:00 2001 -From: Robby Stephenson -Date: Sun, 11 Feb 2024 13:05:32 -0500 -Subject: [PATCH] Remove Allocine data source - -API has been discontinued ---- - doc/configuration.docbook | 8 - - src/fetch/CMakeLists.txt | 1 - - src/fetch/allocinefetcher.cpp | 516 -------------------------- - src/fetch/allocinefetcher.h | 134 ------- - src/fetch/fetch.h | 2 +- - src/fetch/fetcherinitializer.cpp | 2 - - src/fetch/fetchmanager.cpp | 2 - - src/fetch/scripts/CMakeLists.txt | 2 - - src/fetch/scripts/fr.allocine.py | 475 ------------------------ - src/fetch/scripts/fr.allocine.py.spec | 39 -- - src/tests/CMakeLists.txt | 14 - - src/tests/allocinefetchertest.cpp | 220 ----------- - src/tests/allocinefetchertest.h | 54 --- - 13 files changed, 1 insertion(+), 1468 deletions(-) - delete mode 100644 src/fetch/allocinefetcher.cpp - delete mode 100644 src/fetch/allocinefetcher.h - delete mode 100755 src/fetch/scripts/fr.allocine.py - delete mode 100644 src/fetch/scripts/fr.allocine.py.spec - delete mode 100644 src/tests/allocinefetchertest.cpp - delete mode 100644 src/tests/allocinefetchertest.h - -diff --git a/doc/configuration.docbook b/doc/configuration.docbook -index f1d6dbb..a577f30 100644 ---- a/doc/configuration.docbook -+++ b/doc/configuration.docbook -@@ -166,7 +166,6 @@ while the full list is ava - OPDS catalogs, - - the Internet Movie Database, --AlloCiné, - TheMovieDB.org, - the Open Movie Database, - FilmAffinity, -@@ -363,13 +362,6 @@ The Internet Movie Database provides in - - - -- --AlloCiné -- --AlloCiné is an online movie information service, based in France. -- -- -- - - FilmAffinity - -diff --git a/src/fetch/CMakeLists.txt b/src/fetch/CMakeLists.txt -index 0b54c93..d93542f 100644 ---- a/src/fetch/CMakeLists.txt -+++ b/src/fetch/CMakeLists.txt -@@ -4,7 +4,6 @@ ADD_SUBDIRECTORY( scripts ) - - SET(fetch_STAT_SRCS - adsfetcher.cpp -- allocinefetcher.cpp - amazonfetcher.cpp - amazonrequest.cpp - animenfofetcher.cpp -diff --git a/src/fetch/allocinefetcher.cpp b/src/fetch/allocinefetcher.cpp -deleted file mode 100644 -index bc05285..0000000 ---- a/src/fetch/allocinefetcher.cpp -+++ /dev/null -@@ -1,516 +0,0 @@ --/*************************************************************************** -- Copyright (C) 2012-2022 Robby Stephenson -- ***************************************************************************/ -- --/*************************************************************************** -- * * -- * This program is free software; you can redistribute it and/or * -- * modify it under the terms of the GNU General Public License as * -- * published by the Free Software Foundation; either version 2 of * -- * the License or (at your option) version 3 or any later version * -- * accepted by the membership of KDE e.V. (or its successor approved * -- * by the membership of KDE e.V.), which shall act as a proxy * -- * defined in Section 14 of version 3 of the license. * -- * * -- * This program is distributed in the hope that it will be useful, * -- * but WITHOUT ANY WARRANTY; without even the implied warranty of * -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -- * GNU General Public License for more details. * -- * * -- * You should have received a copy of the GNU General Public License * -- * along with this program. If not, see . * -- * * -- ***************************************************************************/ -- --#include // for TELLICO_VERSION -- --#include "allocinefetcher.h" --#include "../collections/videocollection.h" --#include "../images/imagefactory.h" --#include "../entry.h" --#include "../utils/guiproxy.h" --#include "../utils/string_utils.h" --#include "../core/filehandler.h" --#include "../tellico_debug.h" -- --#include --#include --#include --#include --#include -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --namespace { -- static const char* ALLOCINE_API_KEY = "100ED1DA33EB"; -- static const char* ALLOCINE_API_URL = "http://api.allocine.fr/rest/v3/"; -- static const char* ALLOCINE_PARTNER_KEY = "1a1ed8c1bed24d60ae3472eed1da33eb"; --} -- --using namespace Tellico; --using Tellico::Fetch::AbstractAllocineFetcher; --using Tellico::Fetch::AllocineFetcher; -- --AbstractAllocineFetcher::AbstractAllocineFetcher(QObject* parent_, const QString& baseUrl_) -- : Fetcher(parent_) -- , m_started(false) -- , m_apiKey(QLatin1String(ALLOCINE_API_KEY)) -- , m_baseUrl(baseUrl_) -- , m_numCast(10) { -- Q_ASSERT(!m_baseUrl.isEmpty()); --} -- --AbstractAllocineFetcher::~AbstractAllocineFetcher() { --} -- --bool AbstractAllocineFetcher::canSearch(Fetch::FetchKey k) const { -- return k == Keyword; --} -- --bool AbstractAllocineFetcher::canFetch(int type) const { -- return type == Data::Collection::Video; --} -- --void AbstractAllocineFetcher::readConfigHook(const KConfigGroup& config_) { -- QString k = config_.readEntry("API Key", ALLOCINE_API_KEY); -- if(!k.isEmpty()) { -- m_apiKey = k; -- } -- m_numCast = config_.readEntry("Max Cast", 10); --} -- --void AbstractAllocineFetcher::search() { -- m_started = true; -- -- const QString method(QStringLiteral("search")); -- -- QUrl u(m_baseUrl); -- u = u.adjusted(QUrl::StripTrailingSlash); -- u.setPath(u.path() + QLatin1Char('/') + method); -- -- // the order of the parameters appears to matter -- QList > params; -- params.append(qMakePair(QStringLiteral("partner"), m_apiKey)); -- -- // I can't figure out how to encode accent marks, but they don't -- // seem to be necessary -- QString q = removeAccents(request().value()); -- // should I just remove all non alphabetical characters? -- // see https://bugs.kde.org/show_bug.cgi?id=337432 -- q.remove(QRegularExpression(QStringLiteral("[,:!?;\\(\\)]"))); -- q.replace(QLatin1Char('\''), QLatin1Char('+')); -- q.replace(QLatin1Char(' '), QLatin1Char('+')); -- -- switch(request().key()) { -- case Keyword: -- params.append(qMakePair(QStringLiteral("q"), q)); -- break; -- -- default: -- myWarning() << source() << "- key not recognized:" << request().key(); -- stop(); -- return; -- } -- -- params.append(qMakePair(QStringLiteral("format"), QStringLiteral("json"))); -- params.append(qMakePair(QStringLiteral("filter"), QStringLiteral("movie"))); -- -- const QString sed = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd")); -- params.append(qMakePair(QStringLiteral("sed"), sed)); -- -- const QByteArray sig = calculateSignature(method, params); -- -- QUrlQuery query; -- query.setQueryItems(params); -- query.addQueryItem(QStringLiteral("sig"), QLatin1String(sig)); -- u.setQuery(query); --// myDebug() << u; -- -- m_job = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo); -- configureJob(m_job); -- KJobWidgets::setWindow(m_job, GUI::Proxy::widget()); -- connect(m_job.data(), &KJob::result, this, &AbstractAllocineFetcher::slotComplete); --} -- --void AbstractAllocineFetcher::stop() { -- if(!m_started) { -- return; -- } -- if(m_job) { -- m_job->kill(); -- } -- m_started = false; -- emit signalDone(this); --} -- --Tellico::Data::EntryPtr AbstractAllocineFetcher::fetchEntryHook(uint uid_) { -- Data::EntryPtr entry = m_entries.value(uid_); -- if(!entry) { -- myWarning() << "no entry in dict"; -- return Data::EntryPtr(); -- } -- -- QString code = entry->field(QStringLiteral("allocine-code")); -- if(code.isEmpty()) { -- // could mean we already updated the entry -- myDebug() << "no allocine release found"; -- return entry; -- } -- const QString method(QStringLiteral("movie")); -- -- QUrl u(m_baseUrl); -- u = u.adjusted(QUrl::StripTrailingSlash); -- u.setPath(u.path() + QLatin1Char('/') + method); -- -- // the order of the parameters appears to matter -- QList > params; -- params.append(qMakePair(QStringLiteral("partner"), m_apiKey)); -- params.append(qMakePair(QStringLiteral("code"), code)); -- params.append(qMakePair(QStringLiteral("profile"), QStringLiteral("large"))); -- params.append(qMakePair(QStringLiteral("filter"), QStringLiteral("movie"))); -- params.append(qMakePair(QStringLiteral("format"), QStringLiteral("json"))); -- -- const QString sed = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd")); -- params.append(qMakePair(QStringLiteral("sed"), sed)); -- -- const QByteArray sig = calculateSignature(method, params); -- -- QUrlQuery query; -- query.setQueryItems(params); -- query.addQueryItem(QStringLiteral("sig"), QLatin1String(sig)); -- u.setQuery(query); --// myDebug() << "url: " << u; --// QByteArray data = FileHandler::readDataFile(u, true); -- KIO::StoredTransferJob* dataJob = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo); -- configureJob(dataJob); -- if(!dataJob->exec()) { -- myDebug() << "Failed to load" << u; -- return entry; -- } -- const QByteArray data = dataJob->data(); -- --#if 0 -- myWarning() << "Remove debug2 from allocinefetcher.cpp"; -- QFile f(QString::fromLatin1("/tmp/test2.json")); -- if(f.open(QIODevice::WriteOnly)) { -- QTextStream t(&f); -- t.setCodec("UTF-8"); -- t << data; -- } -- f.close(); --#endif -- -- QJsonParseError error; -- QJsonDocument doc = QJsonDocument::fromJson(data, &error); -- QVariantMap result = doc.object().toVariantMap().value(QStringLiteral("movie")).toMap(); -- if(error.error != QJsonParseError::NoError) { -- myDebug() << "Bad JSON results"; --#if 0 -- myWarning() << "Remove debug3 from allocinefetcher.cpp"; -- QFile f2(QString::fromLatin1("/tmp/test3.json")); -- if(f2.open(QIODevice::WriteOnly)) { -- QTextStream t(&f2); -- t.setCodec("UTF-8"); -- t << data; -- } -- f2.close(); --#endif -- return entry; -- } -- populateEntry(entry, result); -- -- // image might still be a URL -- const QString image_id = entry->field(QStringLiteral("cover")); -- if(image_id.contains(QLatin1Char('/'))) { -- const QString id = ImageFactory::addImage(QUrl::fromUserInput(image_id), true /* quiet */); -- if(id.isEmpty()) { -- message(i18n("The cover image could not be loaded."), MessageHandler::Warning); -- } -- // empty image ID is ok -- entry->setField(QStringLiteral("cover"), id); -- } -- -- // don't want to include id -- entry->collection()->removeField(QStringLiteral("allocine-code")); -- QStringList castRows = FieldFormat::splitTable(entry->field(QStringLiteral("cast"))); -- while(castRows.count() > m_numCast) { -- castRows.removeLast(); -- } -- entry->setField(QStringLiteral("cast"), castRows.join(FieldFormat::rowDelimiterString())); -- return entry; --} -- --void AbstractAllocineFetcher::slotComplete(KJob*) { -- if(m_job->error()) { -- myDebug() << "Error:" << m_job->errorString(); -- m_job->uiDelegate()->showErrorMessage(); -- stop(); -- return; -- } -- -- QByteArray data = m_job->data(); -- if(data.isEmpty()) { -- myDebug() << "no data"; -- stop(); -- return; -- } -- // see bug 319662. If fetcher is cancelled, job is killed -- // if the pointer is retained, it gets double-deleted -- m_job = nullptr; -- --#if 0 -- myWarning() << "Remove debug from allocinefetcher.cpp"; -- QFile f(QString::fromLatin1("/tmp/test.json")); -- if(f.open(QIODevice::WriteOnly)) { -- QTextStream t(&f); -- t.setCodec("UTF-8"); -- t << data; -- } -- f.close(); --#endif -- -- QJsonDocument doc = QJsonDocument::fromJson(data); -- QVariantMap result = doc.object().toVariantMap().value(QStringLiteral("feed")).toMap(); --// myDebug() << "total:" << result.value(QLatin1String("totalResults")); -- -- QVariantList resultList = result.value(QStringLiteral("movie")).toList(); -- if(resultList.isEmpty()) { -- myDebug() << "no results"; -- stop(); -- return; -- } -- -- foreach(const QVariant& result, resultList) { -- // myDebug() << "found result:" << result; -- -- //create a new collection for every result since we end up removing the allocine code field -- // when fetchEntryHook is called. See bug 338389 -- Data::EntryPtr entry(new Data::Entry(createCollection())); -- populateEntry(entry, result.toMap()); -- -- FetchResult* r = new FetchResult(this, entry); -- m_entries.insert(r->uid, entry); -- emit signalResultFound(r); -- } -- -- m_hasMoreResults = false; -- stop(); --} -- --Tellico::Data::CollPtr AbstractAllocineFetcher::createCollection() const { -- Data::CollPtr coll(new Data::VideoCollection(true)); -- // always add the allocine release code for fetchEntryHook -- Data::FieldPtr field(new Data::Field(QStringLiteral("allocine-code"), QStringLiteral("Allocine Code"), Data::Field::Number)); -- field->setCategory(i18n("General")); -- coll->addField(field); -- -- // add new fields -- if(optionalFields().contains(QStringLiteral("allocine"))) { -- Data::FieldPtr field(new Data::Field(QStringLiteral("allocine"), i18n("Allocine Link"), Data::Field::URL)); -- field->setCategory(i18n("General")); -- coll->addField(field); -- } -- if(optionalFields().contains(QStringLiteral("origtitle"))) { -- Data::FieldPtr f(new Data::Field(QStringLiteral("origtitle"), i18n("Original Title"))); -- f->setFormatType(FieldFormat::FormatTitle); -- coll->addField(f); -- } -- -- return coll; --} -- --void AbstractAllocineFetcher::populateEntry(Data::EntryPtr entry, const QVariantMap& resultMap) { -- if(entry->collection()->hasField(QStringLiteral("allocine-code"))) { -- entry->setField(QStringLiteral("allocine-code"), mapValue(resultMap, "code")); -- } -- -- entry->setField(QStringLiteral("title"), mapValue(resultMap, "title")); -- if(optionalFields().contains(QStringLiteral("origtitle"))) { -- entry->setField(QStringLiteral("origtitle"), mapValue(resultMap, "originalTitle")); -- } -- if(entry->title().isEmpty()) { -- entry->setField(QStringLiteral("title"), mapValue(resultMap, "originalTitle")); -- } -- entry->setField(QStringLiteral("year"), mapValue(resultMap, "productionYear")); -- entry->setField(QStringLiteral("plot"), mapValue(resultMap, "synopsis")); -- -- const int runTime = mapValue(resultMap, "runtime").toInt(); -- entry->setField(QStringLiteral("running-time"), QString::number(runTime/60)); -- -- const QVariantList castList = resultMap.value(QStringLiteral("castMember")).toList(); -- QStringList actors, directors, producers, composers; -- foreach(const QVariant& castVariant, castList) { -- const QVariantMap castMap = castVariant.toMap(); -- const int code = mapValue(castMap, "activity", "code").toInt(); -- switch(code) { -- case 8001: -- actors << (mapValue(castMap, "person", "name") + FieldFormat::columnDelimiterString() + mapValue(castMap, "role")); -- break; -- case 8002: -- directors << mapValue(castMap, "person", "name"); -- break; -- case 8029: -- producers << mapValue(castMap, "person", "name"); -- break; -- case 8003: -- composers << mapValue(castMap, "person", "name"); -- break; -- } -- } -- entry->setField(QStringLiteral("cast"), actors.join(FieldFormat::rowDelimiterString())); -- entry->setField(QStringLiteral("director"), directors.join(FieldFormat::delimiterString())); -- entry->setField(QStringLiteral("producer"), producers.join(FieldFormat::delimiterString())); -- entry->setField(QStringLiteral("composer"), composers.join(FieldFormat::delimiterString())); -- -- const QVariantMap releaseMap = resultMap.value(QStringLiteral("release")).toMap(); -- entry->setField(QStringLiteral("studio"), mapValue(releaseMap, "distributor", "name")); -- -- QStringList genres; -- foreach(const QVariant& variant, resultMap.value(QLatin1String("genre")).toList()) { -- genres << i18n(mapValue(variant.toMap(), "$").toUtf8().constData()); -- } -- entry->setField(QStringLiteral("genre"), genres.join(FieldFormat::delimiterString())); -- -- QStringList nats; -- foreach(const QVariant& variant, resultMap.value(QLatin1String("nationality")).toList()) { -- nats << mapValue(variant.toMap(), "$"); -- } -- entry->setField(QStringLiteral("nationality"), nats.join(FieldFormat::delimiterString())); -- -- QStringList langs; -- foreach(const QVariant& variant, resultMap.value(QLatin1String("language")).toList()) { -- langs << mapValue(variant.toMap(), "$"); -- } -- entry->setField(QStringLiteral("language"), langs.join(FieldFormat::delimiterString())); -- -- const QVariantMap colorMap = resultMap.value(QLatin1String("color")).toMap(); -- if(colorMap.value(QStringLiteral("code")) == QLatin1String("12001")) { -- entry->setField(QStringLiteral("color"), i18n("Color")); -- } -- -- entry->setField(QStringLiteral("cover"), mapValue(resultMap, "poster", "href")); -- -- if(optionalFields().contains(QStringLiteral("allocine"))) { -- entry->setField(QStringLiteral("allocine"), mapValue(resultMap, "link", "href")); -- } --} -- --Tellico::Fetch::FetchRequest AbstractAllocineFetcher::updateRequest(Data::EntryPtr entry_) { -- QString title = entry_->field(QStringLiteral("title")); -- if(!title.isEmpty()) { -- return FetchRequest(Keyword, title); -- } -- return FetchRequest(); --} -- --void AbstractAllocineFetcher::configureJob(KIO::StoredTransferJob* job_) { -- // 10/8/17: UserAgent appears necessary to receive data -- job_->addMetaData(QLatin1String("SendUserAgent"), QLatin1String("true")); -- job_->addMetaData(QStringLiteral("UserAgent"), QStringLiteral("Tellico/%1") -- .arg(QStringLiteral(TELLICO_VERSION))); -- job_->addMetaData(QLatin1String("Languages"), QLatin1String("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3")); --} -- --AbstractAllocineFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const AbstractAllocineFetcher* fetcher_) -- : Fetch::ConfigWidget(parent_) { -- QGridLayout* l = new QGridLayout(optionsWidget()); -- l->setSpacing(4); -- l->setColumnStretch(1, 10); -- -- int row = -1; -- -- QLabel* label = new QLabel(i18n("&Maximum cast: "), optionsWidget()); -- l->addWidget(label, ++row, 0); -- m_numCast = new QSpinBox(optionsWidget()); -- m_numCast->setMaximum(99); -- m_numCast->setMinimum(0); -- m_numCast->setValue(10); --#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) -- void (QSpinBox::* textChanged)(const QString&) = &QSpinBox::valueChanged; --#else -- void (QSpinBox::* textChanged)(const QString&) = &QSpinBox::textChanged; --#endif -- connect(m_numCast, textChanged, this, &ConfigWidget::slotSetModified); -- l->addWidget(m_numCast, row, 1); -- QString w = i18n("The list of cast members may include many people. Set the maximum number returned from the search."); -- label->setWhatsThis(w); -- m_numCast->setWhatsThis(w); -- label->setBuddy(m_numCast); -- -- l->setRowStretch(++row, 10); -- -- m_numCast->setValue(fetcher_ ? fetcher_->m_numCast : 10); --} -- --void AbstractAllocineFetcher::ConfigWidget::saveConfigHook(KConfigGroup& config_) { -- config_.writeEntry("Max Cast", m_numCast->value()); --} -- --QByteArray AbstractAllocineFetcher::calculateSignature(const QString& method, const QList >& params_) { -- typedef QPair StringPair; -- QByteArray queryString; -- foreach(const StringPair& pair, params_) { -- queryString.append(pair.first.toUtf8().toPercentEncoding("+")); -- queryString.append('='); -- queryString.append(pair.second.toUtf8().toPercentEncoding("+")); -- queryString.append('&'); -- } -- // remove final '&' -- queryString.chop(1); -- -- const QByteArray toSign = method.toUtf8() + queryString + ALLOCINE_PARTNER_KEY; -- const QByteArray hash = QCryptographicHash::hash(toSign, QCryptographicHash::Sha1); -- return hash.toBase64(); --} -- --/**********************************************************************************************/ -- --AllocineFetcher::AllocineFetcher(QObject* parent_) -- : AbstractAllocineFetcher(parent_, QLatin1String(ALLOCINE_API_URL)) { --} -- --AllocineFetcher::~AllocineFetcher() { --} -- --QString AllocineFetcher::source() const { -- return m_name.isEmpty() ? defaultName() : m_name; --} -- --Tellico::Fetch::ConfigWidget* AllocineFetcher::configWidget(QWidget* parent_) const { -- return new AllocineFetcher::ConfigWidget(parent_, this); --} -- --QString AllocineFetcher::defaultName() { -- return QStringLiteral("AlloCiné.fr"); --} -- --QString AllocineFetcher::defaultIcon() { -- return favIcon("http://www.allocine.fr"); --} -- --Tellico::StringHash AllocineFetcher::allOptionalFields() { -- StringHash hash; -- hash[QStringLiteral("origtitle")] = i18n("Original Title"); -- hash[QStringLiteral("allocine")] = i18n("Allocine Link"); -- return hash; --} -- --AllocineFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const AbstractAllocineFetcher* fetcher_) -- : AbstractAllocineFetcher::ConfigWidget(parent_, fetcher_) { -- // now add additional fields widget -- addFieldsWidget(AllocineFetcher::allOptionalFields(), fetcher_ ? fetcher_->optionalFields() : QStringList()); --} -- --QString AllocineFetcher::ConfigWidget::preferredName() const { -- return AllocineFetcher::defaultName(); --} -diff --git a/src/fetch/allocinefetcher.h b/src/fetch/allocinefetcher.h -deleted file mode 100644 -index 946c053..0000000 ---- a/src/fetch/allocinefetcher.h -+++ /dev/null -@@ -1,134 +0,0 @@ --/*************************************************************************** -- Copyright (C) 2012 Robby Stephenson -- ***************************************************************************/ -- --/*************************************************************************** -- * * -- * This program is free software; you can redistribute it and/or * -- * modify it under the terms of the GNU General Public License as * -- * published by the Free Software Foundation; either version 2 of * -- * the License or (at your option) version 3 or any later version * -- * accepted by the membership of KDE e.V. (or its successor approved * -- * by the membership of KDE e.V.), which shall act as a proxy * -- * defined in Section 14 of version 3 of the license. * -- * * -- * This program is distributed in the hope that it will be useful, * -- * but WITHOUT ANY WARRANTY; without even the implied warranty of * -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -- * GNU General Public License for more details. * -- * * -- * You should have received a copy of the GNU General Public License * -- * along with this program. If not, see . * -- * * -- ***************************************************************************/ -- --#ifndef TELLICO_ALLOCINEFETCHER_H --#define TELLICO_ALLOCINEFETCHER_H -- --#include "xmlfetcher.h" --#include "configwidget.h" --#include "../datavectors.h" -- --#include -- --class QSpinBox; -- --class KJob; --namespace KIO { -- class StoredTransferJob; --} -- --namespace Tellico { -- -- namespace Fetch { -- --/** -- * An abstract fetcher for the Allocine family of web sites -- * -- * @author Robby Stephenson -- */ --class AbstractAllocineFetcher : public Fetcher { --Q_OBJECT -- --public: -- /** -- */ -- AbstractAllocineFetcher(QObject* parent, const QString& baseUrl); -- /** -- */ -- virtual ~AbstractAllocineFetcher(); -- -- virtual bool isSearching() const Q_DECL_OVERRIDE { return m_started; } -- virtual bool canSearch(FetchKey k) const Q_DECL_OVERRIDE; -- virtual void stop() Q_DECL_OVERRIDE; -- virtual Data::EntryPtr fetchEntryHook(uint uid) Q_DECL_OVERRIDE; -- virtual bool canFetch(int type) const Q_DECL_OVERRIDE; -- virtual void readConfigHook(const KConfigGroup& config) Q_DECL_OVERRIDE; -- -- class ConfigWidget : public Fetch::ConfigWidget { -- public: -- explicit ConfigWidget(QWidget* parent_, const AbstractAllocineFetcher* fetcher = nullptr); -- virtual void saveConfigHook(KConfigGroup&) Q_DECL_OVERRIDE; -- virtual QString preferredName() const Q_DECL_OVERRIDE = 0; -- private: -- QSpinBox* m_numCast; -- }; -- friend class ConfigWidget; -- --private Q_SLOTS: -- void slotComplete(KJob* job); -- --private: -- static QByteArray calculateSignature(const QString& method, const QList >& params); -- -- virtual void search() Q_DECL_OVERRIDE; -- virtual FetchRequest updateRequest(Data::EntryPtr entry) Q_DECL_OVERRIDE; -- Data::CollPtr createCollection() const; -- void populateEntry(Data::EntryPtr entry, const QVariantMap& resultMap); -- void configureJob(KIO::StoredTransferJob* job); -- -- QHash m_entries; -- QPointer m_job; -- -- bool m_started; -- QString m_apiKey; -- QString m_baseUrl; -- int m_numCast; --}; -- --/** -- * A fetcher for allocine.fr -- * -- * @author Robby Stephenson -- */ --class AllocineFetcher : public AbstractAllocineFetcher { --Q_OBJECT -- --public: -- /** -- */ -- AllocineFetcher(QObject* parent); -- ~AllocineFetcher(); -- -- virtual QString source() const Q_DECL_OVERRIDE; -- virtual Type type() const Q_DECL_OVERRIDE { return Allocine; } -- -- /** -- * Returns a widget for modifying the fetcher's config. -- */ -- virtual Fetch::ConfigWidget* configWidget(QWidget* parent) const Q_DECL_OVERRIDE; -- -- class ConfigWidget : public AbstractAllocineFetcher::ConfigWidget { -- public: -- explicit ConfigWidget(QWidget* parent_, const AbstractAllocineFetcher* fetcher = nullptr); -- virtual QString preferredName() const Q_DECL_OVERRIDE; -- }; -- -- static QString defaultName(); -- static QString defaultIcon(); -- static StringHash allOptionalFields(); --}; -- -- } // end namespace --} // end namespace --#endif -diff --git a/src/fetch/fetch.h b/src/fetch/fetch.h -index 116c8be..df772ea 100644 ---- a/src/fetch/fetch.h -+++ b/src/fetch/fetch.h -@@ -83,7 +83,7 @@ enum Type { - GoogleBook, - MAS, // Removed - Springer, -- Allocine, -+ Allocine, // Removed - ScreenRush, // Removed - FilmStarts, // Removed - SensaCine, // Removed -diff --git a/src/fetch/fetcherinitializer.cpp b/src/fetch/fetcherinitializer.cpp -index 7e23b5d..b1fa249 100644 ---- a/src/fetch/fetcherinitializer.cpp -+++ b/src/fetch/fetcherinitializer.cpp -@@ -55,7 +55,6 @@ - #include "moviemeterfetcher.h" - #include "googlebookfetcher.h" - #include "springerfetcher.h" --#include "allocinefetcher.h" - #include "thegamesdbfetcher.h" - #include "dblpfetcher.h" - #include "mrlookupfetcher.h" -@@ -111,7 +110,6 @@ Tellico::Fetch::FetcherInitializer::FetcherInitializer() { - RegisterFetcher registerGoogleBook(GoogleBook); - RegisterFetcher registerHathiTrust(HathiTrust); - RegisterFetcher registerVNDB(VNDB); -- RegisterFetcher registerAllocine(Allocine); - RegisterFetcher registerMovieMeter(MovieMeter); - RegisterFetcher registerDVDFr(DVDFr); - RegisterFetcher registerDouban(Douban); -diff --git a/src/fetch/fetchmanager.cpp b/src/fetch/fetchmanager.cpp -index 33b7b34..088f6ee 100644 ---- a/src/fetch/fetchmanager.cpp -+++ b/src/fetch/fetchmanager.cpp -@@ -27,7 +27,6 @@ - #include "fetchmanager.h" - #include "configwidget.h" - #include "messagehandler.h" --#include "../entry.h" - #include "../collection.h" - #include "../utils/string_utils.h" - #include "../utils/tellico_utils.h" -@@ -355,7 +354,6 @@ Tellico::Fetch::FetcherVec Manager::defaultFetchers() { - } - if(langs.contains(QStringLiteral("fr"))) { - FETCHER_ADD(DVDFr); -- FETCHER_ADD(Allocine); - } - if(langs.contains(QStringLiteral("ru"))) { - FETCHER_ADD(KinoPoisk); -diff --git a/src/fetch/scripts/CMakeLists.txt b/src/fetch/scripts/CMakeLists.txt -index 4d337c3..78f6bbd 100644 ---- a/src/fetch/scripts/CMakeLists.txt -+++ b/src/fetch/scripts/CMakeLists.txt -@@ -3,12 +3,10 @@ - - SET(SCRIPT_FILES - dark_horse_comics.py -- fr.allocine.py - ) - - SET(SPEC_FILES - dark_horse_comics.py.spec -- fr.allocine.py.spec - ) - - INSTALL(PROGRAMS ${SCRIPT_FILES} DESTINATION ${TELLICO_DATA_INSTALL_DIR}/data-sources ) -diff --git a/src/fetch/scripts/fr.allocine.py b/src/fetch/scripts/fr.allocine.py -deleted file mode 100755 -index a250443..0000000 ---- a/src/fetch/scripts/fr.allocine.py -+++ /dev/null -@@ -1,475 +0,0 @@ --#!/usr/bin/env python --# -*- coding: iso-8859-1 -*- --# kate: replace-tabs off; --# *************************************************************************** --# copyright : (C) 2006-2010 by Mathias Monnerville --# email : tellico@monnerville.com --# *************************************************************************** --# --# *************************************************************************** --# * * --# * This program is free software; you can redistribute it and/or modify * --# * it under the terms of version 2 of the GNU General Public License as * --# * published by the Free Software Foundation; * --# * * --# *************************************************************************** --# --# Version 0.7.3: 2010-12-07 (Reported by Romain Henriet) --# * Fixed some regexp issues --# * Better handling of image parsing/fetching errors --# --# Version 0.7.2.1: 2010-07-27 (Reported by Romain Henriet) --# * Updated title match to allow searching without diacritical marks --# --# Version 0.7.2: 2010-05-27 (Reported by Romain Henriet) --# * Fixed bug preventing searches with accent marks --# * Added post-processing cleanup action to replace raw HTML entities with --# their ISO Latin-1 replacement text --# --# Version 0.7.1: 2010-04-26 (Thanks to Romain Henriet ) --# * Fixed greedy regexp for genre. Fixed nationality output. Add studio. --# --# Version 0.7: 2009-11-12 --# * Allocine has a brand new website. All regexps were broken. --# --# Version 0.6: 2009-03-04 (Thanks to R. Fischer and Henry-Nicolas Tourneur) --# * Fixed parsing issues (various RegExp issues due to allocine's HTML changes) --# --# Version 0.5: 2009-01-21 (Changes contributed by R. Fischer ) --# * Added complete distribution of actors and roles, Genres, Nationalities, producers, composer and scenarist --# * Fixed the plot field that returned a wrong answer when no plot is available --# * Fixed a bug related to parameters encoding --# --# Version 0.4: --# * Fixed parsing errors: some fields in allocine's HTML pages have changed recently. Multiple actors and genres --# could not be retrieved. Fixed bad http request error due to some changes in HTML code. --# --# Version 0.3: --# * Fixed parsing: some fields in allocine's HTML pages have changed. Movie's image could not be fetched anymore. Fixed. --# --# Version 0.2: --# * Fixed parsing: allocine's HTML pages have changed. Movie's image could not be fetched anymore. --# --# Version 0.1: --# * Initial release. -- --import sys, os, re, hashlib, random, types --import urllib, time, base64 --import xml.dom.minidom --import locale --try: -- import htmlentitydefs as htmlents --except ImportError: -- try: -- from html.entities import entitydefs as htmlents -- except ImportError: -- print('Python 2.5+ required') -- raise -- --try: -- # For Python 3.0 and later -- from urllib.request import urlopen --except ImportError: -- # Fall back to Python 2's urllib2 -- from urllib2 import urlopen -- --XML_HEADER = """""" --DOCTYPE = """""" -- --VERSION = "0.7.3" -- --def genMD5(): -- float = random.random() -- return hashlib.md5(str(float)).hexdigest() -- --class BasicTellicoDOM: -- def __init__(self): -- self.__doc = xml.dom.minidom.Document() -- self.__root = self.__doc.createElement('tellico') -- self.__root.setAttribute('xmlns', 'http://periapsis.org/tellico/') -- self.__root.setAttribute('syntaxVersion', '9') -- -- self.__collection = self.__doc.createElement('collection') -- self.__collection.setAttribute('title', 'My Movies') -- self.__collection.setAttribute('type', '3') -- -- self.__fields = self.__doc.createElement('fields') -- # Add all default (standard) fields -- self.__dfltField = self.__doc.createElement('field') -- self.__dfltField.setAttribute('name', '_default') -- -- # Add a custom 'Collection' field -- self.__customField = self.__doc.createElement('field') -- self.__customField.setAttribute('name', 'titre-original') -- self.__customField.setAttribute('title', 'Original Title') -- self.__customField.setAttribute('flags', '0') -- self.__customField.setAttribute('category', unicode('Général', 'latin-1').encode('utf-8')) -- self.__customField.setAttribute('format', '1') -- self.__customField.setAttribute('type', '1') -- self.__customField.setAttribute('i18n', 'yes') -- -- self.__fields.appendChild(self.__dfltField) -- self.__fields.appendChild(self.__customField) -- self.__collection.appendChild(self.__fields) -- -- self.__images = self.__doc.createElement('images') -- -- self.__root.appendChild(self.__collection) -- self.__doc.appendChild(self.__root) -- -- # Current movie id -- self.__currentId = 0 -- -- -- def addEntry(self, movieData): -- """ -- Add a movie entry -- """ -- d = movieData -- entryNode = self.__doc.createElement('entry') -- entryNode.setAttribute('id', str(self.__currentId)) -- -- titleNode = self.__doc.createElement('title') -- titleNode.appendChild(self.__doc.createTextNode(d['title'])) -- -- otitleNode = self.__doc.createElement('titre-original') -- otitleNode.appendChild(self.__doc.createTextNode(d['otitle'])) -- -- yearNode = self.__doc.createElement('year') -- yearNode.appendChild(self.__doc.createTextNode(d['year'])) -- -- genresNode = self.__doc.createElement('genres') -- for g in d['genres']: -- genreNode = self.__doc.createElement('genre') -- genreNode.appendChild(self.__doc.createTextNode(g)) -- genresNode.appendChild(genreNode) -- -- studsNode = self.__doc.createElement('studios') -- for g in d['studio']: -- studNode = self.__doc.createElement('studio') -- studNode.appendChild(self.__doc.createTextNode(g)) -- studsNode.appendChild(studNode) -- -- natsNode = self.__doc.createElement('nationalitys') -- for g in d['nat']: -- natNode = self.__doc.createElement('nationality') -- natNode.appendChild(self.__doc.createTextNode(g)) -- natsNode.appendChild(natNode) -- -- castsNode = self.__doc.createElement('casts') -- i = 0 -- while i < len(d['actors']): -- g = d['actors'][i] -- h = d['actors'][i+1] -- castNode = self.__doc.createElement('cast') -- col1Node = self.__doc.createElement('column') -- col2Node = self.__doc.createElement('column') -- col1Node.appendChild(self.__doc.createTextNode(g)) -- col2Node.appendChild(self.__doc.createTextNode(h)) -- castNode.appendChild(col1Node) -- castNode.appendChild(col2Node) -- castsNode.appendChild(castNode) -- i = i + 2 -- -- dirsNode = self.__doc.createElement('directors') -- for g in d['dirs']: -- dirNode = self.__doc.createElement('director') -- dirNode.appendChild(self.__doc.createTextNode(g)) -- dirsNode.appendChild(dirNode) -- -- prodsNode = self.__doc.createElement('producers') -- for g in d['prods']: -- prodNode = self.__doc.createElement('producer') -- prodNode.appendChild(self.__doc.createTextNode(g)) -- prodsNode.appendChild(prodNode) -- -- scensNode = self.__doc.createElement('writers') -- for g in d['scens']: -- scenNode = self.__doc.createElement('writer') -- scenNode.appendChild(self.__doc.createTextNode(g)) -- scensNode.appendChild(scenNode) -- -- compsNode = self.__doc.createElement('composers') -- for g in d['comps']: -- compNode = self.__doc.createElement('composer') -- compNode.appendChild(self.__doc.createTextNode(g)) -- compsNode.appendChild(compNode) -- -- timeNode = self.__doc.createElement('running-time') -- timeNode.appendChild(self.__doc.createTextNode(d['time'])) -- -- allocineNode = self.__doc.createElement(unicode('allociné-link', 'latin-1').encode('utf-8')) -- allocineNode.appendChild(self.__doc.createTextNode(d['allocine'])) -- -- plotNode = self.__doc.createElement('plot') -- plotNode.appendChild(self.__doc.createTextNode(d['plot'])) -- -- if d['image']: -- imageNode = self.__doc.createElement('image') -- imageNode.setAttribute('format', 'JPEG') -- imageNode.setAttribute('id', d['image'][0]) -- imageNode.setAttribute('width', '120') -- imageNode.setAttribute('height', '160') -- imageNode.appendChild(self.__doc.createTextNode(d['image'][1])) -- -- coverNode = self.__doc.createElement('cover') -- coverNode.appendChild(self.__doc.createTextNode(d['image'][0])) -- -- for name in ( 'titleNode', 'otitleNode', 'yearNode', 'genresNode', 'studsNode', 'natsNode', -- 'castsNode', 'dirsNode', 'timeNode', 'allocineNode', 'plotNode', -- 'prodsNode', 'compsNode', 'scensNode' ): -- entryNode.appendChild(eval(name)) -- -- if d['image']: -- entryNode.appendChild(coverNode) -- self.__images.appendChild(imageNode) -- -- self.__collection.appendChild(entryNode) -- self.__currentId += 1 -- -- def printXML(self): -- """ -- Outputs XML content to stdout -- """ -- self.__collection.appendChild(self.__images) -- print(XML_HEADER); -- print(DOCTYPE) -- print(self.__root.toxml()) -- -- --class AlloCineParser: -- def __init__(self): -- self.__baseURL = 'http://www.allocine.fr' -- self.__basePath = '/film/fichefilm_gen_cfilm' -- self.__castPath = '/film/casting_gen_cfilm' -- self.__searchURL= 'http://www.allocine.fr/recherche/?q=%s' -- self.__movieURL = self.__baseURL + self.__basePath -- self.__castURL = self.__baseURL + self.__castPath -- -- # Define some regexps -- self.__regExps = { -- 'title' : '
(?P.+?)""", -- 'nat' : 'Nationalit.*?(?P.+?).*?Genre.*?(?P.+?)(?P.+?)[0-9])h *(?P[0-9]*).*?Ann', -- 'year' : 'Ann.*?e de production.*?(?P[0-9]{4})', -- 'otitle' : 'Titre original *?:*?.*?(?P.+?)', -- 'plot' : '

(?P.*?)

', -- 'image' : '
.*?http://.+?)\'.?', -- } -- -- self.__castRegExps = { --# 'roleactor' : '.*?(.*?).*?

.*?R.*?le : (?P.*?)

.*?', -- 'roleactor' : '(.*?).*?.*?)

.*?[\r\n\t]*Producteur[\r\n\t]*.*?(.*?)', -- 'scens' : '[\r\n\t]*Sc.*?nariste[\r\n\t]*.*?(.*?)', -- 'comps' : '[\r\n\t]*Compositeur[\r\n\t]*.*?(.*?)', -- } -- -- self.__domTree = BasicTellicoDOM() -- -- def run(self, title): -- """ -- Runs the allocine.fr parser: fetch movie related links, then fills and prints the DOM tree -- to stdout (in tellico format) so that tellico can use it. -- """ -- # the script needs the search string to be encoded in utf-8 -- try: -- # first try system encoding -- title = unicode(title, sys.stdin.encoding or sys.getdefaultencoding()) -- except UnicodeDecodeError: -- # on failure, fallback to 'latin-1' -- title = unicode(title, 'latin-1') -- -- # now encode for urllib -- title = title.encode('utf-8') -- self.__getMovie(title) -- # Print results to stdout -- self.__domTree.printXML() -- -- def __getHTMLContent(self, url): -- """ -- Fetch HTML data from url -- """ -- -- u = urlopen(url) -- self.__data = u.read() -- u.close() -- -- def __fetchMovieLinks(self, title): -- """ -- Retrieve all links related to movie -- @param title Movie title -- """ -- tmp = re.findall(""".*?.*?\.html?)['"] *?>(?P.*?)</a>""" % self.__basePath, self.__data, re.S | re.I) -- matchList = [] -- for match in tmp: -- name = re.sub(r'([\r\n]+|<b>|</b>)', '', match[1]) -- name = re.sub(r'<.*?>', '', name) -- name = re.sub(r'^ *', '', name) -- #if re.search(title, name, re.I): -- if len(name) > 0: -- matchList.append((match[0], name)) -- -- if not matchList: return None -- return matchList -- -- def __fetchMovieInfo(self, url, url2): -- """ -- Looks for movie information -- """ -- self.__getHTMLContent(url) -- matches = data = {} -- -- for name, regexp in self.__regExps.iteritems(): -- matches[name] = re.search(regexp, self.__data, re.S | re.I) -- -- if matches[name]: -- if name == 'title': -- data[name] = matches[name].group('title').strip() -- elif name == 'dirs': -- dirsList = re.sub('</?a.*?>', '', matches[name].group('step1')).split(',') -- data[name] = [] -- for d in dirsList: -- data[name].append(d.strip()) -- -- elif name == 'nat': -- natList = re.findall(r'<span class=".*?">(.*?)</span>', matches[name].group('nat'), re.DOTALL) -- data[name] = [] -- for d in natList: -- data[name].append(d.strip().capitalize()) -- -- elif name == 'genres': -- genresList = re.findall(r'<span itemprop="genre">(.*?)</span>', matches[name].group('step1'), re.DOTALL) -- data[name] = [] -- for d in genresList: -- data[name].append(d.strip().capitalize()) -- -- elif name == 'studio': -- studiosList = re.findall(r'<span itemprop="productionCompany">(.*?)</span>', matches[name].group('step1')) -- data[name] = [] -- for d in studiosList: -- data[name].append(d.strip()) -- -- elif name == 'time': -- h, m = matches[name].group('hours'), matches[name].group('mins') -- if len(m) == 0: -- m = 0 -- totmin = int(h)*60+int(m) -- data[name] = str(totmin) -- -- elif name == 'year': -- data[name] = matches[name].group('year').strip() -- -- elif name == 'otitle': -- otitle = re.sub(r'([\r\n]+|<em>|</em>)', '', matches[name].group('otitle')) -- data[name] = otitle.strip() -- -- elif name == 'plot': -- data[name] = matches[name].group('plot').strip() -- # Cleans up any HTML entities -- data[name] = self.__cleanUp(data[name]) -- -- else: -- matches[name] = '' -- -- # Image check -- try: -- imgtmp = re.findall(self.__regExps['image'], self.__data, re.S | re.I) -- matches['image'] = imgtmp[0] -- -- # Save image to a temporary folder -- md5 = genMD5() -- imObj = urlopen(matches['image'].strip()) -- img = imObj.read() -- imObj.close() -- imgPath = "/tmp/%s.jpeg" % md5 -- f = open(imgPath, 'w') -- f.write(img) -- f.close() -- -- # Base64 encoding -- data['image'] = (md5 + '.jpeg', base64.encodestring(img)) -- -- # Delete temporary image -- os.remove(imgPath) -- except: -- data['image'] = None -- -- # Now looks for casting information -- self.__getHTMLContent(url2) -- page = self.__data.split('\n') -- -- d = zone = 0 -- data['actors'] = [] -- data['prods'] = [] -- data['scens'] = [] -- data['comps'] = [] -- -- # Actors -- subset = re.search(r'Acteurs et actrices.*$', self.__data, re.S | re.I) -- if not subset: return data -- subset = subset.group(0) -- #print subset -- roleactor = re.findall(self.__castRegExps['roleactor'], subset, re.S | re.I) -- for ra in roleactor: -- #print ra -- data['actors'].append(re.sub(r'([\r\n\t]+)', '', ra[0])) -- data['actors'].append(re.sub(r'([\r\n\t]+)', '', ra[1])) -- -- # Producers, Scenarists, Composers -- for kind in ('prods', 'scens', 'comps'): -- data[kind] = [re.sub(r'([\r\n\t]+)', '', k).strip() for k in re.findall(self.__castRegExps[kind], subset, re.S | re.I)] -- -- return data -- -- def __cleanUp(self, data): -- """ -- Cleans up the string(s), replacing raw HTML entities with their -- ISO Latin-1 replacement text. -- @param data string or list of strings -- """ -- if type(data) == types.ListType: -- for s in data: -- for k, v in htmlents.entitydefs.iteritems(): -- s = s.replace("&%s;" % k, v) -- elif type(data) == types.StringType or type(data) == types.UnicodeType: -- for k, v in htmlents.entitydefs.iteritems(): -- data = data.replace("&%s;" % k, v) -- return data -- -- def __getMovie(self, title): -- if not len(title): return -- -- self.__title = title -- self.__getHTMLContent(self.__searchURL % urllib.quote(self.__title)) -- -- # Get all links -- links = self.__fetchMovieLinks(title) -- -- # Now retrieve info -- if links: -- for entry in links: -- data = self.__fetchMovieInfo( url = "%s=%s" % (self.__movieURL, entry[0]), url2 = "%s=%s" % (self.__castURL, entry[0]) ) -- # Add allocine link (custom field) -- data['allocine'] = "%s=%s" % (self.__movieURL, entry[0]) -- self.__domTree.addEntry(data) -- else: -- return None -- -- --def showUsage(): -- print("Usage: %s movietitle" % sys.argv[0]) -- sys.exit(1) -- --def main(): -- if len(sys.argv) < 2: -- showUsage() -- -- parser = AlloCineParser() -- parser.run(sys.argv[1]) -- --if __name__ == '__main__': -- main() -diff --git a/src/fetch/scripts/fr.allocine.py.spec b/src/fetch/scripts/fr.allocine.py.spec -deleted file mode 100644 -index 2ed8bf0..0000000 ---- a/src/fetch/scripts/fr.allocine.py.spec -+++ /dev/null -@@ -1,39 +0,0 @@ --Name=Allocine.fr --Name[ca]=Allocine.fr --Name[ca@valencia]=Allocine.fr --Name[cs]=Allocine.fr --Name[da]=Allocine.fr --Name[de]=Allocine.fr --Name[el]=Allocine.fr --Name[en_GB]=Allocine.fr --Name[eo]=Allocine.fr --Name[es]=Allocine.fr --Name[et]=Allocine.fr --Name[eu]=Allocine.fr --Name[fi]=Allocine.fr --Name[fr]=Allocine.fr --Name[gl]=Allocine.fr --Name[hi]=Allocine.fr --Name[hu]=Allocine.fr --Name[ia]=Allocine.fr --Name[it]=Allocine.fr --Name[ka]=Allocine.fr --Name[ko]=알로시네(프랑스) --Name[nl]=Allocine.fr --Name[nn]=Allocine.fr --Name[pl]=Allocine.fr --Name[pt]=Allocine.fr --Name[pt_BR]=Allocine.fr --Name[ru]=Allocine.fr --Name[sk]=Allocine.fr --Name[sl]=Allocine.fr --Name[sv]=Allocine.fr --Name[tr]=Allocine.fr --Name[uk]=Allocine.fr --Name[x-test]=xxAllocine.frxx --Type=data-source --ArgumentKeys=1 --Arguments=%1 --CollectionType=3 --FormatType=0 --UpdateArgs=%{title} -diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt -index aac0270..9bc2a90 100644 ---- a/src/tests/CMakeLists.txt -+++ b/src/tests/CMakeLists.txt -@@ -514,20 +514,6 @@ ecm_add_test(adsfetchertest.cpp - LINK_LIBRARIES fetcherstest ${TELLICO_TEST_LIBS} - ) - --ecm_add_test(allocinefetchertest.cpp -- ../fetch/allocinefetcher.cpp -- ../fetch/execexternalfetcher.cpp -- ../translators/bibteximporter.cpp -- ../translators/risimporter.cpp -- ../gui/collectiontypecombo.cpp -- TEST_NAME allocinefetchertest -- LINK_LIBRARIES fetcherstest -- translatorstest -- newstuff -- ${TELLICO_BTPARSE_LIBS} -- ${TELLICO_TEST_LIBS} --) -- - ecm_add_test(amazonfetchertest.cpp - ../fetch/amazonfetcher.cpp - ../fetch/amazonrequest.cpp -diff --git a/src/tests/allocinefetchertest.cpp b/src/tests/allocinefetchertest.cpp -deleted file mode 100644 -index 4dede49..0000000 ---- a/src/tests/allocinefetchertest.cpp -+++ /dev/null -@@ -1,220 +0,0 @@ --/*************************************************************************** -- Copyright (C) 2010-2012 Robby Stephenson <robby@periapsis.org> -- ***************************************************************************/ -- --/*************************************************************************** -- * * -- * This program is free software; you can redistribute it and/or * -- * modify it under the terms of the GNU General Public License as * -- * published by the Free Software Foundation; either version 2 of * -- * the License or (at your option) version 3 or any later version * -- * accepted by the membership of KDE e.V. (or its successor approved * -- * by the membership of KDE e.V.), which shall act as a proxy * -- * defined in Section 14 of version 3 of the license. * -- * * -- * This program is distributed in the hope that it will be useful, * -- * but WITHOUT ANY WARRANTY; without even the implied warranty of * -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -- * GNU General Public License for more details. * -- * * -- * You should have received a copy of the GNU General Public License * -- * along with this program. If not, see <http://www.gnu.org/licenses/>. * -- * * -- ***************************************************************************/ -- --#undef QT_NO_CAST_FROM_ASCII -- --#include "allocinefetchertest.h" -- --#include "../fetch/execexternalfetcher.h" --#include "../fetch/allocinefetcher.h" --#include "../collections/videocollection.h" --#include "../collectionfactory.h" --#include "../entry.h" --#include "../images/imagefactory.h" -- --#include <KSharedConfig> -- --#include <QTest> -- --QTEST_GUILESS_MAIN( AllocineFetcherTest ) -- --AllocineFetcherTest::AllocineFetcherTest() : AbstractFetcherTest() { --} -- --void AllocineFetcherTest::initTestCase() { -- Tellico::RegisterCollection<Tellico::Data::VideoCollection> registerVideo(Tellico::Data::Collection::Video, "video"); -- Tellico::ImageFactory::init(); -- -- m_config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)->group(QStringLiteral("allocine")); -- m_config.writeEntry("Max Cast", QStringLiteral("5")); -- m_config.writeEntry("Custom Fields", QStringLiteral("origtitle,allocine")); --} -- --void AllocineFetcherTest::cleanupTestCase() { -- Tellico::ImageFactory::clean(true); --} -- --void AllocineFetcherTest::testTitle() { -- // Allocine script is currently failing -- return; -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, -- QStringLiteral("Superman Returns")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); -- -- KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); -- KConfigGroup cg = config.group(QStringLiteral("<default>")); -- cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); -- fetcher->readConfig(cg); -- // don't sync() and save the new path -- cg.deleteEntry("ExecPath"); -- -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Superman Returns")); -- QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Bryan Singer")); -- QCOMPARE(entry->field(QStringLiteral("producer")), QStringLiteral("Jon Peters; Gilbert Adler; Bryan Singer; Lorne Orleans")); -- QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("Warner Bros. France")); -- QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2006")); -- QCOMPARE(entry->field(QStringLiteral("genre")), QStringLiteral("Fantastique; Action")); -- QCOMPARE(entry->field(QStringLiteral("nationality")), QString::fromUtf8("Américain; Australien")); -- QCOMPARE(entry->field(QStringLiteral("running-time")), QStringLiteral("154")); -- QStringList castList = Tellico::FieldFormat::splitTable(entry->field(QStringLiteral("cast"))); -- QVERIFY(!castList.isEmpty()); -- QCOMPARE(castList.at(0), QStringLiteral("Brandon Routh::Clark Kent / Superman")); -- QCOMPARE(castList.size(), 8); -- QVERIFY(!entry->field(QStringLiteral("plot")).isEmpty()); -- QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); -- QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); --} -- --void AllocineFetcherTest::testTitleAccented() { -- // Allocine script is currently failing -- return; -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, -- QStringLiteral("Opération Tonnerre")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); -- -- KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); -- KConfigGroup cg = config.group(QStringLiteral("<default>")); -- cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); -- fetcher->readConfig(cg); -- // don't sync() and save the new path -- cg.deleteEntry("ExecPath"); -- -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); -- QCOMPARE(entry->field(QStringLiteral("titre-original")), QStringLiteral("Thunderball")); -- QCOMPARE(entry->field(QStringLiteral("studio")), QString()); --} -- --void AllocineFetcherTest::testTitleAccentRemoved() { -- // Allocine script is currently failing -- return; -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, -- QStringLiteral("Operation Tonnerre")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); -- -- KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); -- KConfigGroup cg = config.group(QStringLiteral("<default>")); -- cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); -- fetcher->readConfig(cg); -- // don't sync() and save the new path -- cg.deleteEntry("ExecPath"); -- -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); --} -- --void AllocineFetcherTest::testPlotQuote() { -- // Allocine script is currently failing -- return; -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Title, -- QStringLiteral("Goldfinger")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::ExecExternalFetcher(this)); -- -- KConfig config(QFINDTESTDATA("../fetch/scripts/fr.allocine.py.spec"), KConfig::SimpleConfig); -- KConfigGroup cg = config.group(QStringLiteral("<default>")); -- cg.writeEntry("ExecPath", QFINDTESTDATA("../fetch/scripts/fr.allocine.py")); -- fetcher->readConfig(cg); -- // don't sync() and save the new path -- cg.deleteEntry("ExecPath"); -- -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Goldfinger")); -- QVERIFY(!entry->field(QStringLiteral("plot")).contains(QStringLiteral("""))); --} -- --void AllocineFetcherTest::testTitleAPI() { -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, -- QStringLiteral("Superman Returns")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); -- fetcher->readConfig(m_config); -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Superman Returns")); -- QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Bryan Singer")); -- QCOMPARE(entry->field(QStringLiteral("producer")), QStringLiteral("Jon Peters; Gilbert Adler; Bryan Singer; Lorne Orleans")); -- QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("Warner Bros. France")); -- QCOMPARE(entry->field(QStringLiteral("year")), QStringLiteral("2006")); -- QCOMPARE(entry->field(QStringLiteral("genre")), QStringLiteral("Fantastique; Action")); -- QCOMPARE(entry->field(QStringLiteral("nationality")), QStringLiteral("U.S.A.; Australie")); -- QCOMPARE(entry->field(QStringLiteral("running-time")), QStringLiteral("154")); -- QStringList castList = Tellico::FieldFormat::splitTable(entry->field(QStringLiteral("cast"))); -- QVERIFY(!castList.isEmpty()); -- QCOMPARE(castList.at(0), QStringLiteral("Brandon Routh::Clark Kent / Superman")); -- QCOMPARE(castList.size(), 5); -- QVERIFY(!entry->field(QStringLiteral("plot")).isEmpty()); -- QVERIFY(!entry->field(QStringLiteral("cover")).isEmpty()); -- QVERIFY(!entry->field(QStringLiteral("cover")).contains(QLatin1Char('/'))); --} -- --void AllocineFetcherTest::testTitleAPIAccented() { -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, -- QStringLiteral("Opération Tonnerre")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); -- fetcher->readConfig(m_config); -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QString::fromUtf8("Opération Tonnerre")); -- QCOMPARE(entry->field(QStringLiteral("origtitle")), QStringLiteral("Thunderball")); -- QCOMPARE(entry->field(QStringLiteral("studio")), QStringLiteral("United International Pictures (UIP)")); -- QCOMPARE(entry->field(QStringLiteral("director")), QStringLiteral("Terence Young")); -- QCOMPARE(entry->field(QStringLiteral("color")), QStringLiteral("Color")); -- QVERIFY(!entry->field(QStringLiteral("allocine")).isEmpty()); --} -- --// mentioned in https://bugs.kde.org/show_bug.cgi?id=337432 --void AllocineFetcherTest::testGhostDog() { -- Tellico::Fetch::FetchRequest request(Tellico::Data::Collection::Video, Tellico::Fetch::Keyword, -- QStringLiteral("Ghost Dog: la voie du samourai")); -- Tellico::Fetch::Fetcher::Ptr fetcher(new Tellico::Fetch::AllocineFetcher(this)); -- fetcher->readConfig(m_config); -- Tellico::Data::EntryList results = DO_FETCH1(fetcher, request, 1); -- -- QCOMPARE(results.size(), 1); -- -- Tellico::Data::EntryPtr entry = results.at(0); -- QCOMPARE(entry->field(QStringLiteral("title")), QStringLiteral("Ghost Dog: la voie du samourai")); --} -diff --git a/src/tests/allocinefetchertest.h b/src/tests/allocinefetchertest.h -deleted file mode 100644 -index 9219c94..0000000 ---- a/src/tests/allocinefetchertest.h -+++ /dev/null -@@ -1,54 +0,0 @@ --/*************************************************************************** -- Copyright (C) 2010-2012 Robby Stephenson <robby@periapsis.org> -- ***************************************************************************/ -- --/*************************************************************************** -- * * -- * This program is free software; you can redistribute it and/or * -- * modify it under the terms of the GNU General Public License as * -- * published by the Free Software Foundation; either version 2 of * -- * the License or (at your option) version 3 or any later version * -- * accepted by the membership of KDE e.V. (or its successor approved * -- * by the membership of KDE e.V.), which shall act as a proxy * -- * defined in Section 14 of version 3 of the license. * -- * * -- * This program is distributed in the hope that it will be useful, * -- * but WITHOUT ANY WARRANTY; without even the implied warranty of * -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -- * GNU General Public License for more details. * -- * * -- * You should have received a copy of the GNU General Public License * -- * along with this program. If not, see <http://www.gnu.org/licenses/>. * -- * * -- ***************************************************************************/ -- --#ifndef ALLOCINEFETCHERTEST_H --#define ALLOCINEFETCHERTEST_H -- --#include "abstractfetchertest.h" -- --#include <KConfigGroup> -- --class AllocineFetcherTest : public AbstractFetcherTest { --Q_OBJECT --public: -- AllocineFetcherTest(); -- --private Q_SLOTS: -- void initTestCase(); -- void cleanupTestCase(); -- -- void testTitle(); -- void testTitleAccented(); -- void testTitleAccentRemoved(); -- void testPlotQuote(); -- -- void testTitleAPI(); -- void testTitleAPIAccented(); -- void testGhostDog(); -- --private: -- KConfigGroup m_config; --}; -- --#endif --- -2.45.2 - diff --git a/tellico-3.5.5.tar.xz b/tellico-3.5.5.tar.xz deleted file mode 100644 index 1ffc025..0000000 --- a/tellico-3.5.5.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:001794c52e99b20feab8373440850549ccd6da0a1fe2345c6192f9385472d06c -size 6796536 diff --git a/tellico-4.0.tar.xz b/tellico-4.0.tar.xz new file mode 100644 index 0000000..e6281d6 --- /dev/null +++ b/tellico-4.0.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d4d05f3d430048d9d694a6e82371c9d54c2c28bf78fc7921db7d089351b4d33 +size 6794676 diff --git a/tellico.changes b/tellico.changes index 9d5cf76..086081e 100644 --- a/tellico.changes +++ b/tellico.changes @@ -1,3 +1,24 @@ +------------------------------------------------------------------- +Wed Sep 4 17:40:54 UTC 2024 - Christophe Marin <christophe@krop.fr> + +- Update to 4.0 + * https://tellico-project.org/blog/ + * Building with Qt6 is enabled by default + * Book and video collections can be imported from file + metadata (kde#214606) + * All entry templates were updated to include any loan + information (kde#411903) + * Creating and viewing the internal log file is supported + through the --log and --logfile command-line options (kde#426624) + * The DBUS interface can output to stdout using -- as the file name + * Choice fields are now allowed to have multiple values (kde#483831) + * The iTunes, Discogs, and MusicBrainz sources now separate + multi-disc albums (kde#479503) + * A configurable locale was added to the IMDb data source + * The Allocine and AnimeNFO data sources were removed +- Drop patch, merged upstream: + * 0001-Remove-Allocine-data-source.patch + ------------------------------------------------------------------- Tue Jun 25 07:05:58 UTC 2024 - Christophe Marin <christophe@krop.fr> diff --git a/tellico.spec b/tellico.spec index 1fbb9fe..eace1cd 100644 --- a/tellico.spec +++ b/tellico.spec @@ -16,66 +16,62 @@ # +%define kf6_version 6.0.0 +%define qt6_version 6.4.0 + Name: tellico -Version: 3.5.5 +Version: 4.0 Release: 0 Summary: A Collection Manager License: GPL-2.0-or-later URL: https://tellico-project.org/ Source0: https://tellico-project.org/files/%{name}-%{version}.tar.xz -# PATCH-FIX-UPSTREAM -Patch0: 0001-Remove-Allocine-data-source.patch -BuildRequires: extra-cmake-modules BuildRequires: fdupes -BuildRequires: libcsv-devel +BuildRequires: kf6-extra-cmake-modules >= %{kf6_version} +BuildRequires: libcsv-devel >= 3.0 BuildRequires: pkgconfig -BuildRequires: cmake(KF5Archive) -BuildRequires: cmake(KF5Cddb) -BuildRequires: cmake(KF5Codecs) -BuildRequires: cmake(KF5Config) -BuildRequires: cmake(KF5ConfigWidgets) -BuildRequires: cmake(KF5CoreAddons) -BuildRequires: cmake(KF5Crash) -BuildRequires: cmake(KF5DocTools) -BuildRequires: cmake(KF5FileMetaData) -BuildRequires: cmake(KF5GuiAddons) -BuildRequires: cmake(KF5I18n) -BuildRequires: cmake(KF5IconThemes) -BuildRequires: cmake(KF5ItemModels) -BuildRequires: cmake(KF5JobWidgets) -BuildRequires: cmake(KF5KIO) -BuildRequires: cmake(KF5NewStuff) -BuildRequires: cmake(KF5Sane) -BuildRequires: cmake(KF5Solid) -BuildRequires: cmake(KF5Sonnet) -BuildRequires: cmake(KF5TextWidgets) -BuildRequires: cmake(KF5Wallet) -BuildRequires: cmake(KF5WidgetsAddons) -BuildRequires: cmake(KF5XmlGui) -BuildRequires: cmake(Qt5Charts) -BuildRequires: cmake(Qt5Core) -BuildRequires: cmake(Qt5DBus) -BuildRequires: cmake(Qt5Network) -BuildRequires: cmake(Qt5PrintSupport) -BuildRequires: cmake(Qt5Test) -BuildRequires: cmake(Qt5Widgets) -BuildRequires: cmake(Qt5Xml) +BuildRequires: cmake(KCddb6) +BuildRequires: cmake(KF6Archive) >= %{kf6_version} +BuildRequires: cmake(KF6Codecs) >= %{kf6_version} +BuildRequires: cmake(KF6Completion) >= %{kf6_version} +BuildRequires: cmake(KF6Config) >= %{kf6_version} +BuildRequires: cmake(KF6ConfigWidgets) >= %{kf6_version} +BuildRequires: cmake(KF6CoreAddons) >= %{kf6_version} +BuildRequires: cmake(KF6Crash) >= %{kf6_version} +BuildRequires: cmake(KF6DocTools) >= %{kf6_version} +BuildRequires: cmake(KF6FileMetaData) >= %{kf6_version} +BuildRequires: cmake(KF6GuiAddons) >= %{kf6_version} +BuildRequires: cmake(KF6I18n) >= %{kf6_version} +BuildRequires: cmake(KF6IconThemes) >= %{kf6_version} +BuildRequires: cmake(KF6ItemModels) >= %{kf6_version} +BuildRequires: cmake(KF6JobWidgets) >= %{kf6_version} +BuildRequires: cmake(KF6KIO) >= %{kf6_version} +BuildRequires: cmake(KF6NewStuff) >= %{kf6_version} +BuildRequires: cmake(KF6Solid) >= %{kf6_version} +BuildRequires: cmake(KF6Sonnet) >= %{kf6_version} +BuildRequires: cmake(KF6TextWidgets) >= %{kf6_version} +BuildRequires: cmake(KF6WidgetsAddons) >= %{kf6_version} +BuildRequires: cmake(KF6XmlGui) >= %{kf6_version} +BuildRequires: cmake(KSaneWidgets6) +BuildRequires: cmake(Qt6Charts) >= %{qt6_version} +BuildRequires: cmake(Qt6Core) >= %{qt6_version} +BuildRequires: cmake(Qt6DBus) >= %{qt6_version} +BuildRequires: cmake(Qt6Network) >= %{qt6_version} +BuildRequires: cmake(Qt6PrintSupport) >= %{qt6_version} +BuildRequires: cmake(Qt6Test) >= %{qt6_version} +BuildRequires: cmake(Qt6WebEngineWidgets) >= %{qt6_version} +BuildRequires: cmake(Qt6Widgets) >= %{qt6_version} +BuildRequires: cmake(Qt6Xml) >= %{qt6_version} BuildRequires: pkgconfig(exempi-2.0) BuildRequires: pkgconfig(libcdio) BuildRequires: pkgconfig(libv4l2) BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(libxslt) -BuildRequires: pkgconfig(poppler-qt5) +BuildRequires: pkgconfig(poppler-qt6) BuildRequires: pkgconfig(taglib) -BuildRequires: pkgconfig(yaz) -# Needed to install/uninstall knewstuff downloads -Requires: /usr/bin/dbus-send -# QWebEngine is not available on ppc -%ifarch %{ix86} x86_64 %{x86_64} %{arm} aarch64 -BuildRequires: cmake(Qt5WebEngineWidgets) -%else -BuildRequires: cmake(KF5KHtml) -%endif +BuildRequires: pkgconfig(yaz) >= 2.0 +# Needs QtWebEngine +ExclusiveArch: x86_64 aarch64 riscv64 %description Tellico is an application for organizing your collections. It provides @@ -92,39 +88,34 @@ sed -i 's#env perl$#perl#' src/config/*-update.pl sed -i 's#env python$#python3#' src/fetch/scripts/*.py %build -%cmake_kf5 "-DENABLE_WEBCAM=true" -d build +%cmake_kf6 -DENABLE_WEBCAM:BOOL=TRUE -%cmake_build +%kf6_build %install -%kf5_makeinstall -C build +%kf6_install -%find_lang %{name} - -%{kf5_find_htmldocs} - -%{kf5_post_install} +%find_lang tellico tellico.lang --with-html %fdupes %{buildroot} %files %license COPYING %doc AUTHORS ChangeLog README.md -%config %{_kf5_configdir}/tellicorc -%dir %{_kf5_appsdir}/kconf_update -%doc %lang(en) %{_kf5_htmldir}/en/tellico/ -%{_datadir}/mime/packages/tellico.xml -%{_kf5_applicationsdir}/org.kde.tellico.desktop -%{_kf5_appsdir}/kconf_update/tellico* -%{_kf5_appsdir}/tellico/ -%{_kf5_appstreamdir}/org.kde.tellico.appdata.xml -%{_kf5_bindir}/tellico -%{_kf5_configkcfgdir}/tellico_config.kcfg -%{_kf5_iconsdir}/hicolor/*/apps/tellico.png -%{_kf5_iconsdir}/hicolor/*/mimetypes/application-x-tellico.png -%{_kf5_knsrcfilesdir}/tellico* -%{_kf5_kxmlguidir}/tellico/ +%doc %lang(en) %{_kf6_htmldir}/en/tellico/ +%config %{_kf6_configdir}/tellico* +%{_kf6_applicationsdir}/org.kde.tellico.desktop +%{_kf6_appstreamdir}/org.kde.tellico.appdata.xml +%{_kf6_bindir}/tellico +%{_kf6_configkcfgdir}/tellico_config.kcfg +%{_kf6_iconsdir}/hicolor/*/apps/tellico.png +%{_kf6_iconsdir}/hicolor/*/mimetypes/application-x-tellico.png +%{_kf6_knsrcfilesdir}/tellico-template.knsrc +%{_kf6_sharedir}/kconf_update/tellico* +%{_kf6_sharedir}/mime/packages/tellico.xml +%{_kf6_sharedir}/tellico/ %files lang -f %{name}.lang +%exclude %{_kf6_htmldir}/en/tellico/ %changelog