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 | |
19 | namespace KIO |
20 | { |
21 | |
22 | struct 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 | |
33 | class SHM |
34 | { |
35 | public: |
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 | |
46 | private: |
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. |
55 | static 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 | */ |
78 | class FilePreviewJob : public KIO::Job |
79 | { |
80 | Q_OBJECT |
81 | public: |
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 | |
94 | private Q_SLOTS: |
95 | void slotStatFile(KJob *job); |
96 | void slotGetOrCreateThumbnail(KJob *job); |
97 | |
98 | private: |
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 | |
171 | inline 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 | |