56 Commits

Author SHA1 Message Date
Rafał Dzięgiel
ff054743e6 flatpak: Sync with Flathub 2025-07-19 15:39:42 +02:00
Rafał Dzięgiel
9432156aec flatpak: Sync with Flathub 2025-07-19 14:54:02 +02:00
Rafał Dzięgiel
47d3ebe693 Revert "flatpak: Skip libmpeg2 build for now"
This reverts commit f63e13ed39.

This workaround does not work, since GStreamer build manifest we use
as shared module from Flathub has explicitly enabled libmpeg2 support.
2025-07-19 12:56:13 +02:00
Rafał Dzięgiel
5f8270f0e8 clapper-app: Retain compatibility with older graphviz 2025-07-19 12:49:01 +02:00
Rafał Dzięgiel
54f059aaa3 clapper-app: Correct pipeline SVG size variable type 2025-07-19 12:03:52 +02:00
Rafał Dzięgiel
f63e13ed39 flatpak: Skip libmpeg2 build for now
Source URI is dead. Build Flatpak without it until
fixed or mirror is found, so our CI works again.
2025-07-19 11:59:22 +02:00
Rafał Dzięgiel
daadabba8d Merge pull request #567 from Rafostar/mpris-compat
MPRIS compat changes
2025-06-26 19:44:07 +02:00
Rafał Dzięgiel
225e665aff clapper: Deprecate MPRIS feature
It was ported to Clapper Enhancers repo as a plugin and applications
using old one should slowy move towards using new plugin instead
2025-06-26 19:37:23 +02:00
Rafał Dzięgiel
31564b568b clapper-app: Prefer MPRIS enhancer over feature object
First try to find and use new MPRIS enhancer plugin, then old MPRIS feature as fallback
2025-06-26 19:09:34 +02:00
Rafał Dzięgiel
1c376612b8 clapper: Do not skip on loading ported enhancers
With some workarounds, we can make enhancers that were ported from
features still be loaded and used within single process without
requirement for building Clapper with their old implementations disabled.

Since MPRIS enhancer already has workarounds in place, lets just remove
all code that was skipping its loading.
2025-06-26 19:06:02 +02:00
Rafał Dzięgiel
c4afd8bea1 Merge pull request #565 from Rafostar/timeline-improvements
Timeline improvements
2025-06-24 19:22:12 +02:00
Rafał Dzięgiel
266c588db9 clapper-gtk: seek-bar: Make custom markers colorful 2025-06-24 18:53:52 +02:00
Rafał Dzięgiel
1c0049ec2b clapper: Make timeline insert/remove work from any thread
Detect and auto switch thread to main if done from a different one.
With this, apps can still continue to implement thread switch and doing
multiple insertions/deletions within single main thread invoke or simply
call this function from a different thread for convenience.
2025-06-24 18:53:43 +02:00
Rafał Dzięgiel
7f326e6875 Merge pull request #564 from Rafostar/disallow-enhancers
clapper: Ability to allow/disallow enhancers
2025-06-24 18:51:52 +02:00
Rafał Dzięgiel
16430c4c66 clapper-app: Allow usage of all reactable enhancers
Enable and use all found enhancer plugins on the system
2025-06-22 13:36:09 +02:00
Rafał Dzięgiel
9f1102bafd clapper: Add ability to enable/disable creation of given enhancer
Allow apps to enable or disable given enhancer instances from being created.
Also as a safely measure, by default only enable enhancers that work
on-demand (extractables) and disable others (reactables).
2025-06-22 13:35:57 +02:00
Rafał Dzięgiel
7b4a19659b Merge pull request #561 from musicinmybrain/remote-only-fsf
Update LGPL-2.1-or-later notices and text for remote-only FSF
2025-06-19 12:43:50 +02:00
Benjamin A. Beasley
8fe46d315c Update LGPL-2.1-or-later license text for remote-only FSF
The Free Software Foundation is now remote-only, and no longer has a
street address. Updated license text is from
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt.
2025-06-14 11:35:04 -04:00
Benjamin A. Beasley
b5cc171803 Update LGPL-2.1-or-later license notices for remote-only FSF
The Free Software Foundation is now remote-only, and no longer has a
street address. Updated license notice text is from
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html#SEC4.
2025-06-14 11:35:02 -04:00
Rafał Dzięgiel
0f929014d5 clapper: reactable: Fix invalid assert check
Fix a copy-paste bug. There is no item to check in this function.
2025-06-09 20:57:45 +02:00
Rafał Dzięgiel
f3b120f451 Merge pull request #559 from Rafostar/reactable
clapper: Add "Reactable" interface
2025-06-09 20:52:15 +02:00
Rafał Dzięgiel
c354d31436 clapper: reactable: Inform which properties were updated 2025-06-09 20:21:39 +02:00
Rafał Dzięgiel
c0b360dc0f clapper-app: Add support for MPRIS enhancer 2025-06-09 18:37:40 +02:00
Rafał Dzięgiel
a6ca0b726c clapper: Implement reactables manager
An object for managing instances of reactable type of enhancers.

Based on/similar to features manager which along with Clapper
features objects gets deprecated in favour of reactables.
2025-06-09 18:36:06 +02:00
Rafał Dzięgiel
976bcc338f clapper: Add "Reactable" interface
An interface for creating enhancers that react to the
playback and/or events that should influence it.
2025-06-08 17:46:35 +02:00
Rafał Dzięgiel
6273446817 Merge pull request #558 from Rafostar/tagged
clapper: Add taglist to media items
2025-06-08 17:43:12 +02:00
Rafał Dzięgiel
72ab32d4ef shared: Do not print deprecations when compiling own code
We keep and use old functions in code for the compatibility reasons with
older API versions. Do not print warnings about them being deprecated when
compiling Clapper library that has and uses such function internally.
2025-06-08 17:17:23 +02:00
Rafał Dzięgiel
e9d0d8f345 clapper: Add taglist to media items
Allow apps to read and/or populate initial taglist within media item.
Apps might care about other tags that Clapper application does not,
so this single property allows them to read whatever tag they might need.
2025-06-08 17:17:13 +02:00
Rafał Dzięgiel
0b8d359844 meson: Improve GIR init section
Init with disabled registry and remove it only for clapper-gtk where its not needed.
2025-06-08 16:57:04 +02:00
Rafał Dzięgiel
4a93bea203 Revert "meson: Remove GIR init section"
This reverts commit b05f0f2b30.
2025-06-08 16:56:57 +02:00
Rafał Dzięgiel
5e2c1a8e30 flatpak: Sync with Flathub 2025-06-08 16:40:47 +02:00
Rafał Dzięgiel
72c8e4ab84 clapper: Fix missing pspec ref when copying proxy
Newly created enhancer proxies hold param specs with a reference
on each and unref them when finalized. For this reason, copied
proxy objects needs to ref pspecs from source, otherwise it would
do an unref without holding a reference on object during destruction.
2025-06-02 20:10:53 +02:00
Rafał Dzięgiel
db61b9c773 gst-plugin: Avoid main thread invoke when used with "ClapperGtkVideo"
This thread invoke is done mainly to support testing with gst-launch-1.0,
otherwise no need when used with "ClapperGtkVideo". We can avoid doing this,
by checking whether this type was already registered in which case it means
that "ClapperGtkVideo" widget is used within GTK application and registered
before sink starts processing data.

In case of "ClapperGtkVideo" we might run into situation where these two threads
are stuck waiting for each other to be idle. This change works around this issue.

Fixes #555
2025-05-25 15:06:32 +02:00
Rafał Dzięgiel
682ad6c3c8 clapper: Allow peeking in Vala
Vala does better job at handling objects without increased reference than
interpreted languages, so its safe to expose list "peek" functions to it.
2025-05-23 16:15:11 +02:00
Rafał Dzięgiel
749796a12f clapper: doc: Update enhancer proxy docs 2025-05-23 16:11:46 +02:00
Rafał Dzięgiel
c557c11e86 clapper: Check enhancer config existence before applying it
Fixes crash due to trying to apply config for an enhancer while
there are no settings in this enhancer to be applied
2025-05-23 08:12:09 +02:00
Rafał Dzięgiel
a2f67a9bc0 Merge pull request #553 from Rafostar/harvest-caching
clapper: Implement harvest caching
2025-05-21 19:49:26 +02:00
Rafał Dzięgiel
ddc0a4d8f9 clapper: doc: Fix missing transfer annotation 2025-05-21 19:42:27 +02:00
Rafał Dzięgiel
92e3e686db clapper: doc: Fix adaptive-start-bitrate description 2025-05-21 18:36:19 +02:00
Rafał Dzięgiel
9fd87dbbb9 clapper: Rename enhancersrc -> extractablesrc
Since this element only uses enhancers of "extractable" type
2025-05-21 17:42:15 +02:00
Rafał Dzięgiel
ca15f4760a clapper: Cleanup cached harvests periodically 2025-05-21 17:42:08 +02:00
Rafał Dzięgiel
6ddb53252a clapper: Implement harvest caching
Using recently added local cache functionality, store harvests
that have expiration date. With this, next time the same URI is
selected for playback we can read it from cache, skipping loading
of any enhancer plugins and doing network requests.

This also works nicely with Clapper discoverer feature.
Making queued items be fetched and cached ahead of playback.
2025-05-20 18:56:28 +02:00
Rafał Dzięgiel
b30d53d8ce clapper: Add ability to set harvest expiration date 2025-05-20 17:18:20 +02:00
Rafał Dzięgiel
1527873bcc clapper: Fix missing unref of mapped file
In case where enhancer plugin version did not match
an unref was missing of read file.
2025-05-19 20:42:32 +02:00
Rafał Dzięgiel
e23f2acb3e meson: Remove unused "config.h" variable 2025-05-18 13:24:51 +02:00
Rafał Dzięgiel
1dfcb218ac meson: Bump min required libadwaita version
Since URI dialog was ported to use AdwAlertDialog,
a minimal Adw version has to be changed to 1.5.
2025-05-17 18:55:31 +02:00
Rafał Dzięgiel
b05f0f2b30 meson: Remove GIR init section
Clapper GStreamer plugin is not part of GObject Introspection,
so no need to init GStreamer when compiling bindings.
2025-05-17 18:20:33 +02:00
Rafał Dzięgiel
dad0d46196 Merge pull request #546 from Rafostar/configurable-enhancers
Make Clapper Enhancers configurable
2025-05-16 18:30:05 +02:00
Rafał Dzięgiel
e34f729f62 clapper: Move basic functions into separate files
Avoid including whole "clapper.h" internally.
Faster recompilation when changes are done.
2025-05-16 17:02:29 +02:00
Rafał Dzięgiel
a97e7d1a96 clapper: Avoid using hardcoded API name in enhancers loader on win32 2025-05-16 17:02:10 +02:00
Rafał Dzięgiel
c6c4fe309b clapper: Implement data cache
Add data cache functions and use them to store enhancer data into local cache file.
This way we can restore all properties and interfaces used in enhancer without
creating its instance. This avoids loading interpreters like Python at init time
making startup a lot faster.
2025-05-16 17:02:07 +02:00
Rafał Dzięgiel
3ef6e9694a clapper-app: Add enhancers to preferences window
Browse, read info and configure Clapper enhancer plugins from preferences window
2025-05-16 17:02:03 +02:00
Rafał Dzięgiel
d951be7a56 clapper: Remove usage of "X-Interfaces" in enhancer data
At this point its sole remaining place where its used is one debug message.
For this reason remove it. With this, enhancer plugin files no longer need to
have this in them unless they want to keep Clapper 0.8 compat (otherwise unused).
2025-05-16 17:01:59 +02:00
Rafał Dzięgiel
98fdd7c58b clapper: Introduce "ClapperEnhancerProxy" objects
Add support for configuring Clapper Enhancers. In order to do that,
introduce enhancer proxy object that act as intermediary between
player and enhancer plugin.

We cannot give direct access to enhancer instances from code, since
they are managed (created and destroyed) by player as/when needed.
Also due to some interpreted languages not working with multiple
threads. Instead, give proxy objects that will store each enhancer
configuration to be applied when an enhancer instance is created.

With this, implementations also gain ability to browse available
enhancers, see what they support and change their properties.

Enhancers are now also assigned to player, instead of being only global.
This allows to configure them separately on a per player instance basis.

Writing configurable enhancers is super easy too, as all plugin has
to do is install standard GParamSpec properties to its class with a
corresponding gschema file (for global props only) and its done.
2025-05-16 17:01:51 +02:00
Rafał Dzięgiel
147d94088c Merge pull request #548 from ximion/master
Use modern appstream, instead of appstream-util for validation
2025-05-03 15:42:28 +02:00
Matthias Klumpp
c7790d9f7b Use modern appstream, instead of appstream-util for validation 2025-05-03 01:56:53 +02:00
185 changed files with 6972 additions and 1574 deletions

View File

@@ -2,7 +2,7 @@
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -158,7 +158,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -267,7 +267,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -422,7 +422,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
@@ -484,9 +484,7 @@ convey the exclusion of warranty; and each file should have at least the
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
License along with this library; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -495,10 +493,9 @@ school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
<signature of Moe Ghoul>, 1 April 1990
Moe Ghoul, President of Vice
That's all there is to it!

View File

@@ -11,7 +11,7 @@ project('clapper', 'c',
glib_req = '>= 2.76.0'
gst_req = '>= 1.24.0'
gtk4_req = '>= 4.10.0'
adw_req = '>= 1.4.0'
adw_req = '>= 1.5.0'
clapper_version = meson.project_version().split('-')[0]
version_array = clapper_version.split('.')

View File

@@ -72,6 +72,43 @@ _open_subtitles_cb (GtkFileDialog *dialog, GAsyncResult *result, ClapperMediaIte
gst_object_unref (item); // Borrowed reference
}
static void
_on_select_file_dir_finish (GFile *file, AdwActionRow *action_row, GError *error)
{
if (G_LIKELY (error == NULL)) {
gchar *path = g_file_get_path (file);
adw_action_row_set_subtitle (action_row, path);
g_free (path);
} else {
if (error->domain != GTK_DIALOG_ERROR || error->code != GTK_DIALOG_ERROR_DISMISSED) {
g_printerr ("Error: %s\n",
(error->message) ? error->message : "Could not open file dialog");
}
g_error_free (error);
}
g_clear_object (&file);
g_object_unref (action_row); // Borrowed reference
}
static void
_select_file_cb (GtkFileDialog *dialog, GAsyncResult *result, AdwActionRow *action_row)
{
GError *error = NULL;
GFile *file = gtk_file_dialog_open_finish (dialog, result, &error);
_on_select_file_dir_finish (file, action_row, error);
}
static void
_select_dir_cb (GtkFileDialog *dialog, GAsyncResult *result, AdwActionRow *action_row)
{
GError *error = NULL;
GFile *file = gtk_file_dialog_select_folder_finish (dialog, result, &error);
_on_select_file_dir_finish (file, action_row, error);
}
static void
_dialog_add_mime_types (GtkFileDialog *dialog, const gchar *filter_name,
const gchar *const *mime_types)
@@ -144,3 +181,35 @@ clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaIte
g_object_unref (dialog);
}
void
clapper_app_file_dialog_select_prefs_file (GtkApplication *gtk_app, AdwActionRow *action_row)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Select File");
gtk_file_dialog_open (dialog, window, NULL,
(GAsyncReadyCallback) _select_file_cb,
g_object_ref (action_row));
g_object_unref (dialog);
}
void
clapper_app_file_dialog_select_prefs_dir (GtkApplication *gtk_app, AdwActionRow *action_row)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Select Folder");
gtk_file_dialog_select_folder (dialog, window, NULL,
(GAsyncReadyCallback) _select_dir_cb,
g_object_ref (action_row));
g_object_unref (dialog);
}

View File

@@ -19,6 +19,7 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <adwaita.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
@@ -29,4 +30,10 @@ void clapper_app_file_dialog_open_files (GtkApplication *gtk_app);
G_GNUC_INTERNAL
void clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaItem *item);
G_GNUC_INTERNAL
void clapper_app_file_dialog_select_prefs_file (GtkApplication *gtk_app, AdwActionRow *action_row);
G_GNUC_INTERNAL
void clapper_app_file_dialog_select_prefs_dir (GtkApplication *gtk_app, AdwActionRow *action_row);
G_END_DECLS

View File

@@ -23,6 +23,7 @@
#include "clapper-app-preferences-window.h"
#include "clapper-app-application.h"
#include "clapper-app-file-dialog.h"
#include "clapper-app-utils.h"
#define GST_CAT_DEFAULT clapper_app_preferences_window_debug
@@ -41,6 +42,14 @@ struct _ClapperAppPreferencesWindow
AdwSpinRow *subtitle_offset_spin_row;
GtkFontDialogButton *font_dialog_button;
GtkStack *enhancers_stack;
GtkWidget *browse_enhancers_page;
GtkWidget *no_enhancers_page;
AdwNavigationPage *enhancers_subpage;
AdwComboRow *enhancers_combo_row;
AdwPreferencesGroup *enhancer_config_group;
AdwNavigationPage *plugins_subpage;
AdwComboRow *plugins_combo_row;
AdwComboRow *features_combo_row;
@@ -48,6 +57,8 @@ struct _ClapperAppPreferencesWindow
GSettings *settings;
GList *enhancer_pspec_rows;
GList *features;
GtkStringList *plugins_list;
@@ -67,6 +78,19 @@ typedef struct
gboolean updated;
} ClapperAppPreferencesIterRanksData;
typedef struct
{
GSettings *settings;
const gchar *key;
} ClapperAppPreferencesResetData;
typedef struct
{
GSettings *settings;
GParamSpec *pspec;
guint flag;
} ClapperAppPreferencesFlagMapData;
enum
{
PROP_0,
@@ -76,6 +100,347 @@ enum
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
_flag_map_data_free (ClapperAppPreferencesFlagMapData *data)
{
GST_TRACE ("Destroying flag map data: %p", data);
g_object_unref (data->settings);
g_free (data);
}
static void
_reset_button_closure (ClapperAppPreferencesResetData *data, GClosure *closure)
{
GST_TRACE ("Destroying reset button data: %p", data);
g_object_unref (data->settings);
g_free (data);
}
static void
_reset_button_clicked_cb (GtkButton *button, ClapperAppPreferencesResetData *data)
{
g_settings_reset (data->settings, data->key);
}
static void
file_selection_row_activated_cb (AdwActionRow *action_row, GParamSpec *pspec)
{
GtkApplication *gtk_app;
GtkWidget *window;
if (!(window = gtk_widget_get_ancestor (GTK_WIDGET (action_row), GTK_TYPE_WINDOW))) {
GST_ERROR ("Could not get a hold of parent window");
return;
}
gtk_app = gtk_window_get_application (GTK_WINDOW (window));
if (pspec->flags & CLAPPER_ENHANCER_PARAM_FILEPATH)
clapper_app_file_dialog_select_prefs_file (gtk_app, action_row);
else
clapper_app_file_dialog_select_prefs_dir (gtk_app, action_row);
}
static gboolean
_get_enum_mapping (GValue *value, GVariant *variant, GParamSpec *pspec)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
const gchar *selected_str = g_variant_get_string (variant, NULL);
guint i, selected = 0;
for (i = 0; i < enum_class->n_values; ++i) {
if (g_strcmp0 (selected_str, enum_class->values[i].value_nick) == 0) {
selected = i;
break;
}
}
g_value_set_uint (value, selected);
return TRUE;
}
static GVariant *
_set_enum_mapping (GValue *value, GVariantType *exp_type, GParamSpec *pspec)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
guint selected = g_value_get_uint (value);
if (G_UNLIKELY (selected == GTK_INVALID_LIST_POSITION))
selected = 0;
return g_variant_new_string (enum_class->values[selected].value_nick);
}
static gboolean
_get_flag_mapping (GValue *value, GVariant *variant, ClapperAppPreferencesFlagMapData *data)
{
guint flags = g_settings_get_flags (data->settings, data->pspec->name);
g_value_set_boolean (value, (flags & data->flag));
return TRUE;
}
static GVariant *
_set_flag_mapping (GValue *value, GVariantType *exp_type, ClapperAppPreferencesFlagMapData *data)
{
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (data->pspec->value_type));
GStrvBuilder *builder;
GVariant *variant;
gchar **strv;
gboolean active = g_value_get_boolean (value);
guint i, flags = g_settings_get_flags (data->settings, data->pspec->name);
if (active)
flags |= data->flag;
else
flags &= ~(data->flag);
builder = g_strv_builder_new ();
for (i = 0; i < flags_class->n_values; ++i) {
if (flags & flags_class->values[i].value)
g_strv_builder_add (builder, flags_class->values[i].value_nick);
}
strv = g_strv_builder_end (builder);
g_strv_builder_unref (builder);
variant = g_variant_new_strv ((const gchar *const *) strv, -1);
g_strfreev (strv);
return variant;
}
static gboolean
_add_enhancer_config_row (ClapperAppPreferencesWindow *self, GParamSpec *pspec,
GSettings *enhancer_settings)
{
GtkWidget *row = NULL, *reset_button;
ClapperAppPreferencesResetData *reset_data;
const gchar *bind_prop = NULL;
gboolean is_enum = FALSE, is_flags = FALSE;
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:{
row = adw_switch_row_new ();
break;
}
case G_TYPE_INT:{
GParamSpecInt *p = (GParamSpecInt *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
break;
}
case G_TYPE_UINT:{
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
break;
}
case G_TYPE_DOUBLE:{
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 0.25);
break;
}
case G_TYPE_STRING:{
if (pspec->flags & (CLAPPER_ENHANCER_PARAM_FILEPATH | CLAPPER_ENHANCER_PARAM_DIRPATH)) {
GtkWidget *image;
image = gtk_image_new_from_icon_name ("document-open-symbolic");
gtk_widget_set_margin_end (image, 10); // matches other rows
row = adw_action_row_new ();
adw_action_row_add_suffix (ADW_ACTION_ROW (row), image);
adw_action_row_set_activatable_widget (ADW_ACTION_ROW (row), image);
g_signal_connect (row, "activated",
G_CALLBACK (file_selection_row_activated_cb), pspec);
} else {
row = adw_entry_row_new ();
}
break;
}
default:{
if ((is_enum = G_IS_PARAM_SPEC_ENUM (pspec))) {
GtkExpression *expression;
AdwEnumListModel *enum_model;
row = adw_combo_row_new ();
expression = gtk_property_expression_new (ADW_TYPE_ENUM_LIST_ITEM, NULL, "nick");
adw_combo_row_set_expression (ADW_COMBO_ROW (row), expression);
enum_model = adw_enum_list_model_new (pspec->value_type);
adw_combo_row_set_model (ADW_COMBO_ROW (row), G_LIST_MODEL (enum_model));
gtk_expression_unref (expression);
g_object_unref (enum_model);
break;
} else if ((is_flags = G_IS_PARAM_SPEC_FLAGS (pspec))) {
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
guint i;
row = adw_expander_row_new ();
for (i = 0; i < flags_class->n_values; ++i) {
GtkWidget *flag_row = adw_switch_row_new ();
ClapperAppPreferencesFlagMapData *fm_data;
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (flag_row),
flags_class->values[i].value_nick);
fm_data = g_new (ClapperAppPreferencesFlagMapData, 1);
fm_data->settings = g_object_ref (enhancer_settings);
fm_data->pspec = pspec;
fm_data->flag = flags_class->values[i].value;
GST_TRACE ("Created flag map data: %p", fm_data);
g_settings_bind_with_mapping (enhancer_settings, pspec->name, flag_row,
"active", G_SETTINGS_BIND_DEFAULT,
(GSettingsBindGetMapping) _get_flag_mapping,
(GSettingsBindSetMapping) _set_flag_mapping,
fm_data, (GDestroyNotify) _flag_map_data_free);
adw_expander_row_add_row (ADW_EXPANDER_ROW (row), flag_row);
}
break;
}
g_warning ("Unsupported enhancer \"%s\" property type: %s",
pspec->name, g_type_name (pspec->value_type));
return FALSE;
}
}
reset_button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
gtk_widget_set_tooltip_text (reset_button, _("Restore default"));
gtk_widget_set_halign (reset_button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (reset_button, GTK_ALIGN_CENTER);
gtk_widget_add_css_class (reset_button, "circular");
gtk_widget_set_tooltip_text (row, g_param_spec_get_blurb (pspec));
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), g_param_spec_get_nick (pspec));
if (ADW_IS_SWITCH_ROW (row)) {
bind_prop = "active";
} else if (ADW_IS_SPIN_ROW (row)) {
bind_prop = "value";
adw_spin_row_set_numeric (ADW_SPIN_ROW (row), TRUE);
} else if (ADW_IS_ENTRY_ROW (row)) {
bind_prop = "text";
} else if (ADW_IS_COMBO_ROW (row)) {
bind_prop = "selected";
} else if (ADW_IS_ACTION_ROW (row)) {
bind_prop = "subtitle";
} else if (!is_flags) { // In case of flags we bind individual widgets
g_assert_not_reached ();
return FALSE;
}
if (ADW_IS_ENTRY_ROW (row))
adw_entry_row_add_prefix (ADW_ENTRY_ROW (row), reset_button);
else if (ADW_IS_ACTION_ROW (row))
adw_action_row_add_prefix (ADW_ACTION_ROW (row), reset_button);
else if (ADW_IS_EXPANDER_ROW (row))
adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), reset_button);
if (is_enum) {
g_settings_bind_with_mapping (enhancer_settings, pspec->name, row,
bind_prop, G_SETTINGS_BIND_DEFAULT,
(GSettingsBindGetMapping) _get_enum_mapping,
(GSettingsBindSetMapping) _set_enum_mapping,
pspec, NULL);
} else if (!is_flags) {
g_settings_bind (enhancer_settings, pspec->name, row,
bind_prop, G_SETTINGS_BIND_DEFAULT);
}
reset_data = g_new (ClapperAppPreferencesResetData, 1);
reset_data->settings = g_object_ref (enhancer_settings);
reset_data->key = pspec->name;
GST_TRACE ("Created reset button data: %p", reset_data);
g_signal_connect_data (reset_button, "clicked",
G_CALLBACK (_reset_button_clicked_cb), reset_data,
(GClosureNotify) _reset_button_closure, G_CONNECT_DEFAULT);
adw_preferences_group_add (self->enhancer_config_group, row);
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
return TRUE;
}
static void
selected_enhancer_changed_cb (AdwComboRow *combo_row,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppPreferencesWindow *self)
{
guint selected = adw_combo_row_get_selected (combo_row);
/* Remove old rows */
if (self->enhancer_pspec_rows) {
GList *el;
for (el = self->enhancer_pspec_rows; el; el = g_list_next (el))
adw_preferences_group_remove (self->enhancer_config_group, GTK_WIDGET (el->data));
g_clear_list (&self->enhancer_pspec_rows, NULL);
}
/* Add new rows */
if (selected != GTK_INVALID_LIST_POSITION) {
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, selected);
GParamSpec **pspecs;
guint n_pspecs;
gboolean has_props = FALSE;
if ((pspecs = clapper_enhancer_proxy_get_target_properties (proxy, &n_pspecs))) {
GSettings *enhancer_settings = NULL;
guint i;
for (i = 0; i < n_pspecs; ++i) {
if (pspecs[i]->flags & CLAPPER_ENHANCER_PARAM_GLOBAL) {
if (!enhancer_settings)
enhancer_settings = clapper_enhancer_proxy_get_settings (proxy);
if (enhancer_settings)
has_props |= _add_enhancer_config_row (self, pspecs[i], enhancer_settings);
}
}
g_clear_object (&enhancer_settings);
}
if (!has_props) {
GtkWidget *row = adw_action_row_new ();
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), _("No configurable properties"));
adw_preferences_group_add (self->enhancer_config_group, row);
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
}
}
}
static void
enhancers_config_activated_cb (AdwActionRow *action_row, ClapperAppPreferencesWindow *self)
{
/* If no model set yet */
if (!adw_combo_row_get_model (self->enhancers_combo_row)) {
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
adw_combo_row_set_model (self->enhancers_combo_row, G_LIST_MODEL (proxies));
adw_combo_row_set_selected (self->enhancers_combo_row, GTK_INVALID_LIST_POSITION);
GST_DEBUG ("Populated names combo row in enhancers subpage");
if (clapper_enhancer_proxy_list_get_n_proxies (proxies) > 0)
gtk_stack_set_visible_child (self->enhancers_stack, self->browse_enhancers_page);
else
gtk_stack_set_visible_child (self->enhancers_stack, self->no_enhancers_page);
}
adw_preferences_window_push_subpage (ADW_PREFERENCES_WINDOW (self), self->enhancers_subpage);
}
/* Sort by plugin name and if the same, sort by element name */
static gint
_compare_plugins_cb (gconstpointer ptr_a, gconstpointer ptr_b)
@@ -301,6 +666,12 @@ _make_plugin_features_string_list (ClapperAppPreferencesWindow *self, const gcha
return features_list;
}
static gboolean
list_has_selection_closure (ClapperAppPreferencesWindow *self, guint selected)
{
return (selected != GTK_INVALID_LIST_POSITION);
}
static GtkStringList *
ranking_features_model_closure (ClapperAppPreferencesWindow *self, GtkStringObject *string_obj)
{
@@ -543,6 +914,7 @@ clapper_app_preferences_window_finalize (GObject *object)
g_object_unref (self->settings);
g_clear_list (&self->enhancer_pspec_rows, NULL);
g_clear_object (&self->plugins_list);
if (self->features)
@@ -598,6 +970,14 @@ clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *kla
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, subtitle_offset_spin_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, font_dialog_button);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_stack);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, browse_enhancers_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, no_enhancers_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_subpage);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancer_config_group);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_subpage);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, features_combo_row);
@@ -605,9 +985,13 @@ clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *kla
gtk_widget_class_bind_template_callback (widget_class, seek_method_name_closure);
gtk_widget_class_bind_template_callback (widget_class, enhancers_config_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, selected_enhancer_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_unrealize_cb);
gtk_widget_class_bind_template_callback (widget_class, list_has_selection_closure);
gtk_widget_class_bind_template_callback (widget_class, ranking_features_model_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_sensitive_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_clicked_cb);

View File

@@ -582,7 +582,7 @@ _create_pipeline_svg_file_in_thread (GTask *task, GObject *source G_GNUC_UNUSED,
GVC_t *gvc;
gchar *path, *template = NULL, *dot_data = NULL, *img_data = NULL;
gint fd;
guint size = 0;
gsize size = 0;
if (!(tmp_subdir = _create_tmp_subdir ("pipelines", cancellable, &error)))
goto finish;
@@ -610,7 +610,16 @@ _create_pipeline_svg_file_in_thread (GTask *task, GObject *source G_GNUC_UNUSED,
gvc = gvContext ();
gvLayout (gvc, graph, "dot");
#ifdef HAVE_GVC_13
gvRenderData (gvc, graph, "svg", &img_data, &size);
#else
{
guint tmp_size = 0; // Temporary uint to satisfy older API
gvRenderData (gvc, graph, "svg", &img_data, &tmp_size);
size = tmp_size;
}
#endif
agclose (graph);
gvFreeContext (gvc);

View File

@@ -86,9 +86,7 @@ typedef struct
gint64 last_tick;
} ClapperAppWindowResizeData;
#if CLAPPER_HAVE_MPRIS
static guint16 instance_count = 0;
#endif
static inline GQuark
clapper_app_window_extra_options_get_quark (void)
@@ -1239,6 +1237,8 @@ clapper_app_window_constructed (GObject *object)
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (object);
ClapperPlayer *player = clapper_app_window_get_player (self);
ClapperQueue *queue = clapper_player_get_queue (player);
ClapperEnhancerProxyList *proxies = clapper_player_get_enhancer_proxies (player);
ClapperEnhancerProxy *proxy;
ClapperGtkExtraMenuButton *button;
AdwStyleManager *manager;
@@ -1252,22 +1252,30 @@ clapper_app_window_constructed (GObject *object)
#if (CLAPPER_HAVE_MPRIS || CLAPPER_HAVE_SERVER || CLAPPER_HAVE_DISCOVERER)
ClapperFeature *feature = NULL;
#endif
#if CLAPPER_HAVE_MPRIS
gchar mpris_name[45];
g_snprintf (mpris_name, sizeof (mpris_name),
"org.mpris.MediaPlayer2.Clapper.instance%" G_GUINT16_FORMAT, instance_count++);
#endif
self->settings = g_settings_new (CLAPPER_APP_ID);
self->last_volume = PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume"));
if ((proxy = clapper_enhancer_proxy_list_get_proxy_by_module (proxies, "clapper-mpris"))) {
clapper_enhancer_proxy_set_locally (proxy,
"own-name", mpris_name,
"identity", CLAPPER_APP_NAME,
"desktop-entry", CLAPPER_APP_ID,
"queue-controllable", TRUE, NULL);
gst_object_unref (proxy);
} else {
#if CLAPPER_HAVE_MPRIS
feature = CLAPPER_FEATURE (clapper_mpris_new (
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);
clapper_player_add_feature (player, feature);
gst_object_unref (feature);
feature = CLAPPER_FEATURE (clapper_mpris_new (
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);
clapper_player_add_feature (player, feature);
gst_object_unref (feature);
#endif
}
#if CLAPPER_HAVE_SERVER
feature = CLAPPER_FEATURE (clapper_server_new ());

View File

@@ -21,10 +21,10 @@ window.info .subcontent streamlist preferencesgroup {
window.preferences .subcontent {
margin: 16px;
}
window.preferences .pluginssubpage .subcontent popover {
window.preferences .configsubpage .subcontent popover {
min-width: 264px;
}
window.preferences .pluginssubpage .subcontent button.pill {
window.preferences .configsubpage .subcontent button.pill {
margin-top: 8px;
margin-bottom: 8px;
}

View File

@@ -1,10 +1,11 @@
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
appstream_cli = find_program('appstreamcli', required: false)
if appstream_cli.found()
test('Validate appstream file',
appstream_util,
appstream_cli,
args: [
'validate-relax',
'--nonet',
'validate',
'--no-net',
'--explain',
join_paths(meson.current_source_dir(), 'metainfo', 'com.github.rafostar.Clapper.metainfo.xml'),
]
)

View File

@@ -31,8 +31,10 @@
gint
main (gint argc, gchar **argv)
{
const gchar *clapper_ldir;
GApplication *application;
ClapperEnhancerProxyList *proxies;
const gchar *clapper_ldir;
guint i, n_proxies;
gint status;
#ifdef G_OS_WIN32
@@ -64,6 +66,15 @@ main (gint argc, gchar **argv)
resolution = clapper_app_utils_win_hi_res_clock_start ();
#endif
proxies = clapper_get_global_enhancer_proxies ();
n_proxies = clapper_enhancer_proxy_list_get_n_proxies (proxies);
/* Allow usage of all enhancers */
for (i = 0; i < n_proxies; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, i);
clapper_enhancer_proxy_set_target_creation_allowed (proxy, TRUE);
}
application = clapper_app_application_new ();
status = g_application_run (application, argc, argv);

View File

@@ -105,6 +105,9 @@ if not pp_option.disabled()
if cgraph_dep.found() and gvc_dep.found()
clapperapp_c_args += ['-DHAVE_GRAPHVIZ']
clapperapp_deps += [cgraph_dep, gvc_dep]
if gvc_dep.version().version_compare('>= 13.0.0')
clapperapp_c_args += ['-DHAVE_GVC_13']
endif
clapperapp_available_functionalities += 'pipeline-preview'
elif pp_option.enabled()
error('pipeline-preview option was enabled, but required dependencies were not found')

View File

@@ -136,6 +136,24 @@
<object class="AdwPreferencesPage">
<property name="title" translatable="yes">Tweaks</property>
<property name="icon-name">applications-engineering-symbolic</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="no">Clapper</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Enhancers</property>
<property name="subtitle" translatable="yes">Browse and configure properties of available enhancers</property>
<property name="activatable">true</property>
<signal name="activated" handler="enhancers_config_activated_cb"/>
<child>
<object class="GtkImage">
<property name="icon_name">go-next-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="no">GStreamer</property>
@@ -160,6 +178,124 @@
<class name="preferences"/>
</style>
</template>
<object class="AdwNavigationPage" id="enhancers_subpage">
<property name="title" translatable="yes">Clapper Enhancers</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"/>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<child>
<object class="AdwClamp">
<style>
<class name="subcontent"/>
</style>
<child>
<object class="GtkStack" id="enhancers_stack">
<child>
<object class="GtkBox" id="browse_enhancers_page">
<property name="orientation">vertical</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Available enhancers</property>
<property name="description" translatable="yes">Select an enhancer plugin to view its information and properties to configure.</property>
<child>
<object class="AdwComboRow" id="enhancers_combo_row">
<property name="title" translatable="yes">Enhancer</property>
<property name="enable-search">true</property>
<property name="expression">
<lookup type="ClapperEnhancerProxy" name="friendly-name"/>
</property>
<signal name="notify::selected" handler="selected_enhancer_changed_cb"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<binding name="visible">
<closure type="gboolean" function="list_has_selection_closure">
<lookup name="selected">enhancers_combo_row</lookup>
</closure>
</binding>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Information</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Module</property>
<binding name="subtitle">
<lookup name="module-name" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Description</property>
<binding name="subtitle">
<lookup name="description" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Version</property>
<binding name="subtitle">
<lookup name="version" type="ClapperEnhancerProxy">
<lookup name="selected-item">enhancers_combo_row</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="enhancer_config_group">
<property name="title" translatable="yes">Properties</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwStatusPage" id="no_enhancers_page">
<property name="vexpand">true</property>
<property name="hexpand">true</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="title" translatable="yes">No Clapper Enhancers Found</property>
<property name="description" translatable="yes">Install some to add more cool functionalities to the player!</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
<signal name="unrealize" handler="plugin_ranking_unrealize_cb"/>
<style>
<class name="configsubpage"/>
</style>
</object>
<object class="AdwNavigationPage" id="plugins_subpage">
<property name="title" translatable="yes">Plugin Ranking</property>
<property name="child">
@@ -240,7 +376,7 @@
</property>
<signal name="unrealize" handler="plugin_ranking_unrealize_cb"/>
<style>
<class name="pluginssubpage"/>
<class name="configsubpage"/>
</style>
</object>
</interface>

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include <clapper/clapper.h>

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-gtk-buffering-paintable-private.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include <clapper/clapper.h>

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
@@ -396,6 +395,47 @@ _update_duration_label (ClapperGtkSeekBar *self, gdouble duration)
gtk_adjustment_set_upper (adjustment, duration);
}
static gboolean
_find_marks_in_widget (GtkWidget *widget, GtkWidget **top_marks, GtkWidget **bottom_marks)
{
GtkWidget *child;
if (g_strcmp0 (gtk_widget_get_css_name (widget), "marks") == 0) {
if (gtk_widget_has_css_class (widget, "top"))
*top_marks = widget;
else if (gtk_widget_has_css_class (widget, "bottom"))
*bottom_marks = widget;
/* Its unexpected to have marks within marks,
* so do not iterate children of marks widget */
return (*top_marks && *bottom_marks);
}
child = gtk_widget_get_first_child (widget);
while (child != NULL) {
if (_find_marks_in_widget (child, top_marks, bottom_marks))
return TRUE;
child = gtk_widget_get_next_sibling (child);
}
return FALSE;
}
static gboolean
_find_last_mark_in_marks (GtkWidget *marks, GtkWidget **last_mark)
{
GtkWidget *widget = gtk_widget_get_last_child (marks);
if (widget && g_strcmp0 (gtk_widget_get_css_name (widget), "mark") == 0) {
*last_mark = widget;
return TRUE;
}
return FALSE;
}
static void
_update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
{
@@ -424,11 +464,43 @@ _update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
for (i = 0; i < n_markers; ++i) {
ClapperMarker *marker = clapper_timeline_get_marker (timeline, i);
ClapperMarkerType marker_type = clapper_marker_get_marker_type (marker);
gdouble start = clapper_marker_get_start (marker);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_TOP, NULL);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_BOTTOM, NULL);
if (marker_type >= CLAPPER_MARKER_TYPE_CUSTOM_1) {
GtkWidget *top_marks = NULL, *bottom_marks = NULL;
GtkWidget *top_mark = NULL, *bottom_mark = NULL;
if (_find_marks_in_widget (self->scale, &top_marks, &bottom_marks)
&& _find_last_mark_in_marks (top_marks, &top_mark)
&& _find_last_mark_in_marks (bottom_marks, &bottom_mark)) {
const gchar *custom_name;
switch (marker_type) {
case CLAPPER_MARKER_TYPE_CUSTOM_1:
custom_name = "custom1";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_2:
custom_name = "custom2";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_3:
custom_name = "custom3";
break;
default:
custom_name = NULL;
break;
}
if (G_LIKELY (custom_name != NULL)) {
gtk_widget_add_css_class (top_mark, custom_name);
gtk_widget_add_css_class (bottom_mark, custom_name);
}
}
}
gst_object_unref (marker);
}

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -129,6 +129,15 @@ clapper-gtk-seek-bar label {
margin-left: 2px;
margin-right: 2px;
}
clapper-gtk-seek-bar scale marks .custom1 indicator {
color: tomato;
}
clapper-gtk-seek-bar scale marks .custom2 indicator {
color: goldenrod;
}
clapper-gtk-seek-bar scale marks .custom3 indicator {
color: limegreen;
}
clapper-gtk-extra-menu-button popover .spinsidebutton {
min-width: 28px;

View File

@@ -176,7 +176,6 @@ if build_gir
clappergtk_enums,
],
extra_args: [
gir_init_section,
'--quiet',
'--warn-all',
'-DCLAPPER_GTK_COMPILATION',

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include <gio/gio.h>

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -1,5 +1,5 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -22,17 +21,23 @@
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include "clapper.h"
#include "clapper-basic-functions.h"
#include "clapper-cache-private.h"
#include "clapper-utils-private.h"
#include "clapper-playbin-bus-private.h"
#include "clapper-app-bus-private.h"
#include "clapper-features-bus-private.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-reactables-manager-private.h"
#include "gst/clapper-plugin-private.h"
#include "clapper-functionalities-availability.h"
#if CLAPPER_WITH_ENHANCERS_LOADER
#include "clapper-enhancers-loader-private.h"
#endif
static ClapperEnhancerProxyList *_proxies = NULL;
static gboolean is_initialized = FALSE;
static GMutex init_lock;
@@ -46,13 +51,17 @@ clapper_init_check_internal (int *argc, char **argv[])
gst_pb_utils_init ();
clapper_cache_initialize ();
clapper_utils_initialize ();
clapper_playbin_bus_initialize ();
clapper_app_bus_initialize ();
clapper_features_bus_initialize ();
clapper_reactables_manager_initialize ();
_proxies = clapper_enhancer_proxy_list_new_named ("global-proxy-list");
#if CLAPPER_WITH_ENHANCERS_LOADER
clapper_enhancers_loader_initialize ();
clapper_enhancers_loader_initialize (_proxies);
#endif
gst_plugin_register_static (
@@ -149,18 +158,75 @@ clapper_init_check (int *argc, char **argv[])
* Returns: whether a plausible enhancer was found.
*
* Since: 0.8
*
* Deprecated: 0.10: Use list of enhancer proxies from [func@Clapper.get_global_enhancer_proxies] or
* [property@Clapper.Player:enhancer-proxies] and check if any proxy matches your search criteria.
*/
gboolean
clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name)
{
gboolean success = FALSE;
gboolean is_https;
guint i, n_proxies;
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), FALSE);
g_return_val_if_fail (scheme != NULL, FALSE);
#if CLAPPER_WITH_ENHANCERS_LOADER
success = clapper_enhancers_loader_check (iface_type, scheme, host, name);
#endif
if (host) {
/* Strip common subdomains, so plugins do not
* have to list all combinations */
if (g_str_has_prefix (host, "www."))
host += 4;
else if (g_str_has_prefix (host, "m."))
host += 2;
}
return success;
/* Whether "http(s)" scheme is used */
is_https = (g_str_has_prefix (scheme, "http")
&& (scheme[4] == '\0' || (scheme[4] == 's' && scheme[5] == '\0')));
if (!host && is_https)
return FALSE;
n_proxies = clapper_enhancer_proxy_list_get_n_proxies (_proxies);
for (i = 0; i < n_proxies; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (_proxies, i);
if (clapper_enhancer_proxy_target_has_interface (proxy, iface_type)
&& clapper_enhancer_proxy_extra_data_lists_value (proxy, "X-Schemes", scheme)
&& (!is_https || clapper_enhancer_proxy_extra_data_lists_value (proxy, "X-Hosts", host))) {
if (name)
*name = clapper_enhancer_proxy_get_friendly_name (proxy);
return TRUE;
}
}
return FALSE;
}
/**
* clapper_get_global_enhancer_proxies:
*
* Get a list of available enhancers in the form of [class@Clapper.EnhancerProxy] objects.
*
* This returns a global list of enhancer proxy objects. You can use it to inspect
* available enhancers without creating a new player instance.
*
* Remember to initialize Clapper library before using this function.
*
* Only enhancer properties with [flags@Clapper.EnhancerParamFlags.GLOBAL] flag can be
* set on proxies in this list. These are meant to be set ONLY by users, not applications
* as they carry over to all player instances (possibly including other apps). Applications
* should instead be changing properties with [flags@Clapper.EnhancerParamFlags.LOCAL] flag
* set from individual proxy lists from [property@Clapper.Player:enhancer-proxies] which
* will affect only that single player instance given list belongs to.
*
* Returns: (transfer none): a global #ClapperEnhancerProxyList of enhancer proxies.
*
* Since: 0.10
*/
ClapperEnhancerProxyList *
clapper_get_global_enhancer_proxies (void)
{
return _proxies;
}

View File

@@ -0,0 +1,45 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enhancer-proxy-list.h>
G_BEGIN_DECLS
CLAPPER_API
void clapper_init (int *argc, char **argv[]);
CLAPPER_API
gboolean clapper_init_check (int *argc, char **argv[]);
CLAPPER_DEPRECATED
gboolean clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
CLAPPER_API
ClapperEnhancerProxyList * clapper_get_global_enhancer_proxies (void);
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -0,0 +1,107 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_cache_initialize (void);
G_GNUC_INTERNAL
gboolean clapper_cache_is_disabled (void);
G_GNUC_INTERNAL
GMappedFile * clapper_cache_open (const gchar *filename, const gchar **data, GError **error);
G_GNUC_INTERNAL
gboolean clapper_cache_read_boolean (const gchar **data);
G_GNUC_INTERNAL
gint clapper_cache_read_int (const gchar **data);
G_GNUC_INTERNAL
guint clapper_cache_read_uint (const gchar **data);
G_GNUC_INTERNAL
gint64 clapper_cache_read_int64 (const gchar **data);
G_GNUC_INTERNAL
gdouble clapper_cache_read_double (const gchar **data);
G_GNUC_INTERNAL
const gchar * clapper_cache_read_string (const gchar **data);
G_GNUC_INTERNAL
const guint8 * clapper_cache_read_data (const gchar **data, gsize *size);
G_GNUC_INTERNAL
GType clapper_cache_read_enum (const gchar **data);
G_GNUC_INTERNAL
GType clapper_cache_read_flags (const gchar **data);
G_GNUC_INTERNAL
GType clapper_cache_read_iface (const gchar **data);
G_GNUC_INTERNAL
GParamSpec * clapper_cache_read_pspec (const gchar **data);
G_GNUC_INTERNAL
GByteArray * clapper_cache_create (void);
G_GNUC_INTERNAL
void clapper_cache_store_boolean (GByteArray *bytes, gboolean val);
G_GNUC_INTERNAL
void clapper_cache_store_int (GByteArray *bytes, gint val);
G_GNUC_INTERNAL
void clapper_cache_store_uint (GByteArray *bytes, guint val);
G_GNUC_INTERNAL
void clapper_cache_store_int64 (GByteArray *bytes, gint64 val);
G_GNUC_INTERNAL
void clapper_cache_store_double (GByteArray *bytes, gdouble val);
G_GNUC_INTERNAL
void clapper_cache_store_string (GByteArray *bytes, const gchar *val);
G_GNUC_INTERNAL
void clapper_cache_store_data (GByteArray *bytes, const guint8 *val, gsize val_size);
G_GNUC_INTERNAL
void clapper_cache_store_enum (GByteArray *bytes, GType enum_type);
G_GNUC_INTERNAL
void clapper_cache_store_flags (GByteArray *bytes, GType flags_type);
G_GNUC_INTERNAL
gboolean clapper_cache_store_iface (GByteArray *bytes, GType iface);
G_GNUC_INTERNAL
gboolean clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec);
G_GNUC_INTERNAL
gboolean clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error);
G_END_DECLS

View File

@@ -0,0 +1,541 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-cache-private.h"
#include "clapper-version.h"
#include "clapper-extractable.h"
#include "clapper-reactable.h"
#define CLAPPER_CACHE_HEADER "CLAPPER"
typedef enum
{
CLAPPER_CACHE_IFACE_EXTRACTABLE = 1,
CLAPPER_CACHE_IFACE_REACTABLE,
} ClapperCacheIfaces;
static GArray *enum_registry = NULL;
static GArray *flags_registry = NULL;
static gboolean cache_disabled = FALSE;
void
clapper_cache_initialize (void)
{
const gchar *env = g_getenv ("CLAPPER_DISABLE_CACHE");
if (G_LIKELY (!env || !g_str_has_prefix (env, "1"))) {
enum_registry = g_array_new (FALSE, TRUE, sizeof (GEnumValue *));
flags_registry = g_array_new (FALSE, TRUE, sizeof (GFlagsValue *));
} else {
cache_disabled = TRUE;
}
}
gboolean
clapper_cache_is_disabled (void)
{
return cache_disabled;
}
GMappedFile *
clapper_cache_open (const gchar *filename, const gchar **data, GError **error)
{
GMappedFile *file;
if (G_UNLIKELY (cache_disabled))
return NULL;
if (!(file = g_mapped_file_new (filename, FALSE, error)))
return NULL;
if (G_UNLIKELY (g_mapped_file_get_length (file) == 0)) {
g_mapped_file_unref (file);
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"File is empty");
return NULL;
}
*data = g_mapped_file_get_contents (file);
/* Header name check */
if (G_UNLIKELY (g_strcmp0 (*data, CLAPPER_CACHE_HEADER) != 0)) {
g_mapped_file_unref (file);
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Invalid file header");
return NULL;
}
*data += strlen (*data) + 1;
/* Header version check */
if (clapper_cache_read_uint (data) != CLAPPER_VERSION_HEX) {
g_mapped_file_unref (file);
/* Just different version, so no error set */
return NULL;
}
return file;
}
inline gboolean
clapper_cache_read_boolean (const gchar **data)
{
gboolean val = *(const gboolean *) *data;
*data += sizeof (gboolean);
return val;
}
inline gint
clapper_cache_read_int (const gchar **data)
{
gint val = *(const gint *) *data;
*data += sizeof (gint);
return val;
}
inline guint
clapper_cache_read_uint (const gchar **data)
{
guint val = *(const guint *) *data;
*data += sizeof (guint);
return val;
}
inline gint64
clapper_cache_read_int64 (const gchar **data)
{
gint64 val = *(const gint64 *) *data;
*data += sizeof (gint64);
return val;
}
inline gdouble
clapper_cache_read_double (const gchar **data)
{
gdouble val = *(const gdouble *) *data;
*data += sizeof (gdouble);
return val;
}
inline const gchar *
clapper_cache_read_string (const gchar **data)
{
const gboolean is_null = clapper_cache_read_boolean (data);
const gchar *str = NULL;
if (!is_null) {
str = *data;
*data += strlen (str) + 1;
}
return str;
}
inline const guint8 *
clapper_cache_read_data (const gchar **data, gsize *size)
{
const guint8 *val = NULL;
*size = *(const gsize *) *data;
*data += sizeof (gsize);
if (G_LIKELY (*size > 0)) {
val = (const guint8 *) *data;
*data += *size;
}
return val;
}
inline GType
clapper_cache_read_enum (const gchar **data)
{
GType type;
const gchar *enum_name;
guint i, n_values;
enum_name = clapper_cache_read_string (data);
n_values = clapper_cache_read_uint (data);
/* If not registered yet */
if ((type = g_type_from_name (enum_name)) == 0) {
GEnumValue *values = g_new0 (GEnumValue, n_values + 1);
for (i = 0; i < n_values; ++i) {
values[i].value = clapper_cache_read_int (data);
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
}
g_array_append_val (enum_registry, values); // store statically
type = g_enum_register_static (g_intern_string (enum_name),
g_array_index (enum_registry, GEnumValue *, enum_registry->len - 1));
} else {
/* Skip over data */
for (i = 0; i < n_values; ++i) {
clapper_cache_read_int (data); // value
clapper_cache_read_string (data); // value_name
clapper_cache_read_string (data); // value_nick
}
}
return type;
}
inline GType
clapper_cache_read_flags (const gchar **data)
{
GType type;
const gchar *flags_name;
guint i, n_values;
flags_name = clapper_cache_read_string (data);
n_values = clapper_cache_read_uint (data);
/* If not registered yet */
if ((type = g_type_from_name (flags_name)) == 0) {
GFlagsValue *values = g_new0 (GFlagsValue, n_values + 1);
for (i = 0; i < n_values; ++i) {
values[i].value = clapper_cache_read_int (data);
values[i].value_name = g_intern_string (clapper_cache_read_string (data));
values[i].value_nick = g_intern_string (clapper_cache_read_string (data));
}
g_array_append_val (flags_registry, values); // store statically
type = g_flags_register_static (g_intern_string (flags_name),
g_array_index (flags_registry, GFlagsValue *, flags_registry->len - 1));
} else {
/* Skip over data */
for (i = 0; i < n_values; ++i) {
clapper_cache_read_int (data); // value
clapper_cache_read_string (data); // value_name
clapper_cache_read_string (data); // value_nick
}
}
return type;
}
GType
clapper_cache_read_iface (const gchar **data)
{
gint iface_id = clapper_cache_read_int (data);
switch (iface_id) {
case CLAPPER_CACHE_IFACE_EXTRACTABLE:
return CLAPPER_TYPE_EXTRACTABLE;
case CLAPPER_CACHE_IFACE_REACTABLE:
return CLAPPER_TYPE_REACTABLE;
default:
return 0;
}
}
GParamSpec *
clapper_cache_read_pspec (const gchar **data)
{
GParamSpec *pspec;
GType value_type;
const gchar *name, *nick, *blurb;
GParamFlags flags;
value_type = *(const GType *) *data;
*data += sizeof (GType);
name = clapper_cache_read_string (data);
nick = clapper_cache_read_string (data);
blurb = clapper_cache_read_string (data);
flags = *(const GParamFlags *) *data;
*data += sizeof (GParamFlags);
/* NOTE: C does not guarantee order in which function arguments
* are evaluated, so read into variables and then create pspec */
switch (value_type) {
case G_TYPE_BOOLEAN:
pspec = g_param_spec_boolean (name, nick, blurb,
clapper_cache_read_boolean (data), flags);
break;
case G_TYPE_INT:{
gint minimum = clapper_cache_read_int (data);
gint maximum = clapper_cache_read_int (data);
gint default_value = clapper_cache_read_int (data);
pspec = g_param_spec_int (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_UINT:{
guint minimum = clapper_cache_read_uint (data);
guint maximum = clapper_cache_read_uint (data);
guint default_value = clapper_cache_read_uint (data);
pspec = g_param_spec_uint (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_DOUBLE:{
gdouble minimum = clapper_cache_read_double (data);
gdouble maximum = clapper_cache_read_double (data);
gdouble default_value = clapper_cache_read_double (data);
pspec = g_param_spec_double (name, nick, blurb,
minimum, maximum, default_value, flags);
break;
}
case G_TYPE_STRING:
pspec = g_param_spec_string (name, nick, blurb,
clapper_cache_read_string (data), flags);
break;
case G_TYPE_ENUM:{
GType enum_type = clapper_cache_read_enum (data);
gint default_value = clapper_cache_read_int (data);
pspec = g_param_spec_enum (name, nick, blurb,
enum_type, default_value, flags);
break;
}
case G_TYPE_FLAGS:{
GType flags_type = clapper_cache_read_flags (data);
guint default_value = clapper_cache_read_uint (data);
pspec = g_param_spec_flags (name, nick, blurb,
flags_type, default_value, flags);
break;
}
default:
return NULL;
}
return g_param_spec_ref_sink (pspec);
}
GByteArray *
clapper_cache_create (void)
{
GByteArray *bytes;
if (G_UNLIKELY (cache_disabled))
return NULL;
bytes = g_byte_array_new ();
/* NOTE: We do not store whether string is NULL here, since it never is */
g_byte_array_append (bytes, (const guint8 *) CLAPPER_CACHE_HEADER, 8); // 7 + 1
clapper_cache_store_uint (bytes, CLAPPER_VERSION_HEX);
return bytes;
}
inline void
clapper_cache_store_boolean (GByteArray *bytes, gboolean val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gboolean));
}
inline void
clapper_cache_store_int (GByteArray *bytes, gint val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gint));
}
inline void
clapper_cache_store_uint (GByteArray *bytes, guint val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (guint));
}
inline void
clapper_cache_store_int64 (GByteArray *bytes, gint64 val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gint64));
}
inline void
clapper_cache_store_double (GByteArray *bytes, gdouble val)
{
g_byte_array_append (bytes, (const guint8 *) &val, sizeof (gdouble));
}
inline void
clapper_cache_store_string (GByteArray *bytes, const gchar *val)
{
/* Distinguish empty string from NULL */
const gboolean is_null = (val == NULL);
clapper_cache_store_boolean (bytes, is_null);
if (!is_null)
g_byte_array_append (bytes, (const guint8 *) val, strlen (val) + 1);
}
inline void
clapper_cache_store_data (GByteArray *bytes, const guint8 *val, gsize val_size)
{
g_byte_array_append (bytes, (const guint8 *) &val_size, sizeof (gsize));
if (G_LIKELY (val_size > 0))
g_byte_array_append (bytes, val, val_size);
}
inline void
clapper_cache_store_enum (GByteArray *bytes, GType enum_type)
{
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (enum_type));
guint i;
clapper_cache_store_string (bytes, g_type_name (enum_type));
clapper_cache_store_uint (bytes, enum_class->n_values);
for (i = 0; i < enum_class->n_values; ++i) {
clapper_cache_store_int (bytes, enum_class->values[i].value);
clapper_cache_store_string (bytes, enum_class->values[i].value_name);
clapper_cache_store_string (bytes, enum_class->values[i].value_nick);
}
}
inline void
clapper_cache_store_flags (GByteArray *bytes, GType flags_type)
{
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (flags_type));
guint i;
clapper_cache_store_string (bytes, g_type_name (flags_type));
clapper_cache_store_uint (bytes, flags_class->n_values);
for (i = 0; i < flags_class->n_values; ++i) {
clapper_cache_store_int (bytes, flags_class->values[i].value);
clapper_cache_store_string (bytes, flags_class->values[i].value_name);
clapper_cache_store_string (bytes, flags_class->values[i].value_nick);
}
}
gboolean
clapper_cache_store_iface (GByteArray *bytes, GType iface)
{
gint iface_id = 0;
if (iface == CLAPPER_TYPE_EXTRACTABLE)
iface_id = CLAPPER_CACHE_IFACE_EXTRACTABLE;
else if (iface == CLAPPER_TYPE_REACTABLE)
iface_id = CLAPPER_CACHE_IFACE_REACTABLE;
else
return FALSE;
clapper_cache_store_int (bytes, iface_id);
return TRUE;
}
gboolean
clapper_cache_store_pspec (GByteArray *bytes, GParamSpec *pspec)
{
GParamFlags flags;
const gboolean is_enum = G_IS_PARAM_SPEC_ENUM (pspec);
const gboolean is_flags = (!is_enum && G_IS_PARAM_SPEC_FLAGS (pspec));
if (is_enum) {
GType enum_type = G_TYPE_ENUM;
g_byte_array_append (bytes, (const guint8 *) &enum_type, sizeof (GType));
} else if (is_flags) {
GType flags_type = G_TYPE_FLAGS;
g_byte_array_append (bytes, (const guint8 *) &flags_type, sizeof (GType));
} else {
g_byte_array_append (bytes, (const guint8 *) &pspec->value_type, sizeof (GType));
}
clapper_cache_store_string (bytes, g_param_spec_get_name (pspec));
clapper_cache_store_string (bytes, g_param_spec_get_nick (pspec));
clapper_cache_store_string (bytes, g_param_spec_get_blurb (pspec));
flags = pspec->flags;
flags &= ~G_PARAM_STATIC_STRINGS; // Data read from cache is never static
g_byte_array_append (bytes, (const guint8 *) &flags, sizeof (GParamFlags));
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:{
GParamSpecBoolean *p = (GParamSpecBoolean *) pspec;
clapper_cache_store_boolean (bytes, p->default_value);
break;
}
case G_TYPE_INT:{
GParamSpecInt *p = (GParamSpecInt *) pspec;
clapper_cache_store_int (bytes, p->minimum);
clapper_cache_store_int (bytes, p->maximum);
clapper_cache_store_int (bytes, p->default_value);
break;
}
case G_TYPE_UINT:{
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
clapper_cache_store_uint (bytes, p->minimum);
clapper_cache_store_uint (bytes, p->maximum);
clapper_cache_store_uint (bytes, p->default_value);
break;
}
case G_TYPE_DOUBLE:{
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
clapper_cache_store_double (bytes, p->minimum);
clapper_cache_store_double (bytes, p->maximum);
clapper_cache_store_double (bytes, p->default_value);
break;
}
case G_TYPE_STRING:{
GParamSpecString *p = (GParamSpecString *) pspec;
clapper_cache_store_string (bytes, p->default_value);
break;
}
default:{
if (is_enum) {
GParamSpecEnum *p = (GParamSpecEnum *) pspec;
clapper_cache_store_enum (bytes, pspec->value_type);
clapper_cache_store_int (bytes, p->default_value);
break;
} else if (is_flags) {
GParamSpecFlags *p = (GParamSpecFlags *) pspec;
clapper_cache_store_flags (bytes, pspec->value_type);
clapper_cache_store_uint (bytes, p->default_value);
break;
}
return FALSE;
}
}
return TRUE;
}
gboolean
clapper_cache_write (const gchar *filename, GByteArray *bytes, GError **error)
{
gchar *dirname = g_path_get_dirname (filename);
gboolean has_dir;
has_dir = (g_mkdir_with_parents (dirname, 0755) == 0);
g_free (dirname);
if (!has_dir) {
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"Could not create directory to store cache content");
return FALSE;
}
/* Using "g_file_set_contents" to replace file atomically */
return g_file_set_contents (filename, (const gchar *) bytes->data, bytes->len, error);
}

View File

@@ -0,0 +1,44 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
ClapperEnhancerProxyList * clapper_enhancer_proxy_list_new_named (const gchar *name);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_take_proxy (ClapperEnhancerProxyList *list, ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_fill_from_global_proxies (ClapperEnhancerProxyList *list);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *list);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *list, GType iface_type);
G_END_DECLS

View File

@@ -0,0 +1,351 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
* ClapperEnhancerProxyList:
*
* A list of enhancer proxies.
*
* Since: 0.10
*/
#include <gio/gio.h>
#include "clapper-basic-functions.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-enhancer-proxy-private.h"
#define GST_CAT_DEFAULT clapper_enhancer_proxy_list_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperEnhancerProxyList
{
GstObject parent;
GPtrArray *proxies;
};
enum
{
PROP_0,
PROP_N_PROXIES,
PROP_LAST
};
static void clapper_enhancer_proxy_list_model_iface_init (GListModelInterface *iface);
#define parent_class clapper_enhancer_proxy_list_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperEnhancerProxyList, clapper_enhancer_proxy_list, GST_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, clapper_enhancer_proxy_list_model_iface_init));
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static GType
clapper_enhancer_proxy_list_model_get_item_type (GListModel *model)
{
return CLAPPER_TYPE_ENHANCER_PROXY;
}
static guint
clapper_enhancer_proxy_list_model_get_n_items (GListModel *model)
{
return CLAPPER_ENHANCER_PROXY_LIST_CAST (model)->proxies->len;
}
static gpointer
clapper_enhancer_proxy_list_model_get_item (GListModel *model, guint index)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (model);
ClapperEnhancerProxy *proxy = NULL;
if (G_LIKELY (index < self->proxies->len))
proxy = gst_object_ref (g_ptr_array_index (self->proxies, index));
return proxy;
}
static void
clapper_enhancer_proxy_list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = clapper_enhancer_proxy_list_model_get_item_type;
iface->get_n_items = clapper_enhancer_proxy_list_model_get_n_items;
iface->get_item = clapper_enhancer_proxy_list_model_get_item;
}
/*
* clapper_enhancer_proxy_list_new_named:
* @name: (nullable): name of the #GstObject
*
* Returns: (transfer full): a new #ClapperEnhancerProxyList instance
*/
ClapperEnhancerProxyList *
clapper_enhancer_proxy_list_new_named (const gchar *name)
{
ClapperEnhancerProxyList *list;
list = g_object_new (CLAPPER_TYPE_ENHANCER_PROXY_LIST,
"name", name, NULL);
gst_object_ref_sink (list);
return list;
}
void
clapper_enhancer_proxy_list_take_proxy (ClapperEnhancerProxyList *self, ClapperEnhancerProxy *proxy)
{
g_ptr_array_add (self->proxies, proxy);
gst_object_set_parent (GST_OBJECT_CAST (proxy), GST_OBJECT_CAST (self));
}
/*
* clapper_enhancer_proxy_list_fill_from_global_proxies:
*
* Fill list with unconfigured proxies from global proxies list.
*/
void
clapper_enhancer_proxy_list_fill_from_global_proxies (ClapperEnhancerProxyList *self)
{
ClapperEnhancerProxyList *global_list = clapper_get_global_enhancer_proxies ();
static guint _list_id = 0;
guint i;
for (i = 0; i < global_list->proxies->len; ++i) {
ClapperEnhancerProxy *proxy, *proxy_copy;
gchar obj_name[64];
proxy = clapper_enhancer_proxy_list_peek_proxy (global_list, i);
/* Name newly created proxy, very useful for debugging. Keep index per
* list, so it will be the same as the player that proxy belongs to. */
g_snprintf (obj_name, sizeof (obj_name), "%s-proxy%u",
clapper_enhancer_proxy_get_friendly_name (proxy), _list_id);
proxy_copy = clapper_enhancer_proxy_copy (proxy, obj_name);
clapper_enhancer_proxy_list_take_proxy (self, proxy_copy);
}
_list_id++;
}
static gint
_sort_values_by_name (ClapperEnhancerProxy *proxy_a, ClapperEnhancerProxy *proxy_b)
{
return g_ascii_strcasecmp (
clapper_enhancer_proxy_get_friendly_name (proxy_a),
clapper_enhancer_proxy_get_friendly_name (proxy_b));
}
/*
* clapper_enhancer_proxy_list_sort:
*
* Sort all list elements by enhancer friendly name.
*/
void
clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *self)
{
g_ptr_array_sort_values (self->proxies, (GCompareFunc) _sort_values_by_name);
}
/*
* clapper_enhancer_proxy_list_has_proxy_with_interface:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any enhancer proxy was found.
*/
gboolean
clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *self, GType iface_type)
{
guint i;
for (i = 0; i < self->proxies->len; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (self, i);
if (clapper_enhancer_proxy_target_has_interface (proxy, iface_type))
return TRUE;
}
return FALSE;
}
/**
* clapper_enhancer_proxy_list_get_proxy:
* @list: a #ClapperEnhancerProxyList
* @index: an enhancer proxy index
*
* Get the #ClapperEnhancerProxy at index.
*
* This behaves the same as [method@Gio.ListModel.get_item], and is here
* for code uniformity and convenience to avoid type casting by user.
*
* Returns: (transfer full) (nullable): The #ClapperEnhancerProxy at @index.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_get_proxy (ClapperEnhancerProxyList *self, guint index)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
return g_list_model_get_item (G_LIST_MODEL (self), index);
}
/**
* clapper_enhancer_proxy_list_peek_proxy: (skip)
* @list: a #ClapperEnhancerProxyList
* @index: an enhancer proxy index
*
* Get the #ClapperEnhancerProxy at index.
*
* Similar to [method@Clapper.EnhancerProxyList.get_proxy], but does not take
* a new reference on proxy.
*
* Proxies in a list are only removed when a [class@Clapper.Player] instance
* they originate from is destroyed, so do not use returned object afterwards
* unless you take an additional reference on it.
*
* Returns: (transfer none) (nullable): The #ClapperEnhancerProxy at @index.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_peek_proxy (ClapperEnhancerProxyList *self, guint index)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
return g_ptr_array_index (self->proxies, index);
}
/**
* clapper_enhancer_proxy_list_get_proxy_by_module:
* @list: a #ClapperEnhancerProxyList
* @module_name: an enhancer module name
*
* Get the #ClapperEnhancerProxy by module name as defined in its plugin file.
*
* A convenience function to find a #ClapperEnhancerProxy by its unique
* module name in the list.
*
* Returns: (transfer full) (nullable): The #ClapperEnhancerProxy with requested module name.
*
* Since: 0.10
*/
ClapperEnhancerProxy *
clapper_enhancer_proxy_list_get_proxy_by_module (ClapperEnhancerProxyList *self, const gchar *module_name)
{
guint i;
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), NULL);
g_return_val_if_fail (module_name != NULL, NULL);
for (i = 0; i < self->proxies->len; ++i) {
ClapperEnhancerProxy *proxy = g_ptr_array_index (self->proxies, i);
if (strcmp (clapper_enhancer_proxy_get_module_name (proxy), module_name) == 0)
return gst_object_ref (proxy);
}
return NULL;
}
/**
* clapper_enhancer_proxy_list_get_n_proxies:
* @list: a #ClapperEnhancerProxyList
*
* Get the number of proxies in #ClapperEnhancerProxyList.
*
* This behaves the same as [method@Gio.ListModel.get_n_items], and is here
* for code uniformity and convenience to avoid type casting by user.
*
* Returns: The number of proxies in #ClapperEnhancerProxyList.
*
* Since: 0.10
*/
guint
clapper_enhancer_proxy_list_get_n_proxies (ClapperEnhancerProxyList *self)
{
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY_LIST (self), 0);
return g_list_model_get_n_items (G_LIST_MODEL (self));
}
static void
_proxy_remove_func (ClapperEnhancerProxy *proxy)
{
gst_object_unparent (GST_OBJECT_CAST (proxy));
gst_object_unref (proxy);
}
static void
clapper_enhancer_proxy_list_init (ClapperEnhancerProxyList *self)
{
self->proxies = g_ptr_array_new_with_free_func ((GDestroyNotify) _proxy_remove_func);
}
static void
clapper_enhancer_proxy_list_finalize (GObject *object)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
g_ptr_array_unref (self->proxies);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_enhancer_proxy_list_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperEnhancerProxyList *self = CLAPPER_ENHANCER_PROXY_LIST_CAST (object);
switch (prop_id) {
case PROP_N_PROXIES:
g_value_set_uint (value, clapper_enhancer_proxy_list_get_n_proxies (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_enhancer_proxy_list_class_init (ClapperEnhancerProxyListClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancerproxylist", 0,
"Clapper Enhancer Proxy List");
gobject_class->get_property = clapper_enhancer_proxy_list_get_property;
gobject_class->finalize = clapper_enhancer_proxy_list_finalize;
/**
* ClapperEnhancerProxyList:n-proxies:
*
* Number of proxies in the list.
*
* Since: 0.10
*/
param_specs[PROP_N_PROXIES] = g_param_spec_uint ("n-proxies",
NULL, NULL, 0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

@@ -0,0 +1,52 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enhancer-proxy.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_ENHANCER_PROXY_LIST (clapper_enhancer_proxy_list_get_type())
#define CLAPPER_ENHANCER_PROXY_LIST_CAST(obj) ((ClapperEnhancerProxyList *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperEnhancerProxyList, clapper_enhancer_proxy_list, CLAPPER, ENHANCER_PROXY_LIST, GstObject)
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_get_proxy (ClapperEnhancerProxyList *list, guint index);
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_peek_proxy (ClapperEnhancerProxyList *list, guint index);
CLAPPER_API
ClapperEnhancerProxy * clapper_enhancer_proxy_list_get_proxy_by_module (ClapperEnhancerProxyList *list, const gchar *module_name);
CLAPPER_API
guint clapper_enhancer_proxy_list_get_n_proxies (ClapperEnhancerProxyList *list);
G_END_DECLS

View File

@@ -0,0 +1,56 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
ClapperEnhancerProxy * clapper_enhancer_proxy_new_global_take (GObject *peas_info); // Using parent type for building without libpeas
G_GNUC_INTERNAL
ClapperEnhancerProxy * clapper_enhancer_proxy_copy (ClapperEnhancerProxy *src_proxy, const gchar *copy_name);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_fill_from_cache (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *proxy, GObject *enhancer);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
GObject * clapper_enhancer_proxy_get_peas_info (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_has_locally_set (ClapperEnhancerProxy *proxy, const gchar *property_name);
G_GNUC_INTERNAL
GstStructure * clapper_enhancer_proxy_make_current_config (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
void clapper_enhancer_proxy_apply_config_to_enhancer (ClapperEnhancerProxy *proxy, const GstStructure *config, GObject *enhancer);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_ENHANCER_PROXY (clapper_enhancer_proxy_get_type())
#define CLAPPER_ENHANCER_PROXY_CAST(obj) ((ClapperEnhancerProxy *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperEnhancerProxy, clapper_enhancer_proxy, CLAPPER, ENHANCER_PROXY, GstObject)
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_friendly_name (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_module_name (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_module_dir (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_description (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_version (ClapperEnhancerProxy *proxy);
CLAPPER_API
const gchar * clapper_enhancer_proxy_get_extra_data (ClapperEnhancerProxy *proxy, const gchar *key);
CLAPPER_API
gboolean clapper_enhancer_proxy_extra_data_lists_value (ClapperEnhancerProxy *proxy, const gchar *key, const gchar *value);
CLAPPER_API
GType * clapper_enhancer_proxy_get_target_interfaces (ClapperEnhancerProxy *proxy, guint *n_interfaces);
CLAPPER_API
gboolean clapper_enhancer_proxy_target_has_interface (ClapperEnhancerProxy *proxy, GType iface_type);
CLAPPER_API
GParamSpec ** clapper_enhancer_proxy_get_target_properties (ClapperEnhancerProxy *proxy, guint *n_properties);
CLAPPER_API
GSettings * clapper_enhancer_proxy_get_settings (ClapperEnhancerProxy *proxy);
CLAPPER_API
void clapper_enhancer_proxy_set_locally (ClapperEnhancerProxy *proxy, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED;
CLAPPER_API
void clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *proxy, GHashTable *table);
CLAPPER_API
void clapper_enhancer_proxy_set_target_creation_allowed (ClapperEnhancerProxy *proxy, gboolean allowed);
CLAPPER_API
gboolean clapper_enhancer_proxy_get_target_creation_allowed (ClapperEnhancerProxy *proxy);
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -22,21 +21,15 @@
#include <glib.h>
#include <glib-object.h>
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_enhancers_loader_initialize (void);
void clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies);
G_GNUC_INTERNAL
gboolean clapper_enhancers_loader_has_enhancers (GType iface_type);
G_GNUC_INTERNAL
gchar ** clapper_enhancers_loader_get_schemes (GType iface_type);
G_GNUC_INTERNAL
gboolean clapper_enhancers_loader_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
G_GNUC_INTERNAL
GObject * clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri);
GObject * clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type);
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -28,11 +27,12 @@ static HMODULE _enhancers_dll_handle = NULL;
#endif
#include "clapper-enhancers-loader-private.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-enhancer-proxy-private.h"
#define ENHANCER_INTERFACES "X-Interfaces"
#define ENHANCER_SCHEMES "X-Schemes"
#define ENHANCER_HOSTS "X-Hosts"
#define ENHANCER_IFACE_NAME_FROM_TYPE(type) (g_type_name (type) + 7) // strip "Clapper" prefix
// Supported interfaces
#include "clapper-extractable.h"
#include "clapper-reactable.h"
#define GST_CAT_DEFAULT clapper_enhancers_loader_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -52,16 +52,39 @@ _import_enhancers (const gchar *enhancers_path)
g_strfreev (dir_paths);
}
static GObject *
_force_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
{
GObject *enhancer = NULL;
PeasPluginInfo *info = (PeasPluginInfo *) clapper_enhancer_proxy_get_peas_info (proxy);
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_module_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_LOG ("No \"%s\" enhancer in module: %s", g_type_name (iface_type),
peas_plugin_info_get_module_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
return enhancer;
}
/*
* clapper_enhancers_loader_initialize:
*
* Initializes #PeasEngine with directories that store enhancers.
*/
void
clapper_enhancers_loader_initialize (void)
clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
{
const gchar *enhancers_path;
gchar *custom_path = NULL;
guint i, n_items;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancersloader", 0,
"Clapper Enhancer Loader");
@@ -74,9 +97,8 @@ clapper_enhancers_loader_initialize (void)
win_base_dir = g_win32_get_package_installation_directory_of_module (
_enhancers_dll_handle);
/* FIXME: Avoid hardcoded major version */
custom_path = g_build_filename (win_base_dir,
"lib", "clapper-0.0", "enhancers", NULL);
"lib", CLAPPER_API_NAME, "enhancers", NULL);
enhancers_path = custom_path; // assign temporarily
g_free (win_base_dir);
@@ -104,321 +126,72 @@ clapper_enhancers_loader_initialize (void)
_import_enhancers (enhancers_path);
}
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_INFO) {
GListModel *list = (GListModel *) _engine;
guint i, n_items = g_list_model_get_n_items (list);
n_items = g_list_model_get_n_items ((GListModel *) _engine);
for (i = 0; i < n_items; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item ((GListModel *) _engine, i);
ClapperEnhancerProxy *proxy;
gboolean filled;
for (i = 0; i < n_items; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
GST_INFO ("Found enhancer: %s (%s)", peas_plugin_info_get_name (info),
peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES));
g_object_unref (info);
/* Clapper supports only 1 proxy per plugin. Each plugin can
* ship 1 class, but it can implement more than 1 interface. */
proxy = clapper_enhancer_proxy_new_global_take ((GObject *) info);
/* Try to fill missing data from cache (fast).
* Otherwise make an instance and fill missing data from it (slow). */
if (!(filled = clapper_enhancer_proxy_fill_from_cache (proxy))) {
GObject *enhancer;
const GType main_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_REACTABLE };
guint j;
/* We cannot ask libpeas for "any" of our main interfaces, so try each one until found */
for (j = 0; j < G_N_ELEMENTS (main_types); ++j) {
if ((enhancer = _force_create_enhancer (proxy, main_types[j]))) {
filled = clapper_enhancer_proxy_fill_from_instance (proxy, enhancer);
g_object_unref (enhancer);
clapper_enhancer_proxy_export_to_cache (proxy);
break;
}
}
}
GST_INFO ("Clapper enhancers initialized, found: %u", n_items);
if (G_LIKELY (filled)) {
GST_INFO ("Found enhancer: \"%s\" (%s)",
clapper_enhancer_proxy_get_friendly_name (proxy),
clapper_enhancer_proxy_get_module_name (proxy));
clapper_enhancer_proxy_list_take_proxy (proxies, proxy);
} else {
GST_WARNING ("Enhancer init failed: \"%s\" (%s)",
clapper_enhancer_proxy_get_friendly_name (proxy),
clapper_enhancer_proxy_get_module_name (proxy));
gst_object_unref (proxy);
}
}
clapper_enhancer_proxy_list_sort (proxies);
GST_INFO ("Clapper enhancers initialized, found: %u",
clapper_enhancer_proxy_list_get_n_proxies (proxies));
g_free (custom_path);
}
static inline gboolean
_is_name_listed (const gchar *name, const gchar *list_str)
{
gsize name_len = strlen (name);
guint i = 0;
while (list_str[i] != '\0') {
guint end = i;
while (list_str[end] != ';' && list_str[end] != '\0')
++end;
/* Compare letters count until separator and prefix of whole string */
if (end - i == name_len && g_str_has_prefix (list_str + i, name))
return TRUE;
i = end;
/* Move to the next letter after ';' */
if (list_str[i] != '\0')
++i;
}
return FALSE;
}
/*
* clapper_enhancers_loader_get_info:
* @iface_type: an interface #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
*
* Returns: (transfer full) (nullable): available #PeasPluginInfo or %NULL.
*/
static PeasPluginInfo *
clapper_enhancers_loader_get_info (GType iface_type, const gchar *scheme, const gchar *host)
{
GListModel *list = (GListModel *) _engine;
PeasPluginInfo *found_info = NULL;
guint i, n_plugins = g_list_model_get_n_items (list);
const gchar *iface_name;
gboolean is_https;
if (n_plugins == 0) {
GST_INFO ("No Clapper enhancers found");
return NULL;
}
iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
/* Strip common subdomains, so plugins do not
* have to list all combinations */
if (host) {
if (g_str_has_prefix (host, "www."))
host += 4;
else if (g_str_has_prefix (host, "m."))
host += 2;
}
GST_INFO ("Enhancer check, iface: %s, scheme: %s, host: %s",
iface_name, scheme, GST_STR_NULL (host));
is_https = (g_str_has_prefix (scheme, "http")
&& (scheme[4] == '\0' || (scheme[4] == 's' && scheme[5] == '\0')));
if (!host && is_https)
return NULL;
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes, *hosts;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
GST_DEBUG ("Skipping enhancer without interfaces: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
GST_DEBUG ("Skipping enhancer without schemes: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (scheme, schemes)) {
g_object_unref (info);
continue;
}
if (is_https) {
if (!(hosts = peas_plugin_info_get_external_data (info, ENHANCER_HOSTS))) {
GST_DEBUG ("Skipping enhancer without hosts: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (host, hosts)) {
g_object_unref (info);
continue;
}
}
found_info = info;
break;
}
return found_info;
}
/*
* clapper_enhancers_loader_has_enhancers:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any valid enhancer was found.
*/
gboolean
clapper_enhancers_loader_has_enhancers (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
guint i, n_plugins;
GST_DEBUG ("Checking for any enhancers of type: \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
/* Additional validation */
if (!peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES)
|| !peas_plugin_info_get_external_data (info, ENHANCER_HOSTS)) {
g_object_unref (info);
continue;
}
GST_DEBUG ("Found valid enhancers of type: \"%s\"", iface_name);
g_object_unref (info);
return TRUE;
}
GST_DEBUG ("No available enhancers of type: \"%s\"", iface_name);
return FALSE;
}
/*
* clapper_enhancers_loader_get_schemes:
* @iface_type: an interface #GType
*
* Get all supported schemes for a given interface type.
* The returned array consists of unique strings (no duplicates).
*
* Returns: (transfer full): all supported schemes by enhancers of type.
*/
gchar **
clapper_enhancers_loader_get_schemes (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
GSList *found_schemes = NULL, *fs;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
gchar **schemes_strv;
guint i, n_plugins, n_schemes;
GST_DEBUG ("Checking supported URI schemes for \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes;
gchar **tmp_strv;
gint j;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
g_object_unref (info);
continue;
}
tmp_strv = g_strsplit (schemes, ";", 0);
for (j = 0; tmp_strv[j]; ++j) {
const gchar *scheme = tmp_strv[j];
if (!found_schemes || !g_slist_find_custom (found_schemes,
scheme, (GCompareFunc) strcmp)) {
found_schemes = g_slist_append (found_schemes, g_strdup (scheme));
GST_INFO ("Found supported URI scheme: %s", scheme);
}
}
g_strfreev (tmp_strv);
g_object_unref (info);
}
n_schemes = g_slist_length (found_schemes);
schemes_strv = g_new0 (gchar *, n_schemes + 1);
fs = found_schemes;
for (i = 0; i < n_schemes; ++i) {
schemes_strv[i] = fs->data;
fs = fs->next;
}
GST_DEBUG ("Total found URI schemes: %u", n_schemes);
/* Since string pointers were taken,
* free list without content */
g_slist_free (found_schemes);
return schemes_strv;
}
/*
* clapper_enhancers_loader_check:
* clapper_enhancers_loader_create_enhancer:
* @iface_type: a requested #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
* @name: (out) (optional) (transfer none): return location for found enhancer name
* @info: a #PeasPluginInfo
*
* Checks if any enhancer can handle @uri without initializing loader
* or creating enhancer instance, thus this can be used freely from any thread.
*
* Returns: whether enhancer for given scheme and host is available.
*/
gboolean
clapper_enhancers_loader_check (GType iface_type,
const gchar *scheme, const gchar *host, const gchar **name)
{
PeasPluginInfo *info;
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
if (name)
*name = peas_plugin_info_get_name (info);
g_object_unref (info);
return TRUE;
}
return FALSE;
}
/*
* clapper_enhancers_loader_create_enhancer_for_uri:
* @iface_type: a requested #GType
* @uri: a #GUri
*
* Creates a new enhancer object for given URI.
* Creates a new enhancer object using @info.
*
* Enhancer should only be created and used within single thread.
*
* Returns: (transfer full) (nullable): a new enhancer instance.
*/
GObject *
clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri)
clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
{
GObject *enhancer = NULL;
PeasPluginInfo *info;
const gchar *scheme = g_uri_get_scheme (uri);
const gchar *host = g_uri_get_host (uri);
if (!clapper_enhancer_proxy_get_target_creation_allowed (proxy))
return NULL;
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_ERROR ("No \"%s\" enhancer in plugin: %s", ENHANCER_IFACE_NAME_FROM_TYPE (iface_type),
peas_plugin_info_get_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
g_object_unref (info);
}
return enhancer;
return _force_create_enhancer (proxy, iface_type);
}

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -127,4 +126,47 @@ typedef enum
CLAPPER_DISCOVERER_DISCOVERY_NONCURRENT,
} ClapperDiscovererDiscoveryMode;
/* NOTE: GStreamer uses param flags 8-16, so start with 17. */
/**
* ClapperEnhancerParamFlags:
* @CLAPPER_ENHANCER_PARAM_GLOBAL: Use this flag for enhancer properties that should have global access scope.
* Such are meant for application `USER` to configure.
* @CLAPPER_ENHANCER_PARAM_LOCAL: Use this flag for enhancer properties that should have local access scope.
* Such are meant for `APPLICATION` to configure.
* @CLAPPER_ENHANCER_PARAM_FILEPATH: Use this flag for enhancer properties that store string with a file path.
* Applications can use this as a hint to show file selection instead of a text entry.
* @CLAPPER_ENHANCER_PARAM_DIRPATH: Use this flag for enhancer properties that store string with a directory path.
* Applications can use this as a hint to show directory selection instead of a text entry.
*
* Additional [flags@GObject.ParamFlags] to be set in enhancer plugins implementations.
*
* Since: 0.10
*/
typedef enum
{
CLAPPER_ENHANCER_PARAM_GLOBAL = 1 << 17,
CLAPPER_ENHANCER_PARAM_LOCAL = 1 << 18,
CLAPPER_ENHANCER_PARAM_FILEPATH = 1 << 19,
CLAPPER_ENHANCER_PARAM_DIRPATH = 1 << 20,
} ClapperEnhancerParamFlags;
/**
* ClapperReactableItemUpdatedFlags:
* @CLAPPER_REACTABLE_ITEM_UPDATED_TITLE: Media item title was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_DURATION: Media item duration was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE: Media item timeline was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TAGS: Media item tags were updated.
*
* Flags informing which properties were updated within [class@Clapper.MediaItem].
*
* Since: 0.10
*/
typedef enum
{
CLAPPER_REACTABLE_ITEM_UPDATED_TITLE = 1 << 0,
CLAPPER_REACTABLE_ITEM_UPDATED_DURATION = 1 << 1,
CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE = 1 << 2,
CLAPPER_REACTABLE_ITEM_UPDATED_TAGS = 1 << 3,
} ClapperReactableItemUpdatedFlags;
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
@@ -30,6 +29,8 @@
* virtual functions logic, while for controlling playback implementation
* may call [method@Gst.Object.get_parent] to acquire a weak reference on
* a parent [class@Clapper.Player] object feature was added to.
*
* Deprecated: 0.10: Use [iface@Clapper.Reactable] instead.
*/
#include "clapper-feature.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -37,7 +36,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_FEATURE (clapper_feature_get_type())
#define CLAPPER_FEATURE_CAST(obj) ((ClapperFeature *)(obj))
CLAPPER_API
CLAPPER_DEPRECATED_FOR(ClapperReactable)
G_DECLARE_DERIVABLE_TYPE (ClapperFeature, clapper_feature, CLAPPER, FEATURE, GstObject)
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-bus-private.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#include "clapper-features-manager-private.h"

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -23,6 +22,7 @@
#include <gst/tag/tag.h>
#include "clapper-harvest.h"
#include "clapper-enhancer-proxy.h"
G_BEGIN_DECLS
@@ -32,4 +32,10 @@ ClapperHarvest * clapper_harvest_new (void);
G_GNUC_INTERNAL
gboolean clapper_harvest_unpack (ClapperHarvest *harvest, GstBuffer **buffer, gsize *buf_size, GstCaps **caps, GstTagList **tags, GstToc **toc, GstStructure **headers);
G_GNUC_INTERNAL
gboolean clapper_harvest_fill_from_cache (ClapperHarvest *harvest, ClapperEnhancerProxy *proxy, const GstStructure *config, GUri *uri);
G_GNUC_INTERNAL
void clapper_harvest_export_to_cache (ClapperHarvest *harvest, ClapperEnhancerProxy *proxy, const GstStructure *config, GUri *uri);
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
@@ -31,7 +30,11 @@
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2867
*/
#include "config.h"
#include "clapper-harvest-private.h"
#include "clapper-cache-private.h"
#include "clapper-utils.h"
#define GST_CAT_DEFAULT clapper_harvest_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -50,6 +53,8 @@ struct _ClapperHarvest
guint16 n_chapters;
guint16 n_tracks;
gint64 exp_epoch;
};
#define parent_class clapper_harvest_parent_class
@@ -119,6 +124,302 @@ clapper_harvest_unpack (ClapperHarvest *self,
return TRUE;
}
/* Custom implementation due to the lack of TOC serialization in GStreamer */
static void
_harvest_fill_toc_from_cache (ClapperHarvest *self, const gchar **data)
{
guint i, n_entries;
n_entries = clapper_cache_read_uint (data);
for (i = 0; i < n_entries; ++i) {
guint j, n_subentries;
n_subentries = clapper_cache_read_uint (data);
for (j = 0; j < n_subentries; ++j) {
GstTocEntryType type;
const gchar *title;
gdouble start, end;
type = (GstTocEntryType) clapper_cache_read_int (data);
title = clapper_cache_read_string (data);
start = clapper_cache_read_double (data);
end = clapper_cache_read_double (data);
clapper_harvest_toc_add (self, type, title, start, end);
}
}
}
static void
_harvest_store_toc_to_cache (ClapperHarvest *self, GByteArray *bytes)
{
GList *list = NULL, *el;
guint n_entries = 0;
if (self->toc) {
list = gst_toc_get_entries (self->toc);
n_entries = g_list_length (list);
}
clapper_cache_store_uint (bytes, n_entries);
for (el = list; el; el = g_list_next (el)) {
const GstTocEntry *entry = (const GstTocEntry *) el->data;
GList *subentries, *sub_el;
guint n_subentries;
subentries = gst_toc_entry_get_sub_entries (entry);
n_subentries = g_list_length (subentries);
clapper_cache_store_uint (bytes, n_subentries);
for (sub_el = subentries; sub_el; sub_el = g_list_next (sub_el)) {
const GstTocEntry *subentry = (const GstTocEntry *) sub_el->data;
GstTagList *tags;
gint64 start = 0, end = 0;
gdouble start_dbl, end_dbl;
const gchar *title = NULL;
clapper_cache_store_int (bytes, (gint) gst_toc_entry_get_entry_type (subentry));
if ((tags = gst_toc_entry_get_tags (subentry)))
gst_tag_list_peek_string_index (tags, GST_TAG_TITLE, 0, &title);
clapper_cache_store_string (bytes, title);
gst_toc_entry_get_start_stop_times (subentry, &start, &end);
start_dbl = ((gdouble) start) / GST_SECOND;
end_dbl = (end >= 0) ? ((gdouble) end) / GST_SECOND : -1;
clapper_cache_store_double (bytes, start_dbl);
clapper_cache_store_double (bytes, end_dbl);
}
}
}
static inline gchar *
_build_cache_filename (ClapperEnhancerProxy *proxy, GUri *uri)
{
gchar *uri_str = g_uri_to_string (uri);
gchar name[15];
g_snprintf (name, sizeof (name), "%u.bin", g_str_hash (uri_str));
g_free (uri_str);
return g_build_filename (g_get_user_cache_dir (), CLAPPER_API_NAME,
"enhancers", clapper_enhancer_proxy_get_module_name (proxy),
"harvests", name, NULL);
}
/* NOTE: On failure, this function must not modify harvest! */
gboolean
clapper_harvest_fill_from_cache (ClapperHarvest *self, ClapperEnhancerProxy *proxy,
const GstStructure *config, GUri *uri)
{
GMappedFile *mapped_file;
GstStructure *config_cached = NULL;
GError *error = NULL;
gchar *filename;
const gchar *data, *read_str;
const guint8 *buf_data;
guint8 *buf_copy;
gsize buf_size;
gint64 epoch_cached, epoch_now = 0;
gdouble exp_seconds;
gboolean changed, read_ok = FALSE;
filename = _build_cache_filename (proxy, uri);
GST_DEBUG_OBJECT (self, "Importing harvest from cache file: \"%s\"", filename);
mapped_file = clapper_cache_open (filename, &data, &error);
g_free (filename);
if (!mapped_file) {
/* No error if cache disabled or version mismatch */
if (error) {
if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)
GST_DEBUG_OBJECT (self, "No cached harvest found");
else
GST_ERROR_OBJECT (self, "Could not use cached harvest, reason: %s", error->message);
g_error_free (error);
}
return FALSE;
}
/* Plugin version check */
if (g_strcmp0 (clapper_cache_read_string (&data),
clapper_enhancer_proxy_get_version (proxy)) != 0)
goto finish; // no error printing here
if (G_LIKELY ((epoch_cached = clapper_cache_read_int64 (&data)) > 0)) {
GDateTime *date = g_date_time_new_now_utc ();
epoch_now = g_date_time_to_unix (date);
g_date_time_unref (date);
}
/* Check if expired */
if ((exp_seconds = (gdouble) (epoch_cached - epoch_now)) <= 0) {
GST_DEBUG_OBJECT (self, "Cached harvest expired"); // expiration is not an error
goto finish;
}
GST_DEBUG_OBJECT (self, "Cached harvest expiration in %" CLAPPER_TIME_FORMAT,
CLAPPER_TIME_ARGS (exp_seconds));
/* Read last used config to generate cache data */
if ((read_str = clapper_cache_read_string (&data)))
config_cached = gst_structure_from_string (read_str, NULL);
/* Compare used config when cache was generated to the current one */
changed = (config_cached && config)
? !gst_structure_is_equal (config_cached, config)
: (config_cached != config);
gst_clear_structure (&config_cached);
if (changed) {
GST_DEBUG_OBJECT (self, "Enhancer config differs from the last time");
goto finish;
}
/* Read media type */
read_str = clapper_cache_read_string (&data);
if (G_UNLIKELY (read_str == NULL)) {
GST_ERROR_OBJECT (self, "Could not read media type from cache file");
goto finish;
}
/* Read buffer data */
buf_data = clapper_cache_read_data (&data, &buf_size);
if (G_UNLIKELY (buf_data == NULL)) {
GST_ERROR_OBJECT (self, "Could not read buffer data from cache");
goto finish;
}
/* Fill harvest */
buf_copy = g_memdup2 (buf_data, buf_size);
if (!clapper_harvest_fill (self, read_str, buf_copy, buf_size))
goto finish;
/* Read tags */
read_str = clapper_cache_read_string (&data);
if (read_str && (self->tags = gst_tag_list_new_from_string (read_str))) {
GST_LOG_OBJECT (self, "Read %s", read_str);
gst_tag_list_set_scope (self->tags, GST_TAG_SCOPE_GLOBAL);
}
/* Read TOC */
_harvest_fill_toc_from_cache (self, &data);
/* Read headers */
read_str = clapper_cache_read_string (&data);
if (read_str && (self->headers = gst_structure_from_string (read_str, NULL)))
GST_LOG_OBJECT (self, "Read %s", read_str);
read_ok = TRUE;
finish:
g_mapped_file_unref (mapped_file);
if (!read_ok)
return FALSE;
GST_DEBUG_OBJECT (self, "Filled harvest from cache");
return TRUE;
}
void
clapper_harvest_export_to_cache (ClapperHarvest *self, ClapperEnhancerProxy *proxy,
const GstStructure *config, GUri *uri)
{
GByteArray *bytes;
const GstStructure *caps_structure;
gchar *filename, *temp_str = NULL;
gboolean data_ok = TRUE;
/* No caching if no expiration date set */
if (self->exp_epoch <= 0)
return;
/* Might happen if extractor extract function implementation
* returns %TRUE without filling harvest properly */
if (G_UNLIKELY (self->caps == NULL || self->buffer == NULL))
return; // no data to cache
bytes = clapper_cache_create ();
/* If cache disabled */
if (G_UNLIKELY (bytes == NULL))
return;
filename = _build_cache_filename (proxy, uri);
GST_DEBUG_OBJECT (self, "Exporting harvest to cache file: \"%s\"", filename);
/* Store enhancer version that generated harvest */
clapper_cache_store_string (bytes, clapper_enhancer_proxy_get_version (proxy));
/* Store expiration date */
clapper_cache_store_int64 (bytes, self->exp_epoch);
/* Store config used to generate harvest */
if (config)
temp_str = gst_structure_to_string (config);
clapper_cache_store_string (bytes, temp_str); // NULL when no config
g_clear_pointer (&temp_str, g_free);
/* Store media type */
caps_structure = gst_caps_get_structure (self->caps, 0);
if (G_LIKELY (caps_structure != NULL)) {
clapper_cache_store_string (bytes, gst_structure_get_name (caps_structure));
} else {
GST_ERROR_OBJECT (self, "Cannot cache empty caps");
data_ok = FALSE;
}
if (G_LIKELY (data_ok)) {
GstMemory *mem;
GstMapInfo map_info;
/* Store buffer data */
mem = gst_buffer_peek_memory (self->buffer, 0);
if (G_LIKELY (gst_memory_map (mem, &map_info, GST_MAP_READ))) {
clapper_cache_store_data (bytes, map_info.data, map_info.size);
gst_memory_unmap (mem, &map_info);
} else {
GST_ERROR_OBJECT (self, "Could not map harvest buffer for reading");
data_ok = FALSE;
}
}
if (G_LIKELY (data_ok)) {
GError *error = NULL;
/* Store tags */
if (self->tags)
temp_str = gst_tag_list_to_string (self->tags);
clapper_cache_store_string (bytes, temp_str);
g_clear_pointer (&temp_str, g_free);
/* Store TOC */
_harvest_store_toc_to_cache (self, bytes);
/* Store headers */
if (self->headers)
temp_str = gst_structure_to_string (self->headers);
clapper_cache_store_string (bytes, temp_str);
g_clear_pointer (&temp_str, g_free);
if (clapper_cache_write (filename, bytes, &error)) {
GST_DEBUG_OBJECT (self, "Successfully exported harvest to cache file");
} else if (error) {
GST_ERROR_OBJECT (self, "Could not cache harvest, reason: %s", error->message);
g_error_free (error);
}
}
g_free (filename);
g_byte_array_free (bytes, TRUE);
}
/**
* clapper_harvest_fill:
* @harvest: a #ClapperHarvest
@@ -155,7 +456,7 @@ clapper_harvest_fill (ClapperHarvest *self, const gchar *media_type, gpointer da
return FALSE;
}
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) {
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_LOG) {
gboolean is_printable = (strcmp (media_type, "application/dash+xml") == 0)
|| (strcmp (media_type, "application/x-hls") == 0)
|| (strcmp (media_type, "text/uri-list") == 0);
@@ -166,7 +467,7 @@ clapper_harvest_fill (ClapperHarvest *self, const gchar *media_type, gpointer da
data_str = g_new0 (gchar, size + 1);
memcpy (data_str, data, size);
GST_DEBUG_OBJECT (self, "Filled with data:\n%s", data_str);
GST_LOG_OBJECT (self, "Filled with data:\n%s", data_str);
g_free (data_str);
}
@@ -328,7 +629,7 @@ clapper_harvest_toc_add (ClapperHarvest *self, GstTocEntryType type,
g_snprintf (edition, sizeof (edition), "0%i", type);
g_snprintf (id, sizeof (id), "%s.%" G_GUINT16_FORMAT, id_prefix, nth_entry);
GST_DEBUG_OBJECT (self, "Inserting TOC %s: \"%s\""
GST_LOG_OBJECT (self, "Inserting TOC %s: \"%s\""
" (%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")",
id, title, start_time, end_time);
@@ -380,7 +681,7 @@ clapper_harvest_headers_set (ClapperHarvest *self, const gchar *key, ...)
while (key != NULL) {
const gchar *val = va_arg (args, const gchar *);
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, val);
GST_LOG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, val);
gst_structure_set (self->headers, key, G_TYPE_STRING, val, NULL);
key = va_arg (args, const gchar *);
}
@@ -409,10 +710,70 @@ clapper_harvest_headers_set_value (ClapperHarvest *self, const gchar *key, const
_ensure_headers (self);
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, g_value_get_string (value));
GST_LOG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, g_value_get_string (value));
gst_structure_set_value (self->headers, key, value);
}
/**
* clapper_harvest_set_expiration_date_utc:
* @harvest: a #ClapperHarvest
* @date_utc: a #GDateTime in UTC time
*
* Set date in UTC time until harvested content is expected
* to stay alive.
*
* This is used for harvest caching, so next time user requests to
* play the same URI, recently harvested data can be reused without
* the need to run [vfunc@Clapper.Extractable.extract] again.
*
* Since: 0.10
*/
void
clapper_harvest_set_expiration_date_utc (ClapperHarvest *self, GDateTime *date_utc)
{
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (date_utc != NULL);
self->exp_epoch = g_date_time_to_unix (date_utc);
GST_LOG_OBJECT (self, "Expiration epoch: %" G_GINT64_FORMAT, self->exp_epoch);
}
/**
* clapper_harvest_set_expiration_seconds:
* @harvest: a #ClapperHarvest
* @seconds: time in seconds until expiration
*
* Set amount of seconds for how long harvested content is
* expected to stay alive.
*
* Alternative function to [method@Clapper.Harvest.set_expiration_date_utc],
* but takes time as number in seconds from now.
*
* It is safe to pass zero or negative number to this function in
* case when calculating time manually and it already expired.
*
* Since: 0.10
*/
void
clapper_harvest_set_expiration_seconds (ClapperHarvest *self, gdouble seconds)
{
GDateTime *date, *date_epoch;
g_return_if_fail (CLAPPER_IS_HARVEST (self));
GST_LOG_OBJECT (self, "Set expiration in %" CLAPPER_TIME_FORMAT,
CLAPPER_TIME_ARGS (seconds));
date = g_date_time_new_now_utc ();
date_epoch = g_date_time_add_seconds (date, seconds);
g_date_time_unref (date);
self->exp_epoch = g_date_time_to_unix (date_epoch);
g_date_time_unref (date_epoch);
GST_LOG_OBJECT (self, "Expiration epoch: %" G_GINT64_FORMAT, self->exp_epoch);
}
static void
clapper_harvest_init (ClapperHarvest *self)
{

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -61,4 +60,10 @@ void clapper_harvest_headers_set (ClapperHarvest *harvest, const gchar *key, ...
CLAPPER_API
void clapper_harvest_headers_set_value (ClapperHarvest *harvest, const gchar *key, const GValue *value);
CLAPPER_API
void clapper_harvest_set_expiration_date_utc (ClapperHarvest *harvest, GDateTime *date_utc);
CLAPPER_API
void clapper_harvest_set_expiration_seconds (ClapperHarvest *harvest, gdouble seconds);
G_END_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -23,7 +22,7 @@
#include <gst/pbutils/pbutils.h>
#include "clapper-media-item.h"
#include "clapper-player-private.h"
#include "clapper-player.h"
#include "clapper-app-bus-private.h"
G_BEGIN_DECLS

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
/**
@@ -30,6 +29,7 @@
#include "clapper-timeline-private.h"
#include "clapper-player-private.h"
#include "clapper-playbin-bus-private.h"
#include "clapper-reactables-manager-private.h"
#include "clapper-features-manager-private.h"
#include "clapper-utils-private.h"
@@ -43,6 +43,7 @@ struct _ClapperMediaItem
gchar *uri;
gchar *suburi;
GstTagList *tags;
ClapperTimeline *timeline;
guint id;
@@ -56,6 +57,13 @@ struct _ClapperMediaItem
gboolean used;
};
typedef struct
{
ClapperMediaItem *item;
gboolean changed;
gboolean from_user;
} ClapperMediaItemTagIterData;
enum
{
PROP_0,
@@ -63,6 +71,7 @@ enum
PROP_URI,
PROP_SUBURI,
PROP_CACHE_LOCATION,
PROP_TAGS,
PROP_TITLE,
PROP_CONTAINER_FORMAT,
PROP_DURATION,
@@ -111,8 +120,8 @@ clapper_media_item_new (const gchar *uri)
/* FIXME: Set initial container format from file extension parsing */
GST_TRACE_OBJECT (item, "New media item, ID: %u, URI: %s, title: %s",
item->id, item->uri, item->title);
GST_TRACE_OBJECT (item, "New media item, ID: %u, URI: \"%s\", title: \"%s\"",
item->id, item->uri, GST_STR_NULL (item->title));
return item;
}
@@ -258,27 +267,6 @@ clapper_media_item_get_suburi (ClapperMediaItem *self)
return suburi;
}
static gboolean
clapper_media_item_take_title (ClapperMediaItem *self, gchar *title,
ClapperAppBus *app_bus)
{
gboolean changed;
GST_OBJECT_LOCK (self);
if ((changed = g_strcmp0 (self->title, title) != 0)) {
g_free (self->title);
self->title = title;
}
GST_OBJECT_UNLOCK (self);
if (changed)
clapper_app_bus_post_prop_notify (app_bus, GST_OBJECT_CAST (self), param_specs[PROP_TITLE]);
else
g_free (title);
return changed;
}
/**
* clapper_media_item_get_title:
* @item: a #ClapperMediaItem
@@ -305,25 +293,24 @@ clapper_media_item_get_title (ClapperMediaItem *self)
return title;
}
static gboolean
clapper_media_item_take_container_format (ClapperMediaItem *self, gchar *container_format,
ClapperAppBus *app_bus)
static inline gboolean
_refresh_tag_prop_unlocked (ClapperMediaItem *self, const gchar *tag,
gboolean from_user, gchar **tag_ptr)
{
gboolean changed;
const gchar *string;
GST_OBJECT_LOCK (self);
if ((changed = g_strcmp0 (self->container_format, container_format) != 0)) {
g_free (self->container_format);
self->container_format = container_format;
}
GST_OBJECT_UNLOCK (self);
if ((*tag_ptr && from_user) // if already set, user cannot modify it
|| !gst_tag_list_peek_string_index (self->tags, tag, 0, &string) // guarantees non-empty string
|| (g_strcmp0 (*tag_ptr, string) == 0))
return FALSE;
if (changed)
clapper_app_bus_post_prop_notify (app_bus, GST_OBJECT_CAST (self), param_specs[PROP_CONTAINER_FORMAT]);
else
g_free (container_format);
GST_LOG_OBJECT (self, "Tag prop \"%s\" update: \"%s\" -> \"%s\"",
tag, GST_STR_NULL (*tag_ptr), string);
return changed;
g_free (*tag_ptr);
*tag_ptr = g_strdup (string);
return TRUE;
}
/**
@@ -333,6 +320,8 @@ clapper_media_item_take_container_format (ClapperMediaItem *self, gchar *contain
* Get media item container format.
*
* Returns: (transfer full) (nullable): media container format.
*
* Deprecated: 0.10: Get `container-format` from [property@Clapper.MediaItem:tags] instead.
*/
gchar *
clapper_media_item_get_container_format (ClapperMediaItem *self)
@@ -389,11 +378,214 @@ clapper_media_item_get_duration (ClapperMediaItem *self)
return duration;
}
/**
* clapper_media_item_get_tags:
* @item: a #ClapperMediaItem
*
* Get readable list of tags stored in media item.
*
* Returns: (transfer full): a #GstTagList.
*
* Since: 0.10
*/
GstTagList *
clapper_media_item_get_tags (ClapperMediaItem *self)
{
GstTagList *tags = NULL;
g_return_val_if_fail (CLAPPER_IS_MEDIA_ITEM (self), NULL);
GST_OBJECT_LOCK (self);
tags = gst_tag_list_ref (self->tags);
GST_OBJECT_UNLOCK (self);
return tags;
}
static void
_tags_replace_func (const GstTagList *tags, const gchar *tag, ClapperMediaItemTagIterData *data)
{
ClapperMediaItem *self = data->item;
guint index = 0;
gboolean replace = FALSE;
while (TRUE) {
const GValue *old_value = gst_tag_list_get_value_index (self->tags, tag, index);
const GValue *new_value = gst_tag_list_get_value_index (tags, tag, index);
/* Number of old values is the same or greater and
* all values until this iteration were the same */
if (!new_value)
break;
/* A wild new tag appeared */
if (!old_value) {
replace = TRUE;
break;
}
/* Users can only set non-existing tags */
if (data->from_user)
break;
/* Check with tolerance for doubles */
if (G_VALUE_TYPE (old_value) == G_TYPE_DOUBLE
&& G_VALUE_TYPE (new_value) == G_TYPE_DOUBLE) {
gdouble old_dbl, new_dbl;
old_dbl = g_value_get_double (old_value);
new_dbl = g_value_get_double (new_value);
if ((replace = !G_APPROX_VALUE (old_dbl, new_dbl, FLT_EPSILON)))
break;
} else if (gst_value_compare (old_value, new_value) != GST_VALUE_EQUAL) {
replace = TRUE;
break;
}
++index;
}
if (replace) {
const GValue *value;
index = 0;
GST_LOG_OBJECT (self, "Replacing \"%s\" tag value", tag);
/* Ensure writable, but only when replacing something */
if (!data->changed) {
self->tags = gst_tag_list_make_writable (self->tags);
data->changed = TRUE;
}
/* Replace first tag value (so it becomes sole member) */
value = gst_tag_list_get_value_index (tags, tag, index);
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_REPLACE, tag, value);
/* Append any remaining tags (so next time we iterate indexes will match) */
while ((value = gst_tag_list_get_value_index (tags, tag, ++index)))
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_APPEND, tag, value);
}
}
static gboolean
clapper_media_item_insert_tags_internal (ClapperMediaItem *self, const GstTagList *tags,
ClapperAppBus *app_bus, gboolean from_user, ClapperReactableItemUpdatedFlags *flags)
{
ClapperMediaItemTagIterData data;
gboolean title_changed = FALSE, cont_changed = FALSE;
GST_OBJECT_LOCK (self);
data.item = self;
data.changed = FALSE;
data.from_user = from_user;
if (G_LIKELY (tags != self->tags))
gst_tag_list_foreach (tags, (GstTagForeachFunc) _tags_replace_func, &data);
if (data.changed) {
*flags |= CLAPPER_REACTABLE_ITEM_UPDATED_TAGS;
if ((title_changed = _refresh_tag_prop_unlocked (self, GST_TAG_TITLE,
from_user, &self->title))) {
*flags |= CLAPPER_REACTABLE_ITEM_UPDATED_TITLE;
}
cont_changed = _refresh_tag_prop_unlocked (self, GST_TAG_CONTAINER_FORMAT,
from_user, &self->container_format);
}
GST_OBJECT_UNLOCK (self);
if (!data.changed)
return FALSE;
if (app_bus) {
GstObject *src = GST_OBJECT_CAST (self);
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_TAGS]);
if (title_changed)
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_TITLE]);
if (cont_changed)
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_CONTAINER_FORMAT]);
} else {
GObject *src = G_OBJECT (self);
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_TAGS]);
if (title_changed)
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_TITLE]);
if (cont_changed)
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_CONTAINER_FORMAT]);
}
return TRUE;
}
/**
* clapper_media_item_populate_tags:
* @item: a #ClapperMediaItem
* @tags: a #GstTagList of GLOBAL scope
*
* Populate non-existing tags in @item tag list.
*
* Passed @tags must use [enum@Gst.TagScope.GLOBAL] scope.
*
* Note that tags are automatically determined during media playback
* and those take precedence. This function can be useful if an app can
* determine some tags that are not in media metadata or for filling
* item with some initial/cached tags to display in UI before playback.
*
* When a tag already exists in the tag list (was populated) this
* function will not overwrite it. If you really need to permanently
* override some tags in media, you can use `taginject` element as
* player video/audio filter instead.
*
* Returns: whether at least one tag got updated.
*
* Since: 0.10
*/
gboolean
clapper_media_item_populate_tags (ClapperMediaItem *self, const GstTagList *tags)
{
ClapperPlayer *player;
ClapperAppBus *app_bus = NULL;
ClapperReactableItemUpdatedFlags flags = 0;
gboolean changed;
g_return_val_if_fail (CLAPPER_IS_MEDIA_ITEM (self), FALSE);
g_return_val_if_fail (tags != NULL, FALSE);
if (G_UNLIKELY (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_GLOBAL)) {
g_warning ("Cannot populate media item tags using a list with non-global tag scope");
return FALSE;
}
if ((player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self))))
app_bus = player->app_bus;
changed = clapper_media_item_insert_tags_internal (self, tags, app_bus, TRUE, &flags);
if (changed && player) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
gst_clear_object (&player);
return changed;
}
/**
* clapper_media_item_get_timeline:
* @item: a #ClapperMediaItem
*
* Get the [class@Clapper.Timeline] assosiated with @item.
* Get the [class@Clapper.Timeline] associated with @item.
*
* Returns: (transfer none): a #ClapperTimeline of item.
*/
@@ -405,21 +597,6 @@ clapper_media_item_get_timeline (ClapperMediaItem *self)
return self->timeline;
}
static gboolean
clapper_media_item_update_from_container_tags (ClapperMediaItem *self, const GstTagList *tags,
ClapperAppBus *app_bus)
{
gchar *string = NULL;
gboolean changed = FALSE;
if (gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &string))
changed |= clapper_media_item_take_container_format (self, string, app_bus);
if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &string))
changed |= clapper_media_item_take_title (self, string, app_bus);
return changed;
}
void
clapper_media_item_update_from_tag_list (ClapperMediaItem *self, const GstTagList *tags,
ClapperPlayer *player)
@@ -427,11 +604,14 @@ clapper_media_item_update_from_tag_list (ClapperMediaItem *self, const GstTagLis
GstTagScope scope = gst_tag_list_get_scope (tags);
if (scope == GST_TAG_SCOPE_GLOBAL) {
gboolean changed = clapper_media_item_update_from_container_tags (self, tags, player->app_bus);
ClapperReactableItemUpdatedFlags flags = 0;
gboolean changed = clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE, &flags);
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -444,6 +624,7 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
ClapperPlayer *player;
GstDiscovererStreamInfo *sinfo;
GstClockTime duration;
ClapperReactableItemUpdatedFlags flags = 0;
gdouble val_dbl;
gboolean changed = FALSE;
@@ -459,7 +640,7 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
GstDiscovererContainerInfo *cinfo = (GstDiscovererContainerInfo *) sinfo;
if ((tags = gst_discoverer_container_info_get_tags (cinfo)))
changed |= clapper_media_item_update_from_container_tags (self, tags, player->app_bus);
changed |= clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE, &flags);
}
gst_discoverer_stream_info_unref (sinfo);
}
@@ -470,11 +651,16 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
duration = 0;
val_dbl = (gdouble) duration / GST_SECOND;
changed |= clapper_media_item_set_duration (self, val_dbl, player->app_bus);
if (clapper_media_item_set_duration (self, val_dbl, player->app_bus)) {
changed = TRUE;
flags |= CLAPPER_REACTABLE_ITEM_UPDATED_DURATION;
}
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -542,6 +728,9 @@ clapper_media_item_get_used (ClapperMediaItem *self)
static void
clapper_media_item_init (ClapperMediaItem *self)
{
self->tags = gst_tag_list_new_empty ();
gst_tag_list_set_scope (self->tags, GST_TAG_SCOPE_GLOBAL);
self->timeline = clapper_timeline_new ();
gst_object_set_parent (GST_OBJECT_CAST (self->timeline), GST_OBJECT_CAST (self));
}
@@ -571,6 +760,8 @@ clapper_media_item_finalize (GObject *object)
g_free (self->title);
g_free (self->container_format);
gst_tag_list_unref (self->tags);
gst_object_unparent (GST_OBJECT_CAST (self->timeline));
gst_object_unref (self->timeline);
@@ -617,6 +808,9 @@ clapper_media_item_get_property (GObject *object, guint prop_id,
case PROP_SUBURI:
g_value_take_string (value, clapper_media_item_get_suburi (self));
break;
case PROP_TAGS:
g_value_take_boxed (value, clapper_media_item_get_tags (self));
break;
case PROP_TITLE:
g_value_take_string (value, clapper_media_item_get_title (self));
break;
@@ -686,10 +880,27 @@ clapper_media_item_class_init (ClapperMediaItemClass *klass)
NULL, NULL, NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperMediaItem:tags:
*
* A readable list of tags stored in media item.
*
* Since: 0.10
*/
param_specs[PROP_TAGS] = g_param_spec_boxed ("tags",
NULL, NULL, GST_TYPE_TAG_LIST,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/* FIXME: 1.0: Consider rename to e.g. "(menu/display)-title"
* and also make it non-nullable (return URI as final fallback) */
/**
* ClapperMediaItem:title:
*
* Media title.
*
* This might be a different string compared to `title` from
* [property@Clapper.MediaItem:tags], as this gives parsed
* title from file name/URI as fallback when no `title` tag.
*/
param_specs[PROP_TITLE] = g_param_spec_string ("title",
NULL, NULL, NULL,
@@ -699,15 +910,21 @@ clapper_media_item_class_init (ClapperMediaItemClass *klass)
* ClapperMediaItem:container-format:
*
* Media container format.
*
* Deprecated: 0.10: Get `container-format` from [property@Clapper.MediaItem:tags] instead.
*/
param_specs[PROP_CONTAINER_FORMAT] = g_param_spec_string ("container-format",
NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED);
/**
* ClapperMediaItem:duration:
*
* Media duration as a decimal number in seconds.
*
* This might be a different value compared to `duration` from
* [property@Clapper.MediaItem:tags], as this value is updated
* during decoding instead of being a fixed value from metadata.
*/
param_specs[PROP_DURATION] = g_param_spec_double ("duration",
NULL, NULL, 0, G_MAXDOUBLE, 0,

View File

@@ -12,9 +12,8 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
* License along with this library; if not, see
* <https://www.gnu.org/licenses/>.
*/
#pragma once
@@ -27,6 +26,7 @@
#include <glib-object.h>
#include <gio/gio.h>
#include <gst/gst.h>
#include <gst/tag/tag.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-timeline.h>
@@ -63,12 +63,18 @@ gchar * clapper_media_item_get_suburi (ClapperMediaItem *item);
CLAPPER_API
gchar * clapper_media_item_get_title (ClapperMediaItem *item);
CLAPPER_API
CLAPPER_DEPRECATED_FOR(clapper_media_item_get_tags)
gchar * clapper_media_item_get_container_format (ClapperMediaItem *item);
CLAPPER_API
gdouble clapper_media_item_get_duration (ClapperMediaItem *item);
CLAPPER_API
GstTagList * clapper_media_item_get_tags (ClapperMediaItem *item);
CLAPPER_API
gboolean clapper_media_item_populate_tags (ClapperMediaItem *item, const GstTagList *tags);
CLAPPER_API
ClapperTimeline * clapper_media_item_get_timeline (ClapperMediaItem *item);

Some files were not shown because too many files have changed in this diff Show More