diff --git a/lib/gs-appstream.c b/lib/gs-appstream.c index 13eb352a83d99a624d64ba120f5b8358a90568ca..193a3dbfb3581b82a7c17c572202b0310b3d18eb 100644 --- a/lib/gs-appstream.c +++ b/lib/gs-appstream.c @@ -391,7 +391,11 @@ gs_appstream_refine_add_addons (GsPlugin *plugin, } static gboolean -gs_appstream_refine_add_images (GsApp *app, AsScreenshot *ss, XbNode *screenshot, GError **error) +gs_appstream_refine_add_images (GsApp *app, + AsScreenshot *ss, + XbNode *screenshot, + gboolean *out_any_added, + GError **error) { g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) images = NULL; @@ -414,10 +418,45 @@ gs_appstream_refine_add_images (GsApp *app, AsScreenshot *ss, XbNode *screenshot as_screenshot_add_image (ss, im); } + *out_any_added = *out_any_added || images->len > 0; + /* success */ return TRUE; } +static gboolean +gs_appstream_refine_add_videos (GsApp *app, + AsScreenshot *ss, + XbNode *screenshot, + gboolean *out_any_added, + GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) videos = NULL; + + videos = xb_node_query (screenshot, "video", 0, &error_local); + if (videos == NULL) { + if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + return TRUE; + g_propagate_error (error, g_steal_pointer (&error_local)); + return FALSE; + } + for (guint i = 0; i < videos->len; i++) { + XbNode *video = g_ptr_array_index (videos, i); + g_autoptr(AsVideo) vid = as_video_new (); + as_video_set_height (vid, xb_node_get_attr_as_uint (video, "height")); + as_video_set_width (vid, xb_node_get_attr_as_uint (video, "width")); + as_video_set_codec_kind (vid, as_video_codec_kind_from_string (xb_node_get_attr (video, "codec"))); + as_video_set_container_kind (vid, as_video_container_kind_from_string (xb_node_get_attr (video, "container"))); + as_video_set_url (vid, xb_node_get_text (video)); + as_screenshot_add_video (ss, vid); + } + + *out_any_added = *out_any_added || videos->len > 0; + + return TRUE; +} + static gboolean gs_appstream_refine_add_screenshots (GsApp *app, XbNode *component, GError **error) { @@ -435,9 +474,12 @@ gs_appstream_refine_add_screenshots (GsApp *app, XbNode *component, GError **err for (guint i = 0; i < screenshots->len; i++) { XbNode *screenshot = g_ptr_array_index (screenshots, i); g_autoptr(AsScreenshot) ss = as_screenshot_new (); - if (!gs_appstream_refine_add_images (app, ss, screenshot, error)) + gboolean any_added = FALSE; + if (!gs_appstream_refine_add_images (app, ss, screenshot, &any_added, error) || + !gs_appstream_refine_add_videos (app, ss, screenshot, &any_added, error)) return FALSE; - gs_app_add_screenshot (app, ss); + if (any_added) + gs_app_add_screenshot (app, ss); } /* FIXME: move into no refine flags section? */ diff --git a/src/gs-screenshot-image.c b/src/gs-screenshot-image.c index 946ab0984a88adc80fd222658ab422488355e8fc..b74c1584fbd9af377cbaad0c3e2a68a26e2f591e 100644 --- a/src/gs-screenshot-image.c +++ b/src/gs-screenshot-image.c @@ -27,13 +27,12 @@ struct _GsScreenshotImage GtkWidget *box_error; GtkWidget *image1; GtkWidget *image2; + GtkWidget *video; GtkWidget *label_error; GSettings *settings; SoupSession *session; SoupMessage *message; -#if SOUP_CHECK_VERSION(3, 0, 0) GCancellable *cancellable; -#endif gchar *filename; const gchar *current_image; guint width; @@ -104,34 +103,37 @@ gs_screenshot_image_set_error (GsScreenshotImage *ssimg, const gchar *message) static void as_screenshot_show_image (GsScreenshotImage *ssimg) { - g_autoptr(GdkPixbuf) pixbuf = NULL; - - /* no need to composite */ - if (ssimg->width == G_MAXUINT || ssimg->height == G_MAXUINT) { - pixbuf = gdk_pixbuf_new_from_file (ssimg->filename, NULL); + if (as_screenshot_get_media_kind (ssimg->screenshot) == AS_SCREENSHOT_MEDIA_KIND_VIDEO) { + gtk_video_set_filename (GTK_VIDEO (ssimg->video), ssimg->filename); + ssimg->current_image = "video"; } else { - /* this is always going to have alpha */ - pixbuf = gdk_pixbuf_new_from_file_at_scale (ssimg->filename, - (gint) (ssimg->width * ssimg->scale), - (gint) (ssimg->height * ssimg->scale), - FALSE, NULL); - } - - /* show icon */ - if (g_strcmp0 (ssimg->current_image, "image1") == 0) { - if (pixbuf != NULL) { - gtk_picture_set_pixbuf (GTK_PICTURE (ssimg->image2), pixbuf); + g_autoptr(GdkPixbuf) pixbuf = NULL; + + /* no need to composite */ + if (ssimg->width == G_MAXUINT || ssimg->height == G_MAXUINT) { + pixbuf = gdk_pixbuf_new_from_file (ssimg->filename, NULL); + } else { + /* this is always going to have alpha */ + pixbuf = gdk_pixbuf_new_from_file_at_scale (ssimg->filename, + (gint) (ssimg->width * ssimg->scale), + (gint) (ssimg->height * ssimg->scale), + FALSE, NULL); } - gtk_stack_set_visible_child_name (GTK_STACK (ssimg->stack), "image2"); - ssimg->current_image = "image2"; - } else { - if (pixbuf != NULL) { - gtk_picture_set_pixbuf (GTK_PICTURE (ssimg->image1), pixbuf); + + /* show icon */ + if (g_strcmp0 (ssimg->current_image, "image1") == 0) { + if (pixbuf != NULL) + gtk_picture_set_pixbuf (GTK_PICTURE (ssimg->image2), pixbuf); + ssimg->current_image = "image2"; + } else { + if (pixbuf != NULL) + gtk_picture_set_pixbuf (GTK_PICTURE (ssimg->image1), pixbuf); + ssimg->current_image = "image1"; } - gtk_stack_set_visible_child_name (GTK_STACK (ssimg->stack), "image1"); - ssimg->current_image = "image1"; } + gtk_stack_set_visible_child_name (GTK_STACK (ssimg->stack), ssimg->current_image); + gtk_widget_show (GTK_WIDGET (ssimg)); ssimg->showing_image = TRUE; @@ -243,6 +245,11 @@ gs_screenshot_image_show_blurred (GsScreenshotImage *ssimg, if (pb == NULL) return; + if (g_strcmp0 (ssimg->current_image, "video") == 0) { + ssimg->current_image = "image1"; + gtk_stack_set_visible_child_name (GTK_STACK (ssimg->stack), ssimg->current_image); + } + if (g_strcmp0 (ssimg->current_image, "image1") == 0) { gtk_picture_set_pixbuf (GTK_PICTURE (ssimg->image1), pb); } else { @@ -542,11 +549,84 @@ gs_screenshot_show_spinner_cb (gpointer user_data) return FALSE; } +static const gchar * +gs_screenshot_image_get_url (GsScreenshotImage *ssimg) +{ + const gchar *url = NULL; + + /* load an image according to the scale factor */ + ssimg->scale = (guint) gtk_widget_get_scale_factor (GTK_WIDGET (ssimg)); + + if (as_screenshot_get_media_kind (ssimg->screenshot) == AS_SCREENSHOT_MEDIA_KIND_VIDEO) { + GPtrArray *videos; + AsVideo *best_video = NULL; + gint64 best_size = G_MAXINT64; + gint64 wh = (gint64) ssimg->width * ssimg->scale * ssimg->height * ssimg->scale; + + videos = as_screenshot_get_videos (ssimg->screenshot); + for (guint i = 0; videos != NULL && i < videos->len; i++) { + AsVideo *adept = g_ptr_array_index (videos, i); + gint64 tmp; + + tmp = ABS (wh - (gint64) (as_video_get_width (adept) * as_video_get_height (adept))); + if (tmp < best_size) { + best_size = tmp; + best_video = adept; + if (!tmp) + break; + } + } + + if (best_video) + url = as_video_get_url (best_video); + } else if (as_screenshot_get_media_kind (ssimg->screenshot) == AS_SCREENSHOT_MEDIA_KIND_IMAGE) { + AsImage *im; + + im = as_screenshot_get_image (ssimg->screenshot, + ssimg->width * ssimg->scale, + ssimg->height * ssimg->scale); + + /* if we've failed to load a HiDPI image, fallback to LoDPI */ + if (im == NULL && ssimg->scale > 1) { + ssimg->scale = 1; + im = as_screenshot_get_image (ssimg->screenshot, + ssimg->width, + ssimg->height); + } + + if (im) + url = as_image_get_url (im); + } + + return url; +} + +static void +gs_screenshot_video_downloaded_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GsScreenshotImage) ssimg = user_data; + g_autoptr(GError) error = NULL; + + if (gs_download_file_finish (ssimg->session, result, &error)) { + gs_screenshot_image_stop_spinner (ssimg); + as_screenshot_show_image (ssimg); + + g_clear_object (&ssimg->cancellable); + } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_debug ("Failed to download screenshot video: %s", error->message); + /* Reset the width request, thus the image shrinks when the window width is small */ + gtk_widget_set_size_request (ssimg->stack, -1, (gint) ssimg->height); + gs_screenshot_image_stop_spinner (ssimg); + gs_screenshot_image_set_error (ssimg, _("Screenshot not found")); + } +} + void gs_screenshot_image_load_async (GsScreenshotImage *ssimg, GCancellable *cancellable) { - AsImage *im = NULL; const gchar *url; g_autofree gchar *basename = NULL; g_autofree gchar *cache_kind = NULL; @@ -563,20 +643,8 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, /* Reset the width request, thus the image shrinks when the window width is small */ gtk_widget_set_size_request (ssimg->stack, -1, (gint) ssimg->height); - /* load an image according to the scale factor */ - ssimg->scale = (guint) gtk_widget_get_scale_factor (GTK_WIDGET (ssimg)); - im = as_screenshot_get_image (ssimg->screenshot, - ssimg->width * ssimg->scale, - ssimg->height * ssimg->scale); - - /* if we've failed to load a HiDPI image, fallback to LoDPI */ - if (im == NULL && ssimg->scale > 1) { - ssimg->scale = 1; - im = as_screenshot_get_image (ssimg->screenshot, - ssimg->width, - ssimg->height); - } - if (im == NULL) { + url = gs_screenshot_image_get_url (ssimg); + if (url == NULL) { /* TRANSLATORS: this is when we request a screenshot size that * the generator did not create or the parser did not add */ gs_screenshot_image_set_error (ssimg, _("Screenshot size not found")); @@ -584,7 +652,6 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, } /* check if the URL points to a local file */ - url = as_image_get_url (im); if (g_str_has_prefix (url, "file://")) { g_free (ssimg->filename); ssimg->filename = g_strdup (url + 7); @@ -629,11 +696,13 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, /* if we're not showing a full-size image, we try loading a blurred * smaller version of it straight away */ if (!ssimg->showing_image && + as_screenshot_get_media_kind (ssimg->screenshot) == AS_SCREENSHOT_MEDIA_KIND_IMAGE && ssimg->width > AS_IMAGE_THUMBNAIL_WIDTH && ssimg->height > AS_IMAGE_THUMBNAIL_HEIGHT) { const gchar *url_thumb; g_autofree gchar *basename_thumb = NULL; g_autofree gchar *cache_kind_thumb = NULL; + AsImage *im; im = as_screenshot_get_image (ssimg->screenshot, AS_IMAGE_THUMBNAIL_WIDTH * ssimg->scale, AS_IMAGE_THUMBNAIL_HEIGHT * ssimg->scale); @@ -684,11 +753,13 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, } /* cancel any previous messages */ - if (ssimg->message != NULL) { -#if SOUP_CHECK_VERSION(3, 0, 0) + if (ssimg->cancellable != NULL) { g_cancellable_cancel (ssimg->cancellable); g_clear_object (&ssimg->cancellable); -#else + } + + if (ssimg->message != NULL) { +#if !SOUP_CHECK_VERSION(3, 0, 0) soup_session_cancel_message (ssimg->session, ssimg->message, SOUP_STATUS_CANCELLED); @@ -696,6 +767,22 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, g_clear_object (&ssimg->message); } + if (as_screenshot_get_media_kind (ssimg->screenshot) == AS_SCREENSHOT_MEDIA_KIND_VIDEO) { + g_autofree gchar *uri_str = g_uri_to_string (base_uri); + g_autoptr(GFile) output_file = NULL; + + ssimg->cancellable = g_cancellable_new (); + output_file = g_file_new_for_path (ssimg->filename); + + /* Make sure the spinner takes approximately the size the screenshot will use */ + gtk_widget_set_size_request (ssimg->stack, (gint) ssimg->width, (gint) ssimg->height); + + gs_download_file_async (ssimg->session, uri_str, output_file, G_PRIORITY_DEFAULT, NULL, NULL, + ssimg->cancellable, gs_screenshot_video_downloaded_cb, g_object_ref (ssimg)); + + return; + } + #if SOUP_CHECK_VERSION(3, 0, 0) ssimg->message = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri); #else @@ -717,9 +804,6 @@ gs_screenshot_image_load_async (GsScreenshotImage *ssimg, gs_screenshot_soup_msg_set_modified_request (ssimg->message, file); } - /* Make sure the spinner takes approximately the size the screenshot will use */ - gtk_widget_set_size_request (ssimg->stack, (gint) ssimg->width, (gint) ssimg->height); - ssimg->load_timeout_id = g_timeout_add_seconds (SPINNER_TIMEOUT_SECS, gs_screenshot_show_spinner_cb, ssimg); @@ -764,11 +848,13 @@ gs_screenshot_image_dispose (GObject *object) ssimg->load_timeout_id = 0; } - if (ssimg->message != NULL) { -#if SOUP_CHECK_VERSION(3, 0, 0) + if (ssimg->cancellable != NULL) { g_cancellable_cancel (ssimg->cancellable); g_clear_object (&ssimg->cancellable); -#else + } + + if (ssimg->message != NULL) { +#if !SOUP_CHECK_VERSION(3, 0, 0) soup_session_cancel_message (ssimg->session, ssimg->message, SOUP_STATUS_CANCELLED); @@ -836,6 +922,7 @@ gs_screenshot_image_class_init (GsScreenshotImageClass *klass) gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, stack); gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, image1); gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, image2); + gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, video); gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, box_error); gtk_widget_class_bind_template_child (widget_class, GsScreenshotImage, label_error); diff --git a/src/gs-screenshot-image.ui b/src/gs-screenshot-image.ui index 2606be51d800a6c46ffac4dd327da0cb329e3e5b..a108ba77e78a8de708578445257852311e850240 100644 --- a/src/gs-screenshot-image.ui +++ b/src/gs-screenshot-image.ui @@ -55,6 +55,19 @@ + + + video + + + + + + + + error