source: appstream-generator/src/asgen/handlers/screenshothandler.d @ 4841

Last change on this file since 4841 was 4841, checked in by Juanma, 2 years ago

Initial release

File size: 6.8 KB
Line 
1/*
2 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
3 *
4 * Licensed under the GNU Lesser General Public License Version 3
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the license, or
9 * (at your option) any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20module asgen.handlers.screenshothandler;
21
22import std.path : baseName, buildPath;
23import std.uni : toLower;
24import std.string : format;
25import std.array : empty;
26import std.algorithm : startsWith;
27import std.stdio;
28import gobject.ObjectG;
29import gi.appstream;
30import appstream.Component;
31import appstream.Screenshot;
32import appstream.Image;
33static import std.file;
34
35import asgen.config;
36import asgen.result;
37import asgen.utils;
38static import asgen.image;
39
40
41private immutable screenshotSizes = [ImageSize (1248, 702), ImageSize (752, 423), ImageSize (624, 351), ImageSize (224, 126)];
42
43void processScreenshots (GeneratorResult gres, Component cpt, string mediaExportDir)
44{
45    auto scrArr = cpt.getScreenshots ();
46    if (scrArr.len == 0)
47        return;
48
49    Screenshot[] validScrs;
50    for (uint i = 0; i < scrArr.len; i++) {
51        // cast array data to D Screenshot and keep a reference to the C struct
52        auto scr = new Screenshot (cast (AsScreenshot*) scrArr.index (i));
53        auto resScr = processScreenshot (gres, cpt, scr, mediaExportDir, i+1);
54        if (resScr !is null) {
55            validScrs ~= resScr;
56            resScr.doref ();
57        }
58    }
59
60    // drop all screenshots from the component
61    scrArr.removeRange (0, scrArr.len);
62
63    // add valid screenshots back
64    foreach (ref scr; validScrs) {
65        cpt.addScreenshot (scr);
66    }
67}
68
69private Screenshot processScreenshot (GeneratorResult gres, Component cpt, Screenshot scr, string mediaExportDir, uint scrNo)
70{
71    import std.stdio;
72
73    auto imgArr = scr.getImages ();
74    if (imgArr.len == 0) {
75        gres.addHint (cpt.getId (), "metainfo-screenshot-but-no-image");
76        return null;
77    }
78
79    auto initImg = new Image (cast(AsImage*) imgArr.index (0));
80    initImg.doref ();
81    // drop all images
82    imgArr.removeRange (0, imgArr.len);
83
84    auto conf = asgen.config.Config.get ();
85    auto origImgUrl = initImg.getUrl ();
86
87    ubyte[] imgData;
88    try {
89        import std.net.curl;
90        if (origImgUrl.startsWith ("ftp:")) {
91            // we have an FTP url
92            imgData = get!(AutoProtocol, ubyte) (origImgUrl);
93        } else {
94            // assume HTTP(S)
95            auto http = HTTP ();
96            if (!conf.caInfo.empty ())
97                http.caInfo = conf.caInfo;
98            imgData = get!(HTTP, ubyte) (origImgUrl, http);
99        }
100    } catch (Exception e) {
101        gres.addHint (cpt.getId (), "screenshot-download-error", ["url": origImgUrl, "error": e.msg]);
102        return null;
103    }
104
105    auto gcid = gres.gcidForComponent (cpt);
106    if (gcid is null) {
107        gres.addHint (cpt, "internal-error", "No global ID could be found for the component.");
108        return null;
109    }
110
111    immutable cptScreenshotsPath = buildPath (mediaExportDir, gcid, "screenshots");
112    immutable cptScreenshotsUrl = buildPath (gcid, "screenshots");
113    std.file.mkdirRecurse (cptScreenshotsPath);
114
115    uint sourceScrWidth;
116    uint sourceScrHeight;
117    try {
118        auto srcImgName = format ("image-%s_orig.png", scrNo);
119        auto srcImgPath = buildPath (cptScreenshotsPath, srcImgName);
120        auto srcImgUrl =  buildPath (cptScreenshotsUrl, srcImgName);
121
122        // save the source screenshot as PNG image
123        auto srcImg = new asgen.image.Image (imgData, asgen.image.ImageFormat.PNG);
124        srcImg.savePng (srcImgPath);
125
126        auto img = new Image ();
127        img.setKind (ImageKind.SOURCE);
128
129        sourceScrWidth = srcImg.width;
130        sourceScrHeight = srcImg.height;
131        img.setWidth (sourceScrWidth);
132        img.setHeight (sourceScrHeight);
133
134        // if we should not create a screenshots store, delete the just-downloaded file and set
135        // the original upstream URL as source.
136        // we still needed to download the screenshot to get information about its size.
137        if (!conf.featureEnabled (GeneratorFeature.STORE_SCREENSHOTS)) {
138            img.setUrl (origImgUrl);
139            scr.addImage (img);
140
141            // drop screenshot storage directory, in this mode it was only for temporary use
142            std.file.rmdirRecurse (cptScreenshotsPath);
143            return scr;
144        }
145
146        img.setUrl (srcImgUrl);
147        scr.addImage (img);
148    } catch (Exception e) {
149        gres.addHint (cpt.getId (), "screenshot-save-error", ["url": origImgUrl, "error": format ("Can not store source screenshot: %s", e.msg)]);
150        return null;
151    }
152
153    // generate & save thumbnails for the screenshot image
154    bool thumbnailsGenerated = false;
155    foreach (size; screenshotSizes) {
156        // ensure we will only downscale the screenshot for thumbnailing
157        if (size.width > sourceScrWidth)
158            continue;
159        if (size.height > sourceScrHeight)
160            continue;
161
162        try {
163            auto thumb = new asgen.image.Image (imgData, asgen.image.ImageFormat.PNG);
164            if (size.width > size.height)
165                thumb.scaleToWidth (size.width);
166            else
167                thumb.scaleToHeight (size.height);
168
169            // create thumbnail storage path and URL component
170            auto thumbImgName = "image-%s_%sx%s.png".format (scrNo, thumb.width, thumb.height);
171            auto thumbImgPath = buildPath (cptScreenshotsPath, thumbImgName);
172            auto thumbImgUrl =  buildPath (cptScreenshotsUrl, thumbImgName);
173
174            // store the thumbnail image on disk
175            thumb.savePng (thumbImgPath);
176
177            // finally prepare the thumbnail definition and add it to the metadata
178            auto img = new Image ();
179            img.setKind (ImageKind.THUMBNAIL);
180            img.setWidth (thumb.width);
181            img.setHeight (thumb.height);
182            img.setUrl (thumbImgUrl);
183            scr.addImage (img);
184        } catch (Exception e) {
185            gres.addHint (cpt.getId (), "screenshot-save-error", ["url": origImgUrl, "error": format ("Failure while preparing thumbnail: %s", e.msg)]);
186            return null;
187        }
188
189        thumbnailsGenerated = true;
190    }
191
192    if (!thumbnailsGenerated)
193        gres.addHint (cpt.getId (), "screenshot-no-thumbnails", ["url": origImgUrl]);
194
195    return scr;
196}
Note: See TracBrowser for help on using the repository browser.