summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2015-04-02 13:24:21 +0100
committerRichard Hughes <richard@hughsie.com>2015-04-07 08:46:52 +0100
commitff7df39b02f64688a653c536949eb7d5d51fcce0 (patch)
tree7f3de969ef35745a02c2184be8abe787c4e13d74
parent367a1e838531da007967f64b45e98e04f17e4588 (diff)
downloadappstream-glib-ff7df39b02f64688a653c536949eb7d5d51fcce0.tar.gz
Add a mirror-screenshots command to appstream-util
-rw-r--r--client/as-util.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/client/as-util.c b/client/as-util.c
index ec54f1e..6ea6aef 100644
--- a/client/as-util.c
+++ b/client/as-util.c
@@ -2874,6 +2874,455 @@ as_util_check_root_app (AsApp *app, GPtrArray *problems)
}
/**
+ * as_util_app_log:
+ **/
+G_GNUC_PRINTF (2, 3)
+static void
+as_util_app_log (AsApp *app, const gchar *fmt, ...)
+{
+ const gchar *id;
+ guint i;
+ va_list args;
+ _cleanup_free_ gchar *tmp = NULL;
+
+ va_start (args, fmt);
+ tmp = g_strdup_vprintf (fmt, args);
+ va_end (args);
+
+ /* print status */
+ id = as_app_get_id (app);
+ g_print ("%s: ", id);
+ for (i = strlen (id) + 2; i < 35; i++)
+ g_print (" ");
+ g_print ("%s\n", tmp);
+}
+
+/**
+ * as_util_mirror_screenshots_thumb:
+ **/
+static gboolean
+as_util_mirror_screenshots_thumb (AsScreenshot *ss, AsImage *im_src,
+ guint width, guint height, guint scale,
+ const gchar *mirror_uri,
+ const gchar *output_dir,
+ GError **error)
+{
+ _cleanup_free_ gchar *fn = NULL;
+ _cleanup_free_ gchar *size_str = NULL;
+ _cleanup_free_ gchar *url_tmp = NULL;
+ _cleanup_object_unref_ AsImage *im_tmp = NULL;
+
+ /* only save the HiDPI screenshot if it's not padded */
+ if (scale > 1) {
+ if (width * scale > as_image_get_width (im_src) ||
+ height * scale > as_image_get_height (im_src))
+ return TRUE;
+ }
+
+ /* save to disk */
+ size_str = g_strdup_printf ("%ix%i", width * scale, height * scale);
+ fn = g_build_filename (output_dir, size_str,
+ as_image_get_basename (im_src),
+ NULL);
+ if (!g_file_test (fn, G_FILE_TEST_EXISTS)) {
+ if (!as_image_save_filename (im_src, fn,
+ width * scale,
+ height * scale,
+ AS_IMAGE_SAVE_FLAG_PAD_16_9 |
+ AS_IMAGE_SAVE_FLAG_SHARPEN,
+ error))
+ return FALSE;
+ }
+
+ /* add resized image to the screenshot */
+ im_tmp = as_image_new ();
+ as_image_set_width (im_tmp, width * scale);
+ as_image_set_height (im_tmp, height * scale);
+ url_tmp = g_build_filename (mirror_uri,
+ size_str,
+ as_image_get_basename (im_src),
+ NULL);
+ as_image_set_url (im_tmp, url_tmp, -1);
+ as_image_set_kind (im_tmp, AS_IMAGE_KIND_THUMBNAIL);
+ as_image_set_basename (im_tmp, as_image_get_basename (im_src));
+ as_screenshot_add_image (ss, im_tmp);
+ return TRUE;
+}
+
+/**
+ * as_util_mirror_screenshots_app_file:
+ **/
+static gboolean
+as_util_mirror_screenshots_app_file (AsApp *app,
+ const gchar *source_url,
+ const gchar *filename,
+ const gchar *mirror_uri,
+ const gchar *output_dir,
+ GError **error)
+{
+ AsImageAlphaFlags alpha_flags;
+ gboolean is_default;
+ guint i;
+ _cleanup_free_ gchar *basename = NULL;
+ _cleanup_free_ gchar *filename_no_path = NULL;
+ _cleanup_free_ gchar *url_src = NULL;
+ _cleanup_object_unref_ AsImage *im = NULL;
+ _cleanup_object_unref_ AsImage *im_src = NULL;
+ _cleanup_object_unref_ AsScreenshot *ss = NULL;
+ guint sizes[] = { AS_IMAGE_NORMAL_WIDTH, AS_IMAGE_NORMAL_HEIGHT,
+ AS_IMAGE_THUMBNAIL_WIDTH, AS_IMAGE_THUMBNAIL_HEIGHT,
+ AS_IMAGE_LARGE_WIDTH, AS_IMAGE_LARGE_HEIGHT,
+ 0 };
+
+ im_src = as_image_new ();
+ if (!as_image_load_filename (im_src, filename, error))
+ return FALSE;
+
+ /* is the aspect ratio of the source perfectly 16:9 */
+ if ((as_image_get_width (im_src) / 16) * 9 !=
+ as_image_get_height (im_src)) {
+ filename_no_path = g_path_get_basename (filename);
+ g_debug ("%s is not in 16:9 aspect ratio",
+ filename_no_path);
+ }
+
+ /* check screenshot is reasonable in size */
+ if (as_image_get_width (im_src) * 2 < AS_IMAGE_NORMAL_WIDTH ||
+ as_image_get_height (im_src) * 2 < AS_IMAGE_NORMAL_HEIGHT) {
+ filename_no_path = g_path_get_basename (filename);
+ g_set_error (error,
+ AS_APP_ERROR,
+ AS_APP_ERROR_FAILED,
+ "%s is too small to be used: %ix%i",
+ filename_no_path,
+ as_image_get_width (im_src),
+ as_image_get_height (im_src));
+ return FALSE;
+ }
+
+ /* check the image is not padded */
+ alpha_flags = as_image_get_alpha_flags (im_src);
+ if ((alpha_flags & AS_IMAGE_ALPHA_FLAG_TOP) > 0||
+ (alpha_flags & AS_IMAGE_ALPHA_FLAG_BOTTOM) > 0) {
+ filename_no_path = g_path_get_basename (filename);
+ g_debug ("%s has vertical alpha padding",
+ filename_no_path);
+ }
+ if ((alpha_flags & AS_IMAGE_ALPHA_FLAG_LEFT) > 0||
+ (alpha_flags & AS_IMAGE_ALPHA_FLAG_RIGHT) > 0) {
+ filename_no_path = g_path_get_basename (filename);
+ g_debug ("%s has horizontal alpha padding",
+ filename_no_path);
+ }
+
+ ss = as_screenshot_new ();
+ is_default = as_app_get_screenshots(AS_APP(app))->len == 0;
+ as_screenshot_set_kind (ss, is_default ? AS_SCREENSHOT_KIND_DEFAULT :
+ AS_SCREENSHOT_KIND_NORMAL);
+
+ /* add back the source image */
+ im = as_image_new ();
+ as_image_set_url (im, source_url, -1);
+ as_image_set_kind (im, AS_IMAGE_KIND_SOURCE);
+ as_screenshot_add_image (ss, im);
+
+ /* include the app-id in the basename */
+ basename = g_strdup_printf ("%s-%s.png",
+ as_app_get_id_filename (AS_APP (app)),
+ as_image_get_md5 (im_src));
+ as_image_set_basename (im_src, basename);
+
+ /* fonts only have full sized screenshots */
+ if (as_app_get_id_kind (AS_APP (app)) != AS_ID_KIND_FONT) {
+ for (i = 0; sizes[i] != 0; i += 2) {
+
+ /* save LoDPI */
+ if (!as_util_mirror_screenshots_thumb (ss,
+ im_src,
+ sizes[i],
+ sizes[i+1],
+ 1, /* scale */
+ mirror_uri,
+ output_dir,
+ error))
+ return FALSE;
+
+ /* save HiDPI version */
+ if (!as_util_mirror_screenshots_thumb (ss, im_src,
+ sizes[i],
+ sizes[i+1],
+ 2, /* scale */
+ mirror_uri,
+ output_dir,
+ error))
+ return FALSE;
+ }
+ }
+
+ as_app_add_screenshot (app, ss);
+ return TRUE;
+}
+
+/**
+ * as_util_mirror_screenshots_app_url:
+ **/
+static gboolean
+as_util_mirror_screenshots_app_url (AsUtilPrivate *priv,
+ AsApp *app,
+ const gchar *url,
+ const gchar *mirror_uri,
+ const gchar *cache_dir,
+ const gchar *output_dir,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ SoupStatus status;
+ SoupURI *uri = NULL;
+ _cleanup_free_ gchar *basename = NULL;
+ _cleanup_free_ gchar *cache_filename = NULL;
+ _cleanup_object_unref_ SoupMessage *msg = NULL;
+ _cleanup_object_unref_ SoupSession *session = NULL;
+
+ /* local files, typically fonts */
+ if (g_str_has_prefix (url, "file:/")) {
+ _cleanup_free_ gchar *url_new = NULL;
+ _cleanup_object_unref_ AsImage *im = NULL;
+ _cleanup_object_unref_ AsScreenshot *ss = NULL;
+ url_new = g_build_filename (mirror_uri, "source", url + 6, NULL);
+ im = as_image_new ();
+ as_image_set_url (im, url_new, -1);
+ as_image_set_kind (im, AS_IMAGE_KIND_SOURCE);
+ ss = as_screenshot_new ();
+ as_screenshot_set_kind (ss, AS_SCREENSHOT_KIND_DEFAULT);
+ as_screenshot_add_image (ss, im);
+ as_app_add_screenshot (app, ss);
+ return TRUE;
+ }
+
+ /* set up networking */
+ session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "appstream-util",
+ SOUP_SESSION_TIMEOUT, 10,
+ NULL);
+ soup_session_add_feature_by_type (session,
+ SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
+
+ /* download to cache if not already added */
+ basename = g_path_get_basename (url);
+ cache_filename = g_strdup_printf ("%s/%s-%s",
+ cache_dir,
+ as_app_get_id_filename (AS_APP (app)),
+ basename);
+ if (g_file_test (cache_filename, G_FILE_TEST_EXISTS)) {
+ as_util_app_log (app, "In cache %s", cache_filename);
+ } else if (priv->nonet) {
+ as_util_app_log (app, "Missing %s:%s", url, cache_filename);
+ } else {
+ uri = soup_uri_new (url);
+ if (uri == NULL) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_ERROR,
+ AS_ERROR_FAILED,
+ "Could not parse '%s' as a URL", url);
+ goto out;
+ }
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ as_util_app_log (app, "Downloading %s", url);
+ status = soup_session_send_message (session, msg);
+ if (status != SOUP_STATUS_OK) {
+ ret = FALSE;
+ g_set_error (error,
+ AS_ERROR,
+ AS_ERROR_FAILED,
+ "Downloading failed: %s",
+ soup_status_get_phrase (status));
+ goto out;
+ }
+
+ /* save new file */
+ ret = g_file_set_contents (cache_filename,
+ msg->response_body->data,
+ msg->response_body->length,
+ error);
+ if (!ret)
+ goto out;
+ as_util_app_log (app, "Saved to cache %s", cache_filename);
+ }
+
+ /* mirror the filename */
+ ret = as_util_mirror_screenshots_app_file (app,
+ url,
+ cache_filename,
+ mirror_uri,
+ output_dir,
+ error);
+ if (!ret)
+ goto out;
+out:
+ if (uri != NULL)
+ soup_uri_free (uri);
+ return ret;
+}
+
+/**
+ * as_util_mirror_screenshots_app:
+ **/
+static gboolean
+as_util_mirror_screenshots_app (AsUtilPrivate *priv,
+ AsApp *app,
+ GPtrArray *urls,
+ const gchar *mirror_uri,
+ const gchar *cache_dir,
+ const gchar *output_dir,
+ GError **error)
+{
+ guint i;
+ const gchar *url;
+
+ for (i = 0; i < urls->len; i++) {
+ _cleanup_error_free_ GError *error_local = NULL;
+
+ /* download URL or get from cache */
+ url = g_ptr_array_index (urls, i);
+ if (!as_util_mirror_screenshots_app_url (priv,
+ app,
+ url,
+ mirror_uri,
+ cache_dir,
+ output_dir,
+ &error_local)) {
+ as_util_app_log (app, "Failed to download %s: %s",
+ url, error_local->message);
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * as_util_mirror_screenshots:
+ **/
+static gboolean
+as_util_mirror_screenshots (AsUtilPrivate *priv, gchar **values, GError **error)
+{
+ AsApp *app;
+ AsImage *im;
+ AsScreenshot *ss;
+ GPtrArray *apps;
+ GPtrArray *images;
+ GPtrArray *screenshots;
+ guint i;
+ guint j;
+ guint k;
+ const gchar *cache_dir = "./cache/";
+ const gchar *output_dir = "./screenshots/";
+ _cleanup_object_unref_ AsStore *store = NULL;
+ _cleanup_object_unref_ GFile *file = NULL;
+ guint sizes[] = { AS_IMAGE_NORMAL_WIDTH, AS_IMAGE_NORMAL_HEIGHT,
+ AS_IMAGE_THUMBNAIL_WIDTH, AS_IMAGE_THUMBNAIL_HEIGHT,
+ AS_IMAGE_LARGE_WIDTH, AS_IMAGE_LARGE_HEIGHT,
+ 0 };
+
+ /* check args */
+ if (g_strv_length (values) < 2) {
+ g_set_error_literal (error,
+ AS_ERROR,
+ AS_ERROR_INVALID_ARGUMENTS,
+ "Not enough arguments, expected: "
+ "file url [cachedir] [outputdir]");
+ return FALSE;
+ }
+
+ /* overrides */
+ if (g_strv_length (values) >= 3)
+ cache_dir = values[2];
+ if (g_strv_length (values) >= 4)
+ output_dir = values[3];
+
+ /* create dirs */
+ if (g_mkdir_with_parents (cache_dir, 0700) != 0) {
+ g_set_error (error,
+ AS_ERROR,
+ AS_ERROR_FAILED,
+ "Failed to create: %s", cache_dir);
+ return FALSE;
+ }
+
+ /* create the tree of screenshot directories */
+ for (j = 1; j <= 2; j++) {
+ for (i = 0; sizes[i] != 0; i += 2) {
+ _cleanup_free_ gchar *size_str = NULL;
+ _cleanup_free_ gchar *fn = NULL;
+ size_str = g_strdup_printf ("%ix%i",
+ sizes[i+0] * j,
+ sizes[i+1] * j);
+ fn = g_build_filename (output_dir, size_str, NULL);
+ if (g_mkdir_with_parents (fn, 0700) != 0) {
+ g_set_error (error,
+ AS_ERROR,
+ AS_ERROR_FAILED,
+ "Failed to create: %s", fn);
+ return FALSE;
+ }
+ }
+ }
+
+ /* open file */
+ store = as_store_new ();
+ file = g_file_new_for_path (values[0]);
+ if (!as_store_from_file (store, file, NULL, NULL, error))
+ return FALSE;
+
+ /* convert all the screenshots */
+ apps = as_store_get_apps (store);
+ for (i = 0; i < apps->len; i++) {
+ _cleanup_ptrarray_unref_ GPtrArray *urls = NULL;
+
+ /* get app */
+ app = g_ptr_array_index (apps, i);
+ screenshots = as_app_get_screenshots (app);
+ if (screenshots->len == 0)
+ continue;
+
+ /* get source screenshots */
+ urls = g_ptr_array_new_with_free_func (g_free);
+ for (j = 0; j < screenshots->len; j++) {
+ ss = g_ptr_array_index (screenshots, j);
+ images = as_screenshot_get_images (ss);
+ for (k = 0; k < images->len; k++) {
+ im = g_ptr_array_index (images, k);
+ if (as_image_get_kind (im) != AS_IMAGE_KIND_SOURCE)
+ continue;
+ g_ptr_array_add (urls, g_strdup (as_image_get_url (im)));
+ }
+ }
+
+ /* invalidate */
+ g_ptr_array_set_size (screenshots, 0);
+ if (urls->len == 0)
+ continue;
+
+ /* download and save new versions */
+ if (!as_util_mirror_screenshots_app (priv, app, urls,
+ values[1],
+ cache_dir,
+ output_dir,
+ error))
+ return FALSE;
+ }
+
+ /* save file */
+ if (!as_store_to_file (store, file,
+ AS_NODE_TO_XML_FLAG_ADD_HEADER |
+ AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
+ AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
+ NULL, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
* as_util_replace_screenshots:
*
**/
@@ -3166,6 +3615,12 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Replace screenshots in source file"),
as_util_replace_screenshots);
+ as_util_add (priv->cmd_array,
+ "mirror-screenshots",
+ NULL,
+ /* TRANSLATORS: command description */
+ _("Mirror upstream screenshots"),
+ as_util_mirror_screenshots);
/* sort by command name */
g_ptr_array_sort (priv->cmd_array,