1/*
2 * This file is part of the KDE libraries
3 * SPDX-FileCopyrightText: 2025 Akseli Lahtinen <akselmo@akselmo.dev>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#ifndef KIO_FILEPREVIEWJOB_H
9#define KIO_FILEPREVIEWJOB_H
10
11#include "previewjob.h"
12#include <KPluginMetaData>
13#include <QDir>
14#include <QImage>
15#include <QSize>
16#include <kfileitem.h>
17#include <kio/job.h>
18
19namespace KIO
20{
21
22struct PreviewItem {
23 KFileItem item;
24 QSize size = QSize();
25 qreal devicePixelRatio = 1.0;
26 bool ignoreMaximumSize = false;
27 int sequenceIndex = 0;
28 PreviewJob::ScaleType scaleType = PreviewJob::ScaleType::ScaledAndCached;
29 int cacheSize = 0;
30 QMap<QString, int> deviceIdMap;
31};
32
33class SHM
34{
35public:
36 SHM(int id, uchar *address);
37 ~SHM();
38
39 static std::unique_ptr<SHM> create(int size);
40
41 Q_DISABLE_COPY(SHM);
42
43 int id() const;
44 uchar *address() const;
45
46private:
47 // Shared memory segment Id. The segment is allocated to a size
48 // of extent x extent x 4 (32 bit image) on first need.
49 int m_id;
50 // And the data area
51 uchar *m_address;
52};
53
54// Time (in milliseconds) to wait for kio-fuse in a PreviewJob before giving up.
55static constexpr int s_kioFuseMountTimeout = 10000;
56
57/*
58 * This job does multiple small chained jobs to get the thumbnail for an item,
59 * and returns the result.
60 *
61 * First, it attempts to get the device id's for the given paths: thumbRoot and item.localUrl
62 * However if past jobs have already done this, it can reuse the old deviceId table and skip
63 * getting the device id's again.
64 *
65 * Device id's are required for checking if the thumbnail can be cached.
66 *
67 * After getting all this information, if the item has sequenceId higher than 0,
68 * we just get the next item in the sequence and return that result.
69 *
70 * If we're not sequencing, first we try to pull the thumbnail from the cache.
71 * If that is succesful, we just return the file and end the job.
72 *
73 * If not succesful, it's likely we do not have thumbnail for this item, so
74 * we generate one, either by using thumbnailerPlugin or standardThumbnailer.
75 *
76 * We then return the result, whatever it may be.
77 */
78class FilePreviewJob : public KIO::Job
79{
80 Q_OBJECT
81public:
82 FilePreviewJob(const PreviewItem &item, const QString &thumbRoot, const QMap<QString, KPluginMetaData> &mimeMap);
83 ~FilePreviewJob();
84
85 void start() override;
86
87 QMap<QString, QString> thumbnailWorkerMetaData() const;
88 QMap<QString, int> deviceIdMap() const;
89 QImage previewImage() const;
90 PreviewItem item() const;
91 static QList<KPluginMetaData> loadAvailablePlugins();
92 static QList<KPluginMetaData> standardThumbnailers();
93
94private Q_SLOTS:
95 void slotStatFile(KJob *job);
96 void slotGetOrCreateThumbnail(KJob *job);
97
98private:
99 enum CachePolicy {
100 Prevent,
101 Allow,
102 Unknown
103 } m_currentDeviceCachePolicy = Unknown;
104
105 QStringList m_enabledPlugins;
106 // The current item
107 KIO::PreviewItem m_item;
108 // The modification time of that URL
109 QDateTime m_tOrig;
110 // Path to thumbnail cache for the current size
111 QString m_thumbPath;
112 // Original URL of current item in RFC2396 format
113 // (file:///path/to/a%20file instead of file:/path/to/a file)
114 QByteArray m_origName;
115 // Thumbnail file name for current item
116 QString m_thumbName;
117 // Size of thumbnail
118 QSize m_size;
119 // Unscaled size of thumbnail (128, 256 or 512 if cache is enabled)
120 short m_cacheSize;
121 // Whether the thumbnail should be scaled and/or saved
122 PreviewJob::ScaleType m_scaleType;
123 bool m_ignoreMaximumSize;
124 int m_sequenceIndex;
125 // If the file to create a thumb for was a temp file, this is its name
126 QString m_tempName;
127 // The shared memory
128 std::unique_ptr<SHM> m_shm;
129 // Root of thumbnail cache
130 QString m_thumbRoot;
131 // Metadata returned from the KIO thumbnail worker
132 QMap<QString, QString> m_thumbnailWorkerMetaData;
133 qreal m_devicePixelRatio;
134 static const int m_idUnknown = -1;
135 // Id of a device storing currently processed file
136 int m_currentDeviceId = 0;
137 // Device ID for each file. Stored while in STATE_DEVICE_INFO state, used later on.
138 QMap<QString, int> m_deviceIdMap;
139 // the path of a unique temporary directory
140 QString m_tempDirPath;
141 // Whether to try using KIOFuse to resolve files. Set to false if KIOFuse is not available.
142 bool m_tryKioFuse = true;
143 // The preview image. If when emitting return this is empty, job can be considered as failed.
144 QImage m_preview;
145
146 bool m_standardThumbnailer = false;
147 KPluginMetaData m_plugin;
148
149 const QMap<QString, KPluginMetaData> m_mimeMap;
150
151 void statFile();
152 void getOrCreateThumbnail();
153 static QImage loadThumbnailFromCache(const QString &url, qreal dpr);
154 bool isCacheValid(const QImage &thumb);
155 void createThumbnail(const QString &);
156 void createThumbnailViaFuse(const QUrl &, const QUrl &);
157 void createThumbnailViaLocalCopy(const QUrl &);
158 QString parentDirPath(const QString &path) const;
159
160 void emitPreview(const QImage &thumb);
161 void slotThumbData(KIO::Job *, const QByteArray &);
162 void slotStandardThumbData(KIO::Job *, const QImage &);
163 // Checks if thumbnail is on encrypted partition different than thumbRoot
164 CachePolicy canBeCached(const QString &path);
165 int getDeviceId(const QString &path);
166 void saveThumbnailData(QImage &thumb);
167 void preparePluginForMimetype(const QString &mimeType);
168 static void saveThumbnailToCache(const QImage &thumb, const QString &path);
169};
170
171inline FilePreviewJob *filePreviewJob(const PreviewItem &item, const QString &thumbRoot, const QMap<QString, KPluginMetaData> &mimeMap)
172{
173 auto job = new FilePreviewJob(item, thumbRoot, mimeMap);
174 return job;
175}
176}
177
178#endif
179

source code of kio/src/gui/filepreviewjob.h