static char * program_get_metadata_fallback (MexGenericContent *gc, MexContentMetadata key) { MexContent *content = MEX_CONTENT (gc); const gchar *showname; gchar *target; switch (key) { case MEX_CONTENT_METADATA_TITLE: showname = mex_content_get_metadata (content, MEX_CONTENT_METADATA_SERIES_NAME); if (!showname) { const gchar *url; gchar *basename; url = mex_content_get_metadata (content, MEX_CONTENT_METADATA_URL); basename = g_path_get_basename (url); target = g_uri_unescape_string (basename, NULL); g_free (basename); } else { const gchar *episode, *season; episode = mex_content_get_metadata (content, MEX_CONTENT_METADATA_EPISODE); season = mex_content_get_metadata (content, MEX_CONTENT_METADATA_SEASON); if (episode && season) { target = g_strdup_printf ("%s: Season %s, Episode %s", showname, season, episode); } else if (episode) { target = g_strdup_printf ("%s: Episode %s", showname, episode); } else if (season) { target = g_strdup_printf ("%s: Season %s", showname, season); } else { target = g_strdup (showname); } } break; default: target = NULL; break; } return target; }
/* content view */ static void mex_music_player_set_content (MexContentView *player, MexContent *content) { MexMusicPlayerPrivate *priv = MEX_MUSIC_PLAYER (player)->priv; gchar *album_artist; const gchar *uri, *album, *artist, *title; ClutterActorIter iter; ClutterActor *child, *container; if (priv->content) g_object_unref (priv->content); priv->content = content; if (!content) return; g_object_ref (content); /* title */ title = mex_content_get_metadata (content, MEX_CONTENT_METADATA_TITLE); mx_label_set_text (MX_LABEL (priv->title_label), title); /* subtitle (album, artist) */ album = mex_content_get_metadata (content, MEX_CONTENT_METADATA_ALBUM); artist = mex_content_get_metadata (content, MEX_CONTENT_METADATA_ARTIST); album_artist = g_strconcat (album, ", ", artist , NULL); mx_label_set_text (MX_LABEL (priv->subtitle_label), album_artist); g_free (album_artist); /* uri */ uri = mex_content_get_metadata (content, MEX_CONTENT_METADATA_STREAM); clutter_media_set_uri (priv->player, uri); /* find the item in the list */ container = mex_script_get_actor (priv->script, "tracks"); clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { MexContent *button_content; button_content = g_object_get_data (G_OBJECT (child), "content"); mx_button_set_toggled (MX_BUTTON (child), (button_content == content)); } }
static void _add_remove_recursive (MexModel *queue_model, MexModel *model, gboolean add) { gint len, i; len = mex_model_get_length (model); for (i=0; i < len; i++) { MexContent *content; const gchar *mimetype; content = mex_model_get_content (model, i); mimetype = mex_content_get_metadata (content, MEX_CONTENT_METADATA_MIMETYPE); /* Don't accidentally add a directory or group to the queue */ if (!g_strcmp0 (mimetype, "x-grl/box") || !g_strcmp0 (mimetype, "x-mex/group")) continue; if (add) mex_model_add_content (queue_model, content); else mex_model_remove_content (queue_model, content); } g_object_unref (model); }
static void _update_thumbnail (MexContentTile *tile) { MexContentTilePrivate *priv = tile->priv; MexDownloadQueue *queue; const gchar *file; queue = mex_download_queue_get_default (); /* cancel any download already in progress */ if (priv->download_id) { mex_download_queue_cancel (queue, priv->download_id); priv->download_id = NULL; } /* update thumbnail */ file = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_STILL); /* TODO: Display a spinner? */ if (file) priv->download_id = mex_download_queue_enqueue (queue, file, download_queue_completed, tile); else priv->thumbnail_loaded = TRUE; }
static void _update_logo (MexContentTile *tile) { ClutterActor *image; GError *err = NULL; const gchar *logo_url; logo_url = mex_content_get_metadata (tile->priv->content, MEX_CONTENT_METADATA_STATION_LOGO); if (!logo_url) { mex_tile_set_primary_icon (MEX_TILE (tile), NULL); return; } image = mx_image_new (); if (g_str_has_prefix (logo_url, "file://")) logo_url = logo_url + 7; mx_image_set_from_file_at_size (MX_IMAGE (image), logo_url, 26, 26, &err); if (err) { g_warning ("Could not load station logo: %s", err->message); g_clear_error (&err); return; } mex_tile_set_primary_icon (MEX_TILE (tile), image); }
void mex_content_set_last_used_metadatas (MexContent *content) { guint count; gchar str[20], *nstr; const gchar *play_count; GDateTime *datetime; GTimeVal tv; play_count = mex_content_get_metadata (content, MEX_CONTENT_METADATA_PLAY_COUNT); if (play_count) { count = atoi (play_count); count++; } else count = 1; snprintf (str, sizeof (str), "%u", count); mex_content_set_metadata (content, MEX_CONTENT_METADATA_PLAY_COUNT, str); datetime = g_date_time_new_now_local (); if (datetime) { if (g_date_time_to_timeval (datetime, &tv)) { tv.tv_usec = 0; nstr = g_time_val_to_iso8601 (&tv); if (nstr) { mex_content_set_metadata (content, MEX_CONTENT_METADATA_LAST_PLAYED_DATE, nstr); g_free (nstr); } } g_date_time_unref (datetime); } }
static void mex_content_box_toggle_open (MexContentBox *box) { MexContentBoxPrivate *priv = box->priv; gboolean close_notified, next_is_open; const gchar *mimetype; /* search history items should not appear in the "open" state */ mimetype = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE); if (!g_strcmp0 (mimetype, "x-mex/search")) return; /* if the close animation was cancelled then no notify for the closed state * will have been sent, therefore notify for the opened state does not need * to be emitted */ close_notified = (!priv->is_open && !clutter_timeline_is_playing (priv->timeline)); next_is_open = !priv->is_open; if (next_is_open) { /* opening */ clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_FORWARD); mx_stylable_set_style_class (MX_STYLABLE (box), "open"); /* refresh the action list */ mex_content_view_set_content (MEX_CONTENT_VIEW (priv->action_list), priv->content); priv->extras_visible = TRUE; if (close_notified) g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_OPEN]); mex_push_focus (MX_FOCUSABLE (priv->action_list)); } else { priv->is_closing = TRUE; /* closing */ mex_push_focus (MX_FOCUSABLE (priv->tile)); clutter_timeline_set_direction (priv->timeline, CLUTTER_TIMELINE_BACKWARD); priv->is_closing = FALSE; priv->extras_visible = TRUE; } if (!clutter_timeline_is_playing (priv->timeline)) clutter_timeline_rewind (priv->timeline); clutter_timeline_start (priv->timeline); priv->is_open = next_is_open; }
/* * If we need to, generate a thumbnail. * * If the URL is not file:/// then we assume it's okay to use. * */ static void mex_grilo_program_thumbnail (MexContent *content, GrlMedia *media) { const char *url; char *thumb_path; /* If the media isn't local, then we'll ignore it for now */ url = grl_media_get_url (media); if (url == NULL || !g_str_has_prefix (url, "file:///")) return; if (GRL_IS_MEDIA_BOX (media)) { gchar *tmp; tmp = g_build_filename (mex_get_data_dir (), "common", "folder-tile.png", NULL); mex_grilo_program_set_metadata (content, MEX_CONTENT_METADATA_STILL, tmp); g_free (tmp); return; } /* * If we're already got a thumbnail, see if we're happy with it or want to * ignore it. */ thumb_path = (char*)mex_content_get_metadata (content, MEX_CONTENT_METADATA_STILL); if (thumb_path) { /* If the thumbnail is already a good one, we're done */ if (thumb_path && strstr (thumb_path, "/.thumbnails/x-huge/")) return; /* * If the thumbnail currently set is a "normal" thumbnail we ignore it on the * basis that it's likely to have been generated by Totem, and thus is bad. * If your platform doesn't have Totem then this can be removed! */ if (thumb_path && strstr (thumb_path, "/.thumbnails/normal/")) mex_grilo_program_set_metadata (content, MEX_CONTENT_METADATA_STILL, NULL); } /* * Now see if we've a thumbnail generated already. TODO: use the Tumbler API * to do this. */ thumb_path = get_thumbnail_path_for_uri (url); if (g_file_test (thumb_path, G_FILE_TEST_EXISTS)) { gchar *thumb_uri = g_filename_to_uri (thumb_path, NULL, NULL); mex_grilo_program_set_metadata (content, MEX_CONTENT_METADATA_STILL, thumb_uri); g_free (thumb_uri); } else { mex_thumbnailer_generate (url, grl_media_get_mime (media), thumbnail_cb, content); } g_free (thumb_path); }
static void mex_content_box_notify_key_focus_cb (ClutterStage *stage, GParamSpec *pspec, MexContentBox *self) { MexContentBoxPrivate *priv = self->priv; ClutterActor *focus = clutter_stage_get_key_focus (stage); if (focus == priv->tile) { gboolean show_info; if (mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_SYNOPSIS) || mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_DATE) || mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_CREATION_DATE) || mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_DURATION)) show_info = TRUE; else { MexActionManager *manager = mex_action_manager_get_default (); GList *actions = mex_action_manager_get_actions_for_content (manager, priv->content); if (actions && actions->next) show_info = TRUE; else show_info = FALSE; g_list_free (actions); } if (show_info) { ClutterActor *icon = mx_icon_new (); mx_stylable_set_style_class (MX_STYLABLE (icon), "Info"); mex_tile_set_secondary_icon (MEX_TILE (priv->tile), icon); } } else mex_tile_set_secondary_icon (MEX_TILE (priv->tile), NULL); }
static void mex_search_plugin_mimetype_set_cb (MexContent *content) { const gchar *mime = mex_content_get_metadata (content, MEX_CONTENT_METADATA_MIMETYPE); if (!mime || !(*mime) || g_str_equal (mime, "application/x-shockwave-flash")) mex_content_set_metadata (content, MEX_CONTENT_METADATA_MIMETYPE, "x-mex/media"); }
static void mex_media_controls_show_description (MexMediaControls *self, gboolean show) { MexMediaControlsPrivate *priv = self->priv; MxLabel *label; ClutterActor *play_pause_button, *stop_button, *placeholder, *add_to_queue_button; const gchar *text; label = (MxLabel*) clutter_script_get_object (priv->script, "progress-label"); play_pause_button = (ClutterActor*) clutter_script_get_object (priv->script, "play-pause-button"); stop_button = (ClutterActor*) clutter_script_get_object (priv->script, "stop-button"); add_to_queue_button = (ClutterActor*) clutter_script_get_object (priv->script, "add-to-queue-button"); /* the placeholder actor will accept focus so that the title and description * become visible as the user navigates up and down */ placeholder = (ClutterActor*) clutter_script_get_object (priv->script, "placeholder"); if (show) { clutter_actor_hide (priv->slider); clutter_actor_hide (play_pause_button); clutter_actor_hide (stop_button); clutter_actor_hide (add_to_queue_button); clutter_actor_show (placeholder); if (priv->content) text = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_SYNOPSIS); else text = NULL; mx_label_set_text (label, (text) ? text : ""); } else { mx_label_set_text (label, ""); clutter_actor_show (priv->slider); clutter_actor_show (play_pause_button); clutter_actor_show (stop_button); clutter_actor_show (add_to_queue_button); clutter_actor_hide (placeholder); } priv->show_description = show; }
static gint order_by_func (gconstpointer a, gconstpointer b, gpointer user_data) { MexContent **content_a = (MexContent **) a; MexContent **content_b = (MexContent **) b; SortFuncInfo *info = user_data; const gchar *value_a, *value_b; value_a = mex_content_get_metadata (*content_a, info->key); value_b = mex_content_get_metadata (*content_b, info->key); if (info->descending) return g_strcmp0 (value_b, value_a); else return g_strcmp0 (value_a, value_b); }
static void _update_thumbnail (MexContentTile *tile) { MexContentTilePrivate *priv = tile->priv; MexDownloadQueue *queue; const gchar *uri; GFile *file; queue = mex_download_queue_get_default (); /* cancel any download already in progress */ if (priv->download_id) { mex_download_queue_cancel (queue, priv->download_id); priv->download_id = NULL; } /* update thumbnail */ uri = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_STILL); if (uri) { file = g_file_new_for_uri (uri); /* TODO: Display a spinner? */ if (file) { gchar *path = g_file_get_path (file); if (path) { mx_image_set_from_file_at_size (MX_IMAGE (priv->image), path, priv->thumb_width, priv->thumb_height, NULL); priv->thumbnail_loaded = TRUE; priv->image_set = TRUE; clutter_actor_set_size (priv->image, priv->thumb_width, priv->thumb_height); g_free (path); } else { priv->download_id = mex_download_queue_enqueue (queue, uri, download_queue_completed, tile); } g_object_unref (file); } } else priv->thumbnail_loaded = TRUE; }
static void _update_title (MexContentTile *tile) { MexContentTilePrivate *priv = tile->priv; const gchar *title; /* set title */ title = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_TITLE); mex_tile_set_label (MEX_TILE (tile), title); }
static void mex_search_plugin_history_cb (MxAction *action, MexSearchPlugin *self) { MexSearchPluginPrivate *priv = self->priv; MexContent *content = mex_action_get_content (action); const gchar *text = mex_content_get_metadata (content, MEX_CONTENT_METADATA_TITLE); mx_entry_set_text (MX_ENTRY (priv->search_entry), text); mex_search_plugin_search_cb (self); }
static void tile_created_cb (MexProxy *proxy, GObject *content, GObject *object, gpointer controls) { const gchar *mime_type; ClutterEffect *effect; ClutterColor color = { 0, 0, 0, 60 }; /* filter out folders */ mime_type = mex_content_get_metadata (MEX_CONTENT (content), MEX_CONTENT_METADATA_MIMETYPE); if (g_strcmp0 (mime_type, "x-grl/box") == 0) { g_signal_stop_emission_by_name (proxy, "object-created"); return; } mex_tile_set_important (MEX_TILE (object), TRUE); clutter_actor_set_reactive (CLUTTER_ACTOR (object), TRUE); g_object_set (object, "thumb-height", 140, "thumb-width", 250, NULL); g_signal_connect (object, "key-press-event", G_CALLBACK (key_press_event_cb), controls); g_signal_connect (object, "button-release-event", G_CALLBACK (button_release_event_cb), controls); effect = g_object_new (MEX_TYPE_SHADOW, "radius-x", 15, "radius-y", 15, "color", &color, "enabled", FALSE, NULL); clutter_actor_add_effect_with_name (CLUTTER_ACTOR (object), "shadow", effect); effect = g_object_new (MEX_TYPE_SHADOW, "radius-x", 15, "radius-y", 15, "color", &color, NULL); clutter_actor_add_effect_with_name (mx_bin_get_child (MX_BIN (object)), "shadow", effect); g_signal_connect (object, "focus-in", G_CALLBACK (tile_focus_in_cb), NULL); g_signal_connect (object, "focus-out", G_CALLBACK (tile_focus_out_cb), NULL); tile_focus_out_cb (MX_BIN (object)); }
static void mex_media_controls_update_header (MexMediaControls *self) { MexMediaControlsPrivate *priv = self->priv; ClutterActor *label, *image; const gchar *logo_url; GError *err = NULL; label = (ClutterActor*) clutter_script_get_object (priv->script, "title-label"); image = (ClutterActor*) clutter_script_get_object (priv->script, "logo"); mx_label_set_text (MX_LABEL (label), mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_TITLE)); logo_url = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_STATION_LOGO); if (!logo_url) { clutter_actor_hide (image); } else { clutter_actor_show (image); if (g_str_has_prefix (logo_url, "file://")) logo_url = logo_url + 7; mx_image_set_from_file (MX_IMAGE (image), logo_url, &err); if (err) { g_warning ("Could not load logo: %s", err->message); g_clear_error (&err); } } }
void mex_program_get_thumbnail (MexProgram *program, MexGetThumbnailReply reply, gpointer userdata) { MexProgramClass *klass; MexProgramPrivate *priv; const char *still; g_return_if_fail (MEX_IS_PROGRAM (program)); priv = program->priv; /* Some backends (eg. Apple trailers) may be able to cache the stream so we can just use the standard metadata method to obtain it */ still = mex_content_get_metadata (MEX_CONTENT (program), MEX_PROGRAM_METADATA_STILL); if (stream != NULL) { struct _GetStreamPayload *payload = g_slice_new (struct _GetStreamPayload); payload->program = g_object_ref (program); payload->reply = reply; payload->userdata = userdata; /* Technically the stream could be freed before the idle is called Great, now I want refcounted strings */ payload->stream = g_strdup (stream); g_idle_add (emit_get_stream_reply, payload); return; } /* Other backends require custom methods to get the stream url */ klass = MEX_PROGRAM_GET_CLASS (program); if (klass->get_stream) { klass->get_stream (program, reply, userdata); } else { struct _GetStreamPayload *payload = g_slice_new (struct _GetStreamPayload); payload->program = g_object_ref (program); payload->reply = reply; payload->userdata = userdata; payload->stream = NULL; g_idle_add (emit_get_stream_reply, payload); } }
static void mex_queue_button_update (MexQueueButton *button) { MexQueueButtonPrivate *priv = button->priv; if (mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_QUEUED)) { mx_label_set_text (MX_LABEL (priv->label), _("Remove from queue")); g_signal_handlers_block_by_func (button, _queue_button_notify_toggled_cb, button); mx_button_set_toggled (MX_BUTTON (button), TRUE); g_signal_handlers_unblock_by_func (button, _queue_button_notify_toggled_cb, button); } else { mx_label_set_text (MX_LABEL (priv->label), _("Add to queue")); g_signal_handlers_block_by_func (button, _queue_button_notify_toggled_cb, button); mx_button_set_toggled (MX_BUTTON (button), FALSE); g_signal_handlers_unblock_by_func (button, _queue_button_notify_toggled_cb, button); } if (mx_spinner_get_animating (MX_SPINNER (button->priv->spinner))) { mx_label_set_text (MX_LABEL (button->priv->label), _("Adding to queue")); clutter_actor_hide (button->priv->icon); clutter_actor_show (button->priv->spinner); } else { clutter_actor_hide (button->priv->spinner); clutter_actor_show (button->priv->icon); } }
static void _queue_button_notify_toggled_cb (MxButton *button, GParamSpec *pspec, MexQueueButton *q_button) { MexQueueButtonPrivate *priv = q_button->priv; const gchar *mimetype; gboolean directory; mimetype = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE); directory = !g_strcmp0 (mimetype, "x-grl/box") || !g_strcmp0 (mimetype, "x-mex/group"); /* Triggers a train of actions that makes content have it's queued * property set which then runs the notify cb which calls * mex_queue_button_update so we don't need to run it directly */ if (mx_button_get_toggled (button)) { mex_queue_button_set_animated (q_button, TRUE); if (directory) _add_from_directory (q_button, TRUE); else mex_model_add_content (priv->queue_model, priv->content); } else { mex_queue_button_set_animated (q_button, FALSE); if (directory) _add_from_directory (q_button, FALSE); else mex_model_remove_content (priv->queue_model, priv->content); } }
static void mex_music_player_set_context (MexContentView *player, MexModel *model) { MexMusicPlayerPrivate *priv = MEX_MUSIC_PLAYER (player)->priv; ClutterActor *box, *button; MexContent *content; gint i; if (priv->model) g_object_unref (priv->model); priv->model = model; if (model) g_object_ref (model); box = mex_script_get_actor (priv->script, "tracks"); clutter_actor_remove_all_children (box); for (i = 0; (content = mex_model_get_content (model, i)); i++) { const gchar *title; title = mex_content_get_metadata (content, MEX_CONTENT_METADATA_TITLE); button = mx_button_new_with_label (title); mx_stylable_set_style_class (MX_STYLABLE (button), "Track"); mx_button_set_is_toggle (MX_BUTTON (button), TRUE); g_object_set_data (G_OBJECT (button), "content", content); g_signal_connect (button, "clicked", G_CALLBACK (mex_music_player_item_clicked), player); mx_button_set_icon_position (MX_BUTTON (button), MX_POSITION_RIGHT); clutter_actor_add_child (box, button); } }
static void mex_content_box_set_content (MexContentView *view, MexContent *content) { MexContentBox *box = (MexContentBox *) view; MexContentBoxPrivate *priv; priv = box->priv; if (priv->content == content) return; mex_content_view_set_content (MEX_CONTENT_VIEW (priv->tile), content); mex_content_view_set_content (MEX_CONTENT_VIEW (priv->action_list), content); if (priv->content) { GList *l; for (l = priv->bindings; l; l = l->next) g_object_unref (l->data); g_list_free (priv->bindings); priv->bindings = NULL; g_object_unref (priv->content); priv->content = NULL; } if (content) { int i; priv->content = g_object_ref_sink (content); for (i = 0; content_bindings[i].id != MEX_CONTENT_METADATA_NONE; i++) { const gchar *property; GBinding *binding; property = mex_content_get_property_name (content, content_bindings[i].id); if (property == NULL) { /* The Content does not provide a GObject property for this * kind of metadata, we can only sync at creation time */ const gchar *metadata; metadata = mex_content_get_metadata (content, content_bindings[i].id); g_object_set (box, content_bindings[i].target, metadata, NULL); continue; } if (content_bindings[i].fallback) binding = g_object_bind_property_full (content, property, box, content_bindings[i].target, G_BINDING_SYNC_CREATE, content_bindings[i].fallback, NULL, content, NULL); else binding = g_object_bind_property (content, property, box, content_bindings[i].target, G_BINDING_SYNC_CREATE); priv->bindings = g_list_prepend (priv->bindings, binding); } } }
void mex_media_controls_set_content (MexMediaControls *self, MexContent *content, MexModel *context) { MexMediaControlsPrivate *priv = self->priv; gboolean show_description; g_return_if_fail (MEX_IS_CONTENT (content)); if (priv->model == context) { if (priv->content == content) return; if (priv->content) g_object_unref (priv->content); if (content) priv->content = g_object_ref_sink (content); mex_media_controls_focus_content (self, priv->content); mex_media_controls_update_header (self); return; } if (priv->model) { g_object_unref (priv->model); priv->model = NULL; } if (context) priv->model = g_object_ref_sink (context); if (priv->content) { g_object_unref (priv->content); priv->content = NULL; } if (content) priv->content = g_object_ref_sink (content); priv->is_queue_model = FALSE; mex_media_controls_update_header (self); /* We may not have a context if we're launched by something like SetUri*/ if (context) { MexModel *orig_model; orig_model = mex_model_get_model (context); g_object_set (G_OBJECT (priv->proxy_model), "model", orig_model, NULL); mex_view_model_set_start_content (priv->proxy_model, priv->content); mex_view_model_set_loop (priv->proxy_model, TRUE); /* Work out if the context was a queue FIXME unreliable */ /* From coloumn context = MexViewModel MexAggregateModel MexQueueModel */ /* From grid context = MexProxyModel MexProxyModel MexQueueModel */ if (MEX_IS_PROXY_MODEL (context)) { MexModel *model_from_proxy; model_from_proxy = mex_proxy_model_get_model (MEX_PROXY_MODEL (orig_model)); if (MEX_IS_QUEUE_MODEL (model_from_proxy)) priv->is_queue_model = TRUE; } else if (MEX_IS_AGGREGATE_MODEL (orig_model)) { MexModel *real_model; real_model = mex_aggregate_model_get_model_for_content (MEX_AGGREGATE_MODEL (orig_model), content); if (MEX_IS_QUEUE_MODEL (real_model)) priv->is_queue_model = TRUE; } } /* Update content on the queue button */ mex_content_view_set_content (MEX_CONTENT_VIEW (priv->queue_button), priv->content); /* show the description rather than the seek bar for certain content */ show_description = !g_strcmp0 ("x-mex/tv", mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE)); mex_media_controls_show_description(self, show_description); }
static void mex_get_stream_cb (MexProgram *program, const gchar *url, const GError *error, gpointer user_data) { MexPlayer *player = user_data; MexPlayerPrivate *priv = player->priv; MexGenericContent *generic_content; #ifdef USE_PLAYER_CLUTTER_GST ClutterGstVideoTexture *video_texture; #endif /* if idle mode has been set before the program stream was found */ if (priv->idle_mode) return; if (G_UNLIKELY (error)) { g_warning ("Could not play content: %s (%s)", error->message, url); return; } #ifdef USE_PLAYER_CLUTTER_GST /* We seek at the precise time when the file is local, but we * seek to key frame when streaming */ video_texture = CLUTTER_GST_VIDEO_TEXTURE (priv->media); if (g_str_has_prefix (url, "file://")) { clutter_gst_video_texture_set_seek_flags (video_texture, CLUTTER_GST_SEEK_FLAG_ACCURATE); } else { clutter_gst_video_texture_set_seek_flags (video_texture, CLUTTER_GST_SEEK_FLAG_NONE); } /* TODO when we have settings we can configure this feature */ if (g_str_has_prefix (mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE), "audio/")) { GstElement *gst_element, *visual; gint gst_flags; gst_element = clutter_gst_video_texture_get_pipeline (video_texture); g_object_get (G_OBJECT (gst_element), "flags", &gst_flags, NULL); gst_flags = (GST_PLAY_FLAG_VIS | gst_flags); g_object_set (G_OBJECT (gst_element), "flags", gst_flags, NULL); visual = gst_element_factory_make ("libvisual_infinite", NULL); if (visual) g_object_set (G_OBJECT (gst_element), "vis-plugin", visual, NULL); } #endif clutter_media_set_uri (CLUTTER_MEDIA (priv->media), url); generic_content = MEX_GENERIC_CONTENT (priv->content); if (mex_generic_content_get_last_position_start (generic_content)) clutter_media_set_progress (CLUTTER_MEDIA (priv->media), priv->position); clutter_media_set_playing (CLUTTER_MEDIA (priv->media), TRUE); }
GList * mex_action_manager_get_actions_for_content (MexActionManager *manager, MexContent *content) { GList *a, *actions; gpointer key, value; GHashTableIter iter; MexActionManagerPrivate *priv; const gchar *last_position, *mime; g_return_val_if_fail (MEX_IS_ACTION_MANAGER (manager), NULL); mime = mex_content_get_metadata (content, MEX_CONTENT_METADATA_MIMETYPE); last_position = mex_content_get_metadata (content, MEX_CONTENT_METADATA_LAST_POSITION); actions = NULL; priv = manager->priv; g_hash_table_iter_init (&iter, priv->actions); while (g_hash_table_iter_next (&iter, &key, &value)) { gint i; MexActionInfo *info = value; const gchar *action_name; action_name = mx_action_get_name (info->action); /* If there isn't a last position skip: */ if (!last_position) { if (g_str_equal (action_name, "play-from-last") || g_str_equal (action_name, "play-from-begin") || g_str_equal (action_name, "listen-from-begin")) continue; } else { /* We don't want resume and listen or play because * this action is provided by the start */ if (g_str_equal (action_name, "play") || g_str_equal (action_name, "listen")) continue; } /* If we've been asked for actions that apply to a * blank mime-type, only return actions that were * registered for a blank mime-type. */ if (!mime || !*mime) { if (!info->mime_types || !info->mime_types[0] || !(*info->mime_types[0])) actions = g_list_prepend (actions, info); continue; } /* Add actions that match a mime prefix */ for (i = 0; info->mime_types[i]; i++) { if (g_str_has_prefix (mime, info->mime_types[i])) { actions = g_list_prepend (actions, info); break; } } /* Now check that this action isn't for an excluded mimetype */ if (!info->exclude_mime_types) continue; for (i = 0; info->exclude_mime_types[i]; i++) { if (g_str_has_prefix (mime, info->exclude_mime_types[i])) { actions = g_list_remove (actions, info); break; } } } actions = g_list_sort (actions, mex_action_manager_sort_cb); for (a = actions; a; a = a->next) a->data = ((MexActionInfo *)a->data)->action; return actions; }
/* MexContentView implementation */ static void mex_player_set_content (MexContentView *view, MexContent *content) { MexPlayerPrivate *priv = MEX_PLAYER (view)->priv; if (priv->model) mex_media_controls_set_content (MEX_MEDIA_CONTROLS (priv->controls), content, priv->model); if (priv->related_tile) { g_object_unref (priv->related_tile); priv->related_tile = NULL; } if (content) { const gchar *sposition, *sduration; if (priv->content) { save_old_content (MEX_PLAYER (view)); g_object_unref (priv->content); priv->content = NULL; } priv->content = g_object_ref_sink (content); sposition = mex_content_get_metadata (content, MEX_CONTENT_METADATA_LAST_POSITION); sduration = mex_content_get_metadata (content, MEX_CONTENT_METADATA_DURATION); if (sduration && !mex_media_controls_get_playing_queue (MEX_MEDIA_CONTROLS (priv->controls))) priv->duration = atoi (sduration); else priv->duration = 0; if (priv->duration > 0) { if (sposition) { int position = atoi (sposition); priv->position = (gdouble) position / (gdouble) priv->duration; } else { priv->position = 0.0; } } if (MEX_IS_PROGRAM (content)) { mex_program_get_stream (MEX_PROGRAM (content), mex_get_stream_cb, view); } else { const gchar *uri; uri = mex_content_get_metadata (content, MEX_CONTENT_METADATA_STREAM); mex_get_stream_cb (NULL, uri, NULL, view); } if (priv->info_visible) { clutter_actor_animate (priv->info_panel, CLUTTER_EASE_IN_SINE, 250, "opacity", 0x00, NULL); mx_widget_set_disabled (MX_WIDGET (priv->info_panel), TRUE); priv->info_visible = FALSE; } mex_player_set_controls_visible (MEX_PLAYER (view), TRUE); } else { if (priv->content) { g_object_unref (priv->content); priv->content = NULL; } } }
/* When the MexContent set on the queue button is a directory we * try to add the children of the directory and mark the directory * as queued. When the child items are added to the queue model they * are marked as queued via the controller */ static void _add_from_directory (MexQueueButton *q_button, gboolean add) { MexQueueButtonPrivate *priv = q_button->priv; MexFeed *feed; GrlSource *source; GList *metadata_keys; GList *query_keys; const gchar *filter; const gchar *stream_uri; /* check if this content is a group */ if (MEX_IS_GROUP_ITEM (priv->content)) { MexModel *model; model = mex_group_item_get_model (MEX_GROUP_ITEM (priv->content)); _add_remove_recursive (priv->queue_model, g_object_ref (model), add); /* _add_remove_recursive will unref feed */ mex_content_set_metadata (priv->content, MEX_CONTENT_METADATA_QUEUED, (add) ? "yes" : NULL); return; } if (!MEX_IS_GRILO_PROGRAM (priv->content)) return; g_object_get (priv->content, "feed", &feed, NULL); if (MEX_IS_GRILO_FEED (feed)) { g_object_get (feed, "grilo-source", &source, "grilo-query-keys", &query_keys, "grilo-metadata-keys", &metadata_keys, NULL); stream_uri = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_STREAM); /* tracker filesystem feed */ if (MEX_IS_GRILO_TRACKER_FEED (feed)) { gchar *orig_filter; g_object_get (feed, "tracker-filter", &orig_filter, NULL); filter = g_strdup_printf("FILTER(fn:starts-with(nie:url(?urn), '%s'))", stream_uri); feed = mex_grilo_tracker_feed_new (source, query_keys, metadata_keys, filter, NULL); mex_grilo_feed_query (MEX_GRILO_FEED (feed), orig_filter, 0, G_MAXINT); if (orig_filter) g_free (orig_filter); } /* We already know that the content is ia MexGriloProgram * box provides the root Grlio media content. * filesystem grilo feed */ else { GrlMedia *box; g_object_get (priv->content, "grilo-media", &box, NULL); feed = mex_grilo_feed_new (source, query_keys, metadata_keys, box); mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT); g_object_unref (box); } /* unref/free the stuff we g_object_get'ed */ if (source) g_object_unref (source); if (query_keys) g_list_free (query_keys); if (metadata_keys) g_list_free (metadata_keys); /* we don't actually want to add the priv->content into the queue model, * only it's children, which is where the QUEUED flag is usually set */ if (add) { g_signal_connect (feed, "notify::completed", G_CALLBACK (_add_directory_query_complete_cb), q_button); mex_content_set_metadata (priv->content, MEX_CONTENT_METADATA_QUEUED, "yes"); } else { g_signal_connect (feed, "notify::completed", G_CALLBACK (_remove_directory_query_complete_cb), q_button); mex_content_set_metadata (priv->content, MEX_CONTENT_METADATA_QUEUED, NULL); } } else { /* We're not working with a grilo feed therefore we can't help :( */ return; } }
static gboolean _start_video_preview (MexContentTile *self) { MexContentTilePrivate *priv = self->priv; GstElement *pipeline; gint gst_flags; const gchar *mimetype, *uri; /* Check we're still focused */ if (!mex_actor_has_focus (CLUTTER_ACTOR (self))) return FALSE; /* Don't play if the main player is still playing.. * too many videos spoil the broth. */ if (clutter_media_get_playing (mex_player_get_clutter_media (mex_player_get_default ()))) return FALSE; mimetype = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE); if ((mimetype) && strncmp (mimetype, "video/", 6) != 0) return FALSE; if (!(uri = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_STREAM))) return FALSE; priv->video_preview = clutter_gst_video_texture_new (); pipeline = clutter_gst_video_texture_get_pipeline (CLUTTER_GST_VIDEO_TEXTURE (priv->video_preview)); g_object_get (G_OBJECT (pipeline), "flags", &gst_flags, NULL); gst_flags = 1;//GST_PLAY_FLAG_VIDEO; g_object_set (G_OBJECT (pipeline), "flags", gst_flags, NULL); clutter_gst_video_texture_set_idle_material (CLUTTER_GST_VIDEO_TEXTURE (priv->video_preview), NULL); g_signal_connect (priv->video_preview, "eos", G_CALLBACK (_stop_video_eos), self); clutter_actor_set_opacity (priv->video_preview, 0); g_object_ref (priv->image); clutter_actor_remove_child (CLUTTER_ACTOR (self), priv->image); clutter_actor_add_child (CLUTTER_ACTOR (self), priv->video_preview); clutter_actor_animate (priv->video_preview, CLUTTER_LINEAR, 500, "opacity", 0xff, NULL); clutter_actor_set_size (priv->video_preview, (gfloat)priv->thumb_width, (gfloat)priv->thumb_height); clutter_media_set_uri (CLUTTER_MEDIA (priv->video_preview), uri); clutter_media_set_playing (CLUTTER_MEDIA (priv->video_preview), TRUE); if (priv->stop_video_preview <= 0) priv->stop_video_preview = g_timeout_add_seconds (180, (GSourceFunc)_stop_video_preview, self); return FALSE; }
static void _reset_thumbnail (MexContentTile *tile) { MexContentTilePrivate *priv = tile->priv; MexDownloadQueue *queue = mex_download_queue_get_default (); const gchar *mime = NULL; gchar *placeholder_filename = NULL; queue = mex_download_queue_get_default (); /* cancel any download already in progress */ if (priv->download_id) { mex_download_queue_cancel (queue, priv->download_id); priv->download_id = NULL; } priv->thumbnail_loaded = FALSE; /* Load placeholder image */ if (priv->content) mime = mex_content_get_metadata (priv->content, MEX_CONTENT_METADATA_MIMETYPE); if (mime && g_str_has_prefix (mime, "image/")) { placeholder_filename = "thumb-image.png"; } else if (mime && g_str_equal (mime, "x-mex/tv")) { placeholder_filename = "thumb-tv.png"; } else if (mime && g_str_equal (mime, "video/dvd")) { placeholder_filename = "thumb-disc.png"; } else if (mime && (g_str_has_prefix (mime, "video/") || g_str_equal (mime, "x-mex/media"))) { placeholder_filename = "thumb-video.png"; } else if (mime && (g_str_has_prefix (mime, "audio/"))) { placeholder_filename = "thumb-music.png"; } else if (mime && g_str_equal (mime, "x-grl/box")) { placeholder_filename = "folder-tile.png"; } else if (mime && g_str_equal (mime, "x-mex/group")) { placeholder_filename = "folder-tile.png"; } else if (mime && g_str_equal (mime, "x-mex/app")) { placeholder_filename = "thumb-app.png"; } if (placeholder_filename) { gchar *tmp; const gchar *dir = mex_get_data_dir (); tmp = g_build_filename (dir, "style", placeholder_filename, NULL); _update_thumbnail_from_image (tile, tmp); g_free (tmp); } else { mx_image_clear (MX_IMAGE (priv->image)); /* Reset the height - really, we ought to reset the width and height, * but for all our use-cases, we want to keep the set width. */ clutter_actor_set_height (priv->image, -1); priv->image_set = FALSE; return; } clutter_actor_set_size (priv->image, priv->thumb_width, priv->thumb_height); }
static void set_metadata_from_media (MexContent *content, GrlMedia *media, MexContentMetadata mex_key) { gchar *string; const gchar *cstring; GrlKeyID grl_key = _get_grl_key_from_mex (mex_key); gint n; gint year = 0; if (!grl_key) return; switch (grl_metadata_key_get_type (grl_key)) { case G_TYPE_STRING: cstring = grl_data_get_string (GRL_DATA (media), grl_key); if (cstring) { if (mex_key == MEX_CONTENT_METADATA_TITLE) { gchar *showname = NULL, *title, *season_str; gint season, episode; gchar *replacement; const gchar *mimetype; mimetype = mex_content_get_metadata (content, MEX_CONTENT_METADATA_MIMETYPE); if (!mimetype) mimetype = ""; if (g_str_has_prefix (mimetype, "video/")) { mex_metadata_from_uri (cstring, &title, &showname, &year, &season, &episode); } if (showname) { replacement = g_strdup_printf (_("Episode %d"), episode); } else { GRegex *regex; /* strip off any file extensions */ regex = g_regex_new ("\\.....?$", 0, 0, NULL); replacement = g_regex_replace (regex, cstring, -1, 0, "", 0, NULL); g_regex_unref (regex); } if (!replacement) replacement = g_strdup (cstring); mex_content_set_metadata (content, mex_key, replacement); mex_content_set_metadata (content, MEX_CONTENT_METADATA_SERIES_NAME, showname); season_str = g_strdup_printf (_("Season %d"), season); mex_content_set_metadata (content, MEX_CONTENT_METADATA_SEASON, season_str); g_free (season_str); if (year) { replacement = g_strdup_printf ("%d", year); mex_content_set_metadata (content, MEX_CONTENT_METADATA_YEAR, replacement); g_free (replacement); } } else mex_content_set_metadata (content, mex_key, cstring); } break; case G_TYPE_INT: n = grl_data_get_int (GRL_DATA (media), grl_key); string = g_strdup_printf ("%i", n); mex_content_set_metadata (content, mex_key, string); g_free (string); break; case G_TYPE_FLOAT: string = g_strdup_printf ("%f", grl_data_get_float (GRL_DATA (media), grl_key)); mex_content_set_metadata (content, mex_key, string); g_free (string); break; } }