plasma: reimplement potd wallpaper and move it here

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2024-04-09 05:59:03 +03:00
parent 42a08098d1
commit 9bcff7ec63
6 changed files with 602 additions and 1 deletions

View file

@ -1,3 +1,4 @@
add_subdirectory(color)
add_subdirectory(image)
add_subdirectory(data)
add_subdirectory(potd)

View file

@ -0,0 +1,23 @@
project(plasma-wallpaper-potd)
set(potd_SRCS
potd.cpp
)
kde4_add_plugin(plasma_wallpaper_potd ${potd_SRCS})
target_link_libraries(plasma_wallpaper_potd
KDE4::plasma
KDE4::kdeui
KDE4::kio
)
install(
TARGETS plasma_wallpaper_potd
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
)
install(
FILES
plasma-wallpaper-potd.desktop
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
)

View file

@ -0,0 +1,4 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/plasma_wallpaper_potd.pot
rm -f rc.cpp

View file

@ -0,0 +1,105 @@
[Desktop Entry]
Name=Picture of the Day
Name[ar]=صورة اليوم
Name[ast]=Imaxe del día
Name[bs]=Slika dana
Name[ca]=Imatge del dia
Name[ca@valencia]=Imatge del dia
Name[cs]=Obrázek dne
Name[da]=Dagens billede
Name[de]=Bild des Tages
Name[el]=Φωτογραφία της ημέρας
Name[en_GB]=Picture of the Day
Name[es]=Imagen del día
Name[et]=Päevapilt
Name[fi]=Päivän kuva
Name[fr]=Image du jour
Name[ga]=Grianghraf an Lae
Name[gl]=Imaxe do día
Name[hr]=Slika dana
Name[hu]=A nap képe
Name[is]=Myndir dagsins
Name[it]=Immagine del giorno
Name[ja]=
Name[kk]=Тәулік суреті
Name[km]=
Name[ko]=
Name[lt]=Dienos paveiksliukas
Name[lv]=Dienas attēls
Name[mr]=ि ि
Name[nb]=Dagens bilde
Name[nds]=Bild för Vundaag
Name[nl]=Afbeelding van de dag
Name[nn]=Dagens bilete
Name[pa]=
Name[pl]=Obraz dnia
Name[pt]=Imagem do Dia
Name[pt_BR]=Imagem do dia
Name[ro]=Imaginea zilei
Name[ru]=Изображение дня
Name[sk]=Obrázok dňa
Name[sl]=Slika dneva
Name[sr]=слика дана
Name[sr@ijekavian]=слика дана
Name[sr@ijekavianlatin]=slika dana
Name[sr@latin]=slika dana
Name[sv]=Dagens bild
Name[tr]=Günün Resmi
Name[uk]=Картинка дня
Name[wa]=Imådje do djoû
Name[x-test]=xxPicture of the Dayxx
Name[zh_CN]=
Name[zh_TW]=
Comment=A new picture from the Internet each day
Comment[ar]=صورة من السابكة كل يوم
Comment[bs]=Nova slika sa Interneta svaki dan
Comment[ca]=Una imatge nova des d'Internet cada dia
Comment[ca@valencia]=Una imatge nova des d'Internet cada dia
Comment[cs]=Nový obrázek z internetu denně
Comment[da]=Et nyt billede fra internettet hver dag
Comment[de]=Täglich ein Bild aus dem Internet anzeigen
Comment[el]=Κάθε μέρα μια νέα εικόνα από το διαδίκτυο
Comment[en_GB]=A new picture from the Internet each day
Comment[es]=Una nueva imagen desde Internet cada día
Comment[et]=Iga päev uus pilt internetist
Comment[fi]=Joka päivä uusi kuva Internetistä
Comment[fr]=Une nouvelle image chaque jour provenant d'Internet
Comment[gl]=Mostra imaxes de internet cada día.
Comment[hu]=Minden nap új kép az internetről
Comment[it]=Una nuova immagine ogni giorno da Internet.
Comment[kk]=Күнде Интернеттен жаңа сурет
Comment[ko]=
Comment[nb]=Et nytt bilde fra Internett hver dag
Comment[nds]=Elk Dag en nieg Bild ut dat Internet halen
Comment[nl]=Elke dag een nieuw plaatje uit het internet
Comment[pl]=Każdego dnia nowe zdjęcie z internetu
Comment[pt]=Uma nova imagem diária da Internet
Comment[pt_BR]=Uma nova imagem diária da Internet
Comment[ro]=O nouă imagine din Internet în fiecare zi
Comment[ru]=Новое изображение из Интернета каждый день
Comment[sk]=Nový obrázok z Internetu každý deň
Comment[sl]=Nova slika z interneta vsak dan
Comment[sr]=Сваки дан нова слика са Интернета
Comment[sr@ijekavian]=Сваки дан нова слика са Интернета
Comment[sr@ijekavianlatin]=Svaki dan nova slika sa Interneta
Comment[sr@latin]=Svaki dan nova slika sa Interneta
Comment[sv]=En ny bild från Internet varje dag
Comment[tr]=Her gün İnternet'ten yeni bir resim göster
Comment[uk]=Нове зображення з інтернету щодня
Comment[x-test]=xxA new picture from the Internet each dayxx
Comment[zh_CN]=
Comment[zh_TW]=
Type=Service
Icon=office-calendar
ServiceTypes=Plasma/Wallpaper
X-KDE-Library=plasma_wallpaper_potd
X-KDE-PluginInfo-Author=Ivailo Monev
X-KDE-PluginInfo-Email=xakepa10@gmail.com
X-KDE-PluginInfo-Name=potd
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true

View file

@ -0,0 +1,390 @@
/*
This file is part of the KDE project
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "potd.h"
#include <QImageWriter>
#include <QFileInfo>
#include <QJsonDocument>
#include <QHBoxLayout>
#include <KStandardDirs>
#include <KIO/Job>
#include <KIO/StoredTransferJob>
#include <KRandom>
#include <KDebug>
static const QString s_defaultprovider = QString::fromLatin1("pexels");
static const QByteArray s_podformat = QImageWriter::defaultImageFormat();
// same defaults as that of Plasma::Wallpaper
static const int s_defaultresizemethod = Plasma::Wallpaper::ScaledResize;
static const QColor s_defaultcolor = QColor(0, 0, 0);
// for reference:
// https://www.pexels.com/api/documentation/#photos-curated
static const QString s_pexelsapikey = QString::fromLatin1("WeDUAEYr4oV9211fAie7Jcwat6tNlkrW0BTx7kaoiyITGWp7WMcxpSoM");
static const QString s_pexelsapiurl = QString::fromLatin1("https://api.pexels.com/v1/curated");
// for reference:
// https://www.flickr.com/services/api/misc.urls.html
// https://www.flickr.com/services/api/explore/flickr.interestingness.getList
static const QString s_flickrapiurl = QString::fromLatin1(
"https://api.flickr.com/services/rest/?api_key=31f4917c363e2f76b9fc944790dcc338&format=json&method=flickr.interestingness.getList&date="
);
static const QString s_flickrimageurl = QString::fromLatin1(
"https://live.staticflickr.com/%1/%2_%3_b.jpg"
);
static QString kPoTDPath(const QString &provider)
{
return KGlobal::dirs()->locateLocal("cache", "plasma-potd/" + provider + "." + s_podformat);
}
PoTD::PoTD(QObject *parent, const QVariantList &args)
: Plasma::Wallpaper(parent, args),
m_provider(s_defaultprovider),
m_resizemethod(s_defaultresizemethod),
m_color(s_defaultcolor),
m_timer(nullptr),
m_downloading(false),
m_providerbox(nullptr),
m_resizemethodbox(nullptr),
m_colorbutton(nullptr)
{
m_timer = new QTimer(this);
m_timer->setInterval(10000);
connect(
m_timer, SIGNAL(timeout()),
this, SLOT(slotTimeout())
);
connect(
this, SIGNAL(renderCompleted(QImage)),
this, SLOT(slotRenderCompleted(QImage))
);
}
void PoTD::init(const KConfigGroup &config)
{
m_provider = config.readEntry("provider", s_defaultprovider);
if (m_provider != QLatin1String("pexels") && m_provider != QLatin1String("flickr")) {
kWarning() << "invalid provider" << m_provider;
m_provider = s_defaultprovider;
}
m_resizemethod = config.readEntry("resizemethod", s_defaultresizemethod);
m_color = config.readEntry("color", s_defaultcolor);
if (!m_color.isValid()) {
kWarning() << "invalid color" << m_color;
m_color = s_defaultcolor;
}
slotTimeout();
m_timer->start();
}
void PoTD::save(KConfigGroup &config)
{
config.writeEntry("provider", m_provider);
config.writeEntry("color", m_color);
config.writeEntry("resizemethod", m_resizemethod);
}
void PoTD::paint(QPainter *painter, const QRectF &exposedRect)
{
const QSize brectsize = boundingRect().size().toSize();
if (m_image.isNull() || m_size != brectsize) {
painter->fillRect(exposedRect, QBrush(Qt::black));
m_size = brectsize;
if (!m_imagepath.isEmpty()) {
render(m_imagepath, m_size, static_cast<Plasma::Wallpaper::ResizeMethod>(m_resizemethod), m_color);
}
return;
}
painter->drawImage(exposedRect, m_image, exposedRect);
}
QWidget* PoTD::createConfigurationInterface(QWidget *parent)
{
QWidget* potdconfigwidget = new QWidget(parent);
QHBoxLayout* potdconfiglayout = new QHBoxLayout(potdconfigwidget);
potdconfigwidget->setLayout(potdconfiglayout);
m_providerbox = new QComboBox(potdconfigwidget);
m_providerbox->addItem(i18n("Pexels"), "pexels");
m_providerbox->addItem(i18n("Flickr"), "flickr");
for (int i = 0; i < m_providerbox->count(); i++) {
if (m_providerbox->itemData(i).toString() == m_provider) {
m_providerbox->setCurrentIndex(i);
break;
}
}
connect(
m_providerbox, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotProviderChanged(int))
);
potdconfiglayout->addWidget(m_providerbox);
m_resizemethodbox = new QComboBox(potdconfigwidget);
m_resizemethodbox->addItem(i18n("Scaled & Cropped"), Plasma::Wallpaper::ResizeMethod::ScaledAndCroppedResize);
m_resizemethodbox->addItem(i18n("Scaled"), Plasma::Wallpaper::ResizeMethod::ScaledResize);
m_resizemethodbox->addItem(i18n("Scaled, keep proportions"), Plasma::Wallpaper::ResizeMethod::MaxpectResize);
m_resizemethodbox->addItem(i18n("Centered"), Plasma::Wallpaper::ResizeMethod::CenteredResize);
m_resizemethodbox->addItem(i18n("Tiled"), Plasma::Wallpaper::ResizeMethod::TiledResize);
m_resizemethodbox->addItem(i18n("Center Tiled"), Plasma::Wallpaper::ResizeMethod::CenterTiledResize);
for (int i = 0; i < m_resizemethodbox->count(); i++) {
if (m_resizemethodbox->itemData(i).toInt() == m_resizemethod) {
m_resizemethodbox->setCurrentIndex(i);
break;
}
}
connect(
m_resizemethodbox, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotResizeMethodChanged(int))
);
potdconfiglayout->addWidget(m_resizemethodbox);
m_colorbutton = new KColorButton(potdconfigwidget);
m_colorbutton->setColor(m_color);
connect(m_colorbutton, SIGNAL(changed(QColor)), this, SLOT(slotColorChanged(QColor)));
potdconfiglayout->addWidget(m_colorbutton);
connect(this, SIGNAL(settingsChanged(bool)), parent, SLOT(settingsChanged(bool)));
return potdconfigwidget;
}
void PoTD::slotRenderCompleted(const QImage &image)
{
m_image = image;
emit update(boundingRect());
}
void PoTD::slotSettingsChanged()
{
emit settingsChanged(true);
repaintWallpaper();
}
void PoTD::slotProviderChanged(int index)
{
m_provider = m_providerbox->itemData(index).toString();
slotSettingsChanged();
}
void PoTD::slotResizeMethodChanged(int index)
{
m_resizemethod = m_resizemethodbox->itemData(index).toInt();
slotSettingsChanged();
}
void PoTD::slotColorChanged(const QColor &color)
{
m_color = color;
slotSettingsChanged();
}
void PoTD::slotTimeout()
{
if (m_downloading) {
kDebug() << "download in progress";
return;
}
const QString potdimagepath = kPoTDPath(m_provider);
kDebug() << "checking potd" << potdimagepath;
QFileInfo potdimageinfo(potdimagepath);
if (!potdimageinfo.isFile()
|| potdimageinfo.lastModified().date().day() != QDate::currentDate().day()) {
m_imagepath.clear();
repaintWallpaper();
if (m_provider == "pexels") {
pexelsDownload();
} else if (m_provider == "flickr") {
flickrDownload();
}
} else {
m_imagepath = potdimagepath;
}
}
void PoTD::pexelsDownload()
{
m_downloading = true;
const KUrl potdurl(s_pexelsapiurl);
kDebug() << "starting job for" << potdurl.prettyUrl();
KIO::StoredTransferJob *kstoredjob = KIO::storedGet(potdurl, KIO::HideProgressInfo);
kstoredjob->setAutoDelete(false);
kstoredjob->addMetaData("Authorization", s_pexelsapikey);
connect(kstoredjob, SIGNAL(finished(KJob*)), SLOT(pexelsFinished(KJob*)));
}
void PoTD::pexelsFinished(KJob *kjob)
{
KIO::StoredTransferJob* kstoredjob = qobject_cast<KIO::StoredTransferJob*>(kjob);
if (kstoredjob->error()) {
kWarning() << "request error" << kstoredjob->url();
kstoredjob->deleteLater();
m_downloading = false;
return;
}
const QByteArray jsondata = kstoredjob->data();
kstoredjob->deleteLater();
const QJsonDocument jsondoc = QJsonDocument::fromJson(jsondata);
if (jsondoc.isNull()) {
kWarning() << "JSON error" << jsondoc.errorString();
m_downloading = false;
return;
}
QStringList pexelsphotolist;
const QVariantMap jsonmap = jsondoc.toVariant().toMap();
const QVariantList jsonphotoslist = jsonmap["photos"].toList();
foreach (const QVariant &photo, jsonphotoslist) {
const QVariantMap photomap = photo.toMap()["src"].toMap();
const QString photourl = photomap["landscape"].toString();
if (photourl.isEmpty()) {
kDebug() << "skipping photo without landscape url";
continue;
}
kDebug() << "photo" << photourl;
pexelsphotolist.append(photourl);
}
if (pexelsphotolist.isEmpty()) {
kWarning() << "empty photo list";
m_downloading = false;
return;
}
const KUrl photourl(pexelsphotolist.at(KRandom::randomMax(pexelsphotolist.size())));
kDebug() << "chosen photo" << photourl.prettyUrl();
kstoredjob = KIO::storedGet(photourl, KIO::HideProgressInfo);
kstoredjob->setAutoDelete(false);
kstoredjob->addMetaData("Authorization", s_pexelsapikey);
connect(kstoredjob, SIGNAL(finished(KJob*)), SLOT(imageFinished(KJob*)));
}
void PoTD::flickrDownload()
{
m_downloading = true;
m_flickrdate = QDate::currentDate();
const KUrl potdurl(s_flickrapiurl + m_flickrdate.toString(Qt::ISODate));
kDebug() << "starting job for" << potdurl.prettyUrl();
KIO::StoredTransferJob *kstoredjob = KIO::storedGet(potdurl, KIO::HideProgressInfo);
kstoredjob->setAutoDelete(false);
connect(kstoredjob, SIGNAL(finished(KJob*)), SLOT(flickrFinished(KJob*)));
}
void PoTD::flickrFinished(KJob *kjob)
{
KIO::StoredTransferJob* kstoredjob = qobject_cast<KIO::StoredTransferJob*>(kjob);
if (kstoredjob->error()) {
kWarning() << "request error" << kstoredjob->url();
kstoredjob->deleteLater();
m_downloading = false;
return;
}
// HACK: fix the data to be valid JSON
QByteArray jsondata = kstoredjob->data();
if (jsondata.startsWith("jsonFlickrApi(") && jsondata.endsWith(')')) {
jsondata = jsondata.mid(14, jsondata.size() - 15);
}
kstoredjob->deleteLater();
const QJsonDocument jsondoc = QJsonDocument::fromJson(jsondata);
if (jsondoc.isNull()) {
kWarning() << "JSON error" << jsondoc.errorString();
m_downloading = false;
return;
}
const QVariantMap jsonmap = jsondoc.toVariant().toMap();
const QString apistat = jsonmap["stat"].toString();
if (apistat == QLatin1String("fail")) {
// No pictures available for the specified parameters, decrement the date to two days earlier...
m_flickrdate = m_flickrdate.addDays(-2);
const KUrl queryurl(s_flickrapiurl + m_flickrdate.toString(Qt::ISODate));
kDebug() << "stat fail, retrying with" << queryurl.prettyUrl();
kstoredjob->deleteLater();
kstoredjob = KIO::storedGet(queryurl, KIO::HideProgressInfo);
kstoredjob->setAutoDelete(false);
connect(kstoredjob, SIGNAL(finished(KJob*)), SLOT(flickrFinished(KJob*)));
return;
}
QStringList flickrphotolist;
foreach (const QVariant &photo, jsonmap["photos"].toMap()["photo"].toList()) {
const QVariantMap photomap = photo.toMap();
const QString photoispublic = photomap["ispublic"].toString();
if (photoispublic != QLatin1String("1")) {
kDebug() << "skipping non-public photo";
continue;
}
const QString photoserver = photomap["server"].toString();
const QString photoid = photomap["id"].toString();
const QString photosecret = photomap["secret"].toString();
const QString photourl = s_flickrimageurl.arg(photoserver, photoid, photosecret);
kDebug() << "photo" << photourl;
flickrphotolist.append(photourl);
}
if (flickrphotolist.isEmpty()) {
kWarning() << "empty photo list";
m_downloading = false;
return;
}
const KUrl photourl(flickrphotolist.at(KRandom::randomMax(flickrphotolist.size())));
kDebug() << "chosen photo" << photourl.prettyUrl();
kstoredjob = KIO::storedGet(photourl, KIO::HideProgressInfo);
kstoredjob->setAutoDelete(false);
connect(kstoredjob, SIGNAL(finished(KJob*)), SLOT(imageFinished(KJob*)));
}
void PoTD::imageFinished(KJob *kjob)
{
KIO::StoredTransferJob* kstoredjob = qobject_cast<KIO::StoredTransferJob*>(kjob);
if (kstoredjob->error()) {
kWarning() << "image job error" << kstoredjob->url();
kstoredjob->deleteLater();
m_downloading = false;
return;
}
const QImage potdimage = QImage::fromData(kstoredjob->data());
if (potdimage.isNull()) {
kWarning() << "null image for" << kstoredjob->url();
} else {
const QString potdimagepath = kPoTDPath(m_provider);
if (!potdimage.save(potdimagepath, s_podformat)) {
kWarning() << "could not save image for" << kstoredjob->url();
} else {
m_imagepath = potdimagepath;
repaintWallpaper();
}
}
kstoredjob->deleteLater();
m_downloading = false;
}
void PoTD::repaintWallpaper()
{
m_image = QImage();
emit update(boundingRect());
}
#include "moc_potd.cpp"

View file

@ -0,0 +1,78 @@
/*
This file is part of the KDE project
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef POTD_H
#define POTD_H
#include <QImage>
#include <QColor>
#include <QTimer>
#include <QComboBox>
#include <Plasma/Wallpaper>
#include <KJob>
#include <KColorButton>
class PoTD : public Plasma::Wallpaper
{
Q_OBJECT
public:
PoTD(QObject *parent, const QVariantList &args);
void paint(QPainter* painter, const QRectF &exposedRect) final;
void init(const KConfigGroup &config) final;
void save(KConfigGroup &config) final;
QWidget* createConfigurationInterface(QWidget *parent) final;
Q_SIGNALS:
void settingsChanged(bool changed);
private Q_SLOTS:
void slotRenderCompleted(const QImage &image);
void slotSettingsChanged();
void slotProviderChanged(int index);
void slotResizeMethodChanged(int index);
void slotColorChanged(const QColor &color);
void slotTimeout();
void pexelsFinished(KJob *kjob);
void flickrFinished(KJob *kjob);
void imageFinished(KJob *kjob);
private:
void flickrDownload();
void pexelsDownload();
void repaintWallpaper();
QString m_provider;
QString m_imagepath;
QSize m_size;
QImage m_image;
int m_resizemethod;
QColor m_color;
QTimer* m_timer;
bool m_downloading;
QDate m_flickrdate;
QComboBox* m_providerbox;
QComboBox* m_resizemethodbox;
KColorButton* m_colorbutton;
};
K_EXPORT_PLASMA_WALLPAPER(potd, PoTD)
#endif // POTD_H