mirror of
https://github.com/Rafostar/clapper.git
synced 2025-09-03 01:41:58 +02:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2076309aaa | ||
|
79618edd1e |
@@ -10,9 +10,6 @@ scrolledwindow scrollbar.vertical slider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Adwaita is missing osd ListBox */
|
/* Adwaita is missing osd ListBox */
|
||||||
.clapperplaylist {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
.clapperplaylist row {
|
.clapperplaylist row {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
@@ -31,6 +28,9 @@ scrolledwindow scrollbar.vertical slider {
|
|||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
.osd .clapperplaylist {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
.osd .clapperplaylist row image {
|
.osd .clapperplaylist row image {
|
||||||
-gtk-icon-shadow: none;
|
-gtk-icon-shadow: none;
|
||||||
}
|
}
|
||||||
@@ -223,9 +223,6 @@ scale trough slider {
|
|||||||
.fullscreen.tvmode .positionscale marks.bottom {
|
.fullscreen.tvmode .positionscale marks.bottom {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
.fullscreen.tvmode .positionscale trough {
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
.fullscreen.tvmode .positionscale trough highlight {
|
.fullscreen.tvmode .positionscale trough highlight {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
|
@@ -103,14 +103,14 @@
|
|||||||
<summary>Set PlayFlags for playbin</summary>
|
<summary>Set PlayFlags for playbin</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<!-- YouTube -->
|
<!-- Gtuber -->
|
||||||
<key name="yt-adaptive-enabled" type="b">
|
<key name="yt-adaptive-enabled" type="b">
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<summary>Enable to use adaptive streaming for YouTube</summary>
|
<summary>Enable to use adaptive streaming</summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="yt-quality-type" type="i">
|
<key name="yt-quality-type" type="i">
|
||||||
<default>1</default>
|
<default>1</default>
|
||||||
<summary>Max YouTube video quality type</summary>
|
<summary>Max online video quality type</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<!-- Other -->
|
<!-- Other -->
|
||||||
|
@@ -48,34 +48,6 @@
|
|||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="0.4.1" date="2021-12-20">
|
|
||||||
<description>
|
|
||||||
<p>Fixes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Compatibility with more recent libadwaita versions</li>
|
|
||||||
<li>Toggle mute with M button alone</li>
|
|
||||||
<li>Allow handling YouTube with external GStreamer plugins</li>
|
|
||||||
<li>Fix catching errors when reading clipboard</li>
|
|
||||||
<li>Fix missing translator-credits</li>
|
|
||||||
<li>Fix missing gio-unix-2.0 dep</li>
|
|
||||||
<li>Fix playback pausing when entering fullscreen with touchscreen</li>
|
|
||||||
<li>Fix GST_PLUGIN_FEATURE_RANK env usage</li>
|
|
||||||
<li>Fix video/audio decoder change detection</li>
|
|
||||||
<li>Merge global video tags instead replacing them</li>
|
|
||||||
<li>Few other misc bug fixes</li>
|
|
||||||
</ul>
|
|
||||||
<p>New translations:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Chinese Simplified</li>
|
|
||||||
<li>Czech</li>
|
|
||||||
<li>Hungarian</li>
|
|
||||||
<li>Portuguese</li>
|
|
||||||
<li>Portuguese, Brazilian</li>
|
|
||||||
<li>Russian</li>
|
|
||||||
<li>Spanish</li>
|
|
||||||
</ul>
|
|
||||||
</description>
|
|
||||||
</release>
|
|
||||||
<release version="0.4.0" date="2021-09-12">
|
<release version="0.4.0" date="2021-09-12">
|
||||||
<description>
|
<description>
|
||||||
<p>Changes:</p>
|
<p>Changes:</p>
|
||||||
|
291
lib/gst/clapper/gstclapper.c
vendored
291
lib/gst/clapper/gstclapper.c
vendored
@@ -253,9 +253,6 @@ static void gst_clapper_audio_info_update (GstClapper * self,
|
|||||||
static void gst_clapper_subtitle_info_update (GstClapper * self,
|
static void gst_clapper_subtitle_info_update (GstClapper * self,
|
||||||
GstClapperStreamInfo * stream_info);
|
GstClapperStreamInfo * stream_info);
|
||||||
|
|
||||||
static gboolean find_active_decoder_with_stream_id (GstClapper * self,
|
|
||||||
GstElementFactoryListType type, const gchar * stream_id);
|
|
||||||
|
|
||||||
/* For playbin3 */
|
/* For playbin3 */
|
||||||
static void gst_clapper_streams_info_create_from_collection (GstClapper * self,
|
static void gst_clapper_streams_info_create_from_collection (GstClapper * self,
|
||||||
GstClapperMediaInfo * media_info, GstStreamCollection * collection);
|
GstClapperMediaInfo * media_info, GstStreamCollection * collection);
|
||||||
@@ -1865,15 +1862,6 @@ media_info_update (GstClapper * self, GstClapperMediaInfo * info)
|
|||||||
"image_sample: %p", info->title, info->container, info->image_sample);
|
"image_sample: %p", info->title, info->container, info->image_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
merge_tags (GstTagList **my_tags, GstTagList *tags)
|
|
||||||
{
|
|
||||||
if (*my_tags)
|
|
||||||
gst_tag_list_insert (*my_tags, tags, GST_TAG_MERGE_REPLACE);
|
|
||||||
else
|
|
||||||
*my_tags = gst_tag_list_ref (tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
|
tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
|
||||||
{
|
{
|
||||||
@@ -1889,12 +1877,17 @@ tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
|
|||||||
if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_GLOBAL) {
|
if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_GLOBAL) {
|
||||||
g_mutex_lock (&self->lock);
|
g_mutex_lock (&self->lock);
|
||||||
if (self->media_info) {
|
if (self->media_info) {
|
||||||
merge_tags (&self->media_info->tags, tags);
|
if (self->media_info->tags)
|
||||||
|
gst_tag_list_unref (self->media_info->tags);
|
||||||
|
self->media_info->tags = gst_tag_list_ref (tags);
|
||||||
media_info_update (self, self->media_info);
|
media_info_update (self, self->media_info);
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
} else {
|
} else {
|
||||||
merge_tags (&self->global_tags, tags);
|
if (self->global_tags)
|
||||||
|
gst_tag_list_unref (self->global_tags);
|
||||||
|
self->global_tags = gst_tag_list_ref (tags);
|
||||||
|
g_mutex_unlock (&self->lock);
|
||||||
}
|
}
|
||||||
g_mutex_unlock (&self->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_tag_list_unref (tags);
|
gst_tag_list_unref (tags);
|
||||||
@@ -2057,7 +2050,6 @@ streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
|
|||||||
{
|
{
|
||||||
GstClapper *self = GST_CLAPPER (user_data);
|
GstClapper *self = GST_CLAPPER (user_data);
|
||||||
GstStreamCollection *collection = NULL;
|
GstStreamCollection *collection = NULL;
|
||||||
gchar *video_sid, *audio_sid;
|
|
||||||
guint i, len;
|
guint i, len;
|
||||||
|
|
||||||
gst_message_parse_streams_selected (msg, &collection);
|
gst_message_parse_streams_selected (msg, &collection);
|
||||||
@@ -2106,22 +2098,7 @@ streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
|
|||||||
|
|
||||||
*current_sid = g_strdup (stream_id);
|
*current_sid = g_strdup (stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
video_sid = g_strdup (self->video_sid);
|
|
||||||
audio_sid = g_strdup (self->audio_sid);
|
|
||||||
|
|
||||||
g_mutex_unlock (&self->lock);
|
g_mutex_unlock (&self->lock);
|
||||||
|
|
||||||
if (video_sid) {
|
|
||||||
find_active_decoder_with_stream_id (self, GST_ELEMENT_FACTORY_TYPE_DECODER
|
|
||||||
| GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, video_sid);
|
|
||||||
g_free (video_sid);
|
|
||||||
}
|
|
||||||
if (audio_sid) {
|
|
||||||
find_active_decoder_with_stream_id (self, GST_ELEMENT_FACTORY_TYPE_DECODER
|
|
||||||
| GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, audio_sid);
|
|
||||||
g_free (audio_sid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -3032,12 +3009,11 @@ decoder_changed_signal_data_free (DecoderChangedSignalData * data)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
emit_decoder_changed (GstClapper * self, gchar * decoder_name,
|
emit_decoder_changed (GstClapper * self, gchar * decoder_name,
|
||||||
GstElementFactoryListType type)
|
gboolean is_video)
|
||||||
{
|
{
|
||||||
GstClapperSignalDispatcherFunc func = NULL;
|
GstClapperSignalDispatcherFunc func = NULL;
|
||||||
|
|
||||||
if ((type & GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO) ==
|
if (is_video) {
|
||||||
GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO) {
|
|
||||||
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
|
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
|
||||||
signals[SIGNAL_VIDEO_DECODER_CHANGED], 0, NULL, NULL, NULL) != 0 &&
|
signals[SIGNAL_VIDEO_DECODER_CHANGED], 0, NULL, NULL, NULL) != 0 &&
|
||||||
g_strcmp0 (self->last_vdecoder, decoder_name) != 0) {
|
g_strcmp0 (self->last_vdecoder, decoder_name) != 0) {
|
||||||
@@ -3045,8 +3021,7 @@ emit_decoder_changed (GstClapper * self, gchar * decoder_name,
|
|||||||
g_free (self->last_vdecoder);
|
g_free (self->last_vdecoder);
|
||||||
self->last_vdecoder = g_strdup (decoder_name);
|
self->last_vdecoder = g_strdup (decoder_name);
|
||||||
}
|
}
|
||||||
} else if ((type & GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO) ==
|
} else {
|
||||||
GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO) {
|
|
||||||
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
|
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
|
||||||
signals[SIGNAL_AUDIO_DECODER_CHANGED], 0, NULL, NULL, NULL) != 0 &&
|
signals[SIGNAL_AUDIO_DECODER_CHANGED], 0, NULL, NULL, NULL) != 0 &&
|
||||||
g_strcmp0 (self->last_adecoder, decoder_name) != 0) {
|
g_strcmp0 (self->last_adecoder, decoder_name) != 0) {
|
||||||
@@ -3067,138 +3042,6 @@ emit_decoder_changed (GstClapper * self, gchar * decoder_name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
iterate_decoder_pads (GstClapper * self, GstElement * element,
|
|
||||||
const gchar * stream_id, GstElementFactoryListType type)
|
|
||||||
{
|
|
||||||
GstIterator *iter;
|
|
||||||
GValue value = { 0, };
|
|
||||||
gboolean found = FALSE;
|
|
||||||
|
|
||||||
iter = gst_element_iterate_src_pads (element);
|
|
||||||
|
|
||||||
while (gst_iterator_next (iter, &value) == GST_ITERATOR_OK) {
|
|
||||||
GstPad *decoder_pad = g_value_get_object (&value);
|
|
||||||
gchar *decoder_stream_id = gst_pad_get_stream_id (decoder_pad);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "Decoder stream: %s", decoder_stream_id);
|
|
||||||
|
|
||||||
/* In case of playbin3, pad may not be active yet */
|
|
||||||
if ((found = (g_strcmp0 (decoder_stream_id, stream_id) == 0
|
|
||||||
|| (!decoder_stream_id && self->use_playbin3)))) {
|
|
||||||
GstElementFactory *factory;
|
|
||||||
gchar *plugin_name;
|
|
||||||
|
|
||||||
factory = gst_element_get_factory (element);
|
|
||||||
plugin_name = gst_object_get_name (GST_OBJECT_CAST (factory));
|
|
||||||
|
|
||||||
if (plugin_name) {
|
|
||||||
GST_DEBUG_OBJECT (self, "Found decoder: %s", plugin_name);
|
|
||||||
emit_decoder_changed (self, plugin_name, type);
|
|
||||||
|
|
||||||
g_free (plugin_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (decoder_stream_id);
|
|
||||||
g_value_unset (&value);
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_iterator_free (iter);
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
find_active_decoder_with_stream_id (GstClapper * self, GstElementFactoryListType type,
|
|
||||||
const gchar * stream_id)
|
|
||||||
{
|
|
||||||
GstIterator *iter;
|
|
||||||
GValue value = { 0, };
|
|
||||||
gboolean found = FALSE;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "Searching for decoder with stream: %s", stream_id);
|
|
||||||
|
|
||||||
iter = gst_bin_iterate_recurse (GST_BIN (self->playbin));
|
|
||||||
|
|
||||||
while (gst_iterator_next (iter, &value) == GST_ITERATOR_OK) {
|
|
||||||
GstElement *element = g_value_get_object (&value);
|
|
||||||
GstElementFactory *factory = gst_element_get_factory (element);
|
|
||||||
|
|
||||||
if (factory && gst_element_factory_list_is_type (factory, type))
|
|
||||||
found = iterate_decoder_pads (self, element, stream_id, type);
|
|
||||||
|
|
||||||
g_value_unset (&value);
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_iterator_free (iter);
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
update_current_decoder (GstClapper *self, GstElementFactoryListType type)
|
|
||||||
{
|
|
||||||
GstIterator *iter;
|
|
||||||
GValue value = { 0, };
|
|
||||||
|
|
||||||
iter = gst_bin_iterate_all_by_element_factory_name (
|
|
||||||
GST_BIN (self->playbin), "input-selector");
|
|
||||||
|
|
||||||
while (gst_iterator_next (iter, &value) == GST_ITERATOR_OK) {
|
|
||||||
GstElement *element = g_value_get_object (&value);
|
|
||||||
GstPad *active_pad;
|
|
||||||
gboolean found = FALSE;
|
|
||||||
|
|
||||||
g_object_get (G_OBJECT (element), "active-pad", &active_pad, NULL);
|
|
||||||
|
|
||||||
if (active_pad) {
|
|
||||||
gchar *stream_id;
|
|
||||||
|
|
||||||
stream_id = gst_pad_get_stream_id (active_pad);
|
|
||||||
gst_object_unref (active_pad);
|
|
||||||
|
|
||||||
if (stream_id) {
|
|
||||||
found = find_active_decoder_with_stream_id (self, type, stream_id);
|
|
||||||
g_free (stream_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_value_unset (&value);
|
|
||||||
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_iterator_free (iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
current_video_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
|
|
||||||
GstClapper * self)
|
|
||||||
{
|
|
||||||
GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_DECODER
|
|
||||||
| GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO;
|
|
||||||
|
|
||||||
update_current_decoder (self, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
current_audio_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
|
|
||||||
GstClapper * self)
|
|
||||||
{
|
|
||||||
GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_DECODER
|
|
||||||
| GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
|
|
||||||
|
|
||||||
update_current_decoder (self, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
element_setup_cb (GstElement * playbin, GstElement * element, GstClapper * self)
|
element_setup_cb (GstElement * playbin, GstElement * element, GstClapper * self)
|
||||||
{
|
{
|
||||||
@@ -3211,6 +3054,13 @@ element_setup_cb (GstElement * playbin, GstElement * element, GstClapper * self)
|
|||||||
if (plugin_name) {
|
if (plugin_name) {
|
||||||
GST_INFO_OBJECT (self, "Plugin setup: %s", plugin_name);
|
GST_INFO_OBJECT (self, "Plugin setup: %s", plugin_name);
|
||||||
|
|
||||||
|
if (gst_element_factory_list_is_type (factory,
|
||||||
|
GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO))
|
||||||
|
emit_decoder_changed (self, plugin_name, TRUE);
|
||||||
|
else if (gst_element_factory_list_is_type (factory,
|
||||||
|
GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO))
|
||||||
|
emit_decoder_changed (self, plugin_name, FALSE);
|
||||||
|
|
||||||
/* TODO: Set plugin props */
|
/* TODO: Set plugin props */
|
||||||
}
|
}
|
||||||
g_free (plugin_name);
|
g_free (plugin_name);
|
||||||
@@ -3379,11 +3229,6 @@ gst_clapper_main (gpointer data)
|
|||||||
G_CALLBACK (audio_tags_changed_cb), self);
|
G_CALLBACK (audio_tags_changed_cb), self);
|
||||||
g_signal_connect (self->playbin, "text-tags-changed",
|
g_signal_connect (self->playbin, "text-tags-changed",
|
||||||
G_CALLBACK (subtitle_tags_changed_cb), self);
|
G_CALLBACK (subtitle_tags_changed_cb), self);
|
||||||
|
|
||||||
g_signal_connect (self->playbin, "notify::current-video",
|
|
||||||
G_CALLBACK (current_video_notify_cb), self);
|
|
||||||
g_signal_connect (self->playbin, "notify::current-audio",
|
|
||||||
G_CALLBACK (current_audio_notify_cb), self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_signal_connect (self->playbin, "notify::volume",
|
g_signal_connect (self->playbin, "notify::volume",
|
||||||
@@ -3479,103 +3324,6 @@ gst_clapper_has_plugin_with_features (const gchar * name)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
parse_feature_name (gchar * str, const gchar ** feature)
|
|
||||||
{
|
|
||||||
if (!str)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
g_strstrip (str);
|
|
||||||
|
|
||||||
if (str[0] != '\0') {
|
|
||||||
*feature = str;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
parse_feature_rank (gchar * str, GstRank * rank)
|
|
||||||
{
|
|
||||||
if (!str)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
g_strstrip (str);
|
|
||||||
|
|
||||||
if (g_ascii_isdigit (str[0])) {
|
|
||||||
unsigned long l;
|
|
||||||
char *endptr;
|
|
||||||
l = strtoul (str, &endptr, 10);
|
|
||||||
if (endptr > str && endptr[0] == 0) {
|
|
||||||
*rank = (GstRank) l;
|
|
||||||
} else {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
} else if (g_ascii_strcasecmp (str, "NONE") == 0) {
|
|
||||||
*rank = GST_RANK_NONE;
|
|
||||||
} else if (g_ascii_strcasecmp (str, "MARGINAL") == 0) {
|
|
||||||
*rank = GST_RANK_MARGINAL;
|
|
||||||
} else if (g_ascii_strcasecmp (str, "SECONDARY") == 0) {
|
|
||||||
*rank = GST_RANK_SECONDARY;
|
|
||||||
} else if (g_ascii_strcasecmp (str, "PRIMARY") == 0) {
|
|
||||||
*rank = GST_RANK_PRIMARY;
|
|
||||||
} else if (g_ascii_strcasecmp (str, "MAX") == 0) {
|
|
||||||
*rank = (GstRank) G_MAXINT;
|
|
||||||
} else {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_env_feature_rank_update (void)
|
|
||||||
{
|
|
||||||
const gchar *env;
|
|
||||||
gchar **split, **walk;
|
|
||||||
|
|
||||||
env = g_getenv ("GST_PLUGIN_FEATURE_RANK");
|
|
||||||
|
|
||||||
if (!env)
|
|
||||||
return;
|
|
||||||
|
|
||||||
split = g_strsplit (env, ",", 0);
|
|
||||||
|
|
||||||
for (walk = split; *walk; walk++) {
|
|
||||||
if (strchr (*walk, ':')) {
|
|
||||||
gchar **values;
|
|
||||||
|
|
||||||
values = g_strsplit (*walk, ":", 2);
|
|
||||||
if (values[0] && values[1]) {
|
|
||||||
GstRank rank;
|
|
||||||
const gchar *name;
|
|
||||||
|
|
||||||
if (parse_feature_name (values[0], &name)
|
|
||||||
&& parse_feature_rank (values[1], &rank)) {
|
|
||||||
GstPluginFeature *feature;
|
|
||||||
|
|
||||||
feature = gst_registry_find_feature (gst_registry_get (), name,
|
|
||||||
GST_TYPE_ELEMENT_FACTORY);
|
|
||||||
if (feature) {
|
|
||||||
GstRank old_rank;
|
|
||||||
|
|
||||||
old_rank = gst_plugin_feature_get_rank (feature);
|
|
||||||
if (old_rank != rank) {
|
|
||||||
gst_plugin_feature_set_rank (feature, rank);
|
|
||||||
GST_DEBUG ("Updated rank from env: %i -> %i for %s", old_rank, rank, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev (values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev (split);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_clapper_prepare_gstreamer (void)
|
gst_clapper_prepare_gstreamer (void)
|
||||||
{
|
{
|
||||||
@@ -3598,9 +3346,6 @@ gst_clapper_prepare_gstreamer (void)
|
|||||||
gst_clapper_set_feature_rank ("v4l2slvp8dec", GST_RANK_NONE);
|
gst_clapper_set_feature_rank ("v4l2slvp8dec", GST_RANK_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After setting defaults, update them from ENV */
|
|
||||||
_env_feature_rank_update ();
|
|
||||||
|
|
||||||
gst_clapper_gstreamer_prepared = TRUE;
|
gst_clapper_gstreamer_prepared = TRUE;
|
||||||
GST_DEBUG ("GStreamer plugins prepared");
|
GST_DEBUG ("GStreamer plugins prepared");
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
project('com.github.rafostar.Clapper', 'c', 'cpp',
|
project('com.github.rafostar.Clapper', 'c', 'cpp',
|
||||||
version: '0.4.1',
|
version: '0.4.0',
|
||||||
meson_version: '>= 0.50.0',
|
meson_version: '>= 0.50.0',
|
||||||
license: 'GPL-3.0-or-later',
|
license: 'GPL-3.0-or-later',
|
||||||
default_options: [
|
default_options: [
|
||||||
|
@@ -23,7 +23,6 @@
|
|||||||
"-Dintrospection=enabled",
|
"-Dintrospection=enabled",
|
||||||
"-Ddoc=disabled",
|
"-Ddoc=disabled",
|
||||||
"-Dgtk_doc=disabled",
|
"-Dgtk_doc=disabled",
|
||||||
"-Dgpl=enabled",
|
|
||||||
|
|
||||||
"-Dgstreamer:benchmarks=disabled",
|
"-Dgstreamer:benchmarks=disabled",
|
||||||
"-Dgstreamer:gobject-cast-checks=disabled",
|
"-Dgstreamer:gobject-cast-checks=disabled",
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
"name": "gtuber",
|
"name": "gtuber",
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
"config-opts": [
|
"config-opts": [
|
||||||
"-Dintrospection=disabled",
|
"-Dvapi=disabled"
|
||||||
"-Dvapi=disabled",
|
|
||||||
"-Dgst-gtuber=enabled"
|
|
||||||
],
|
],
|
||||||
"cleanup": [
|
"cleanup": [
|
||||||
"/include",
|
"/include",
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
%global glib2_version 2.56.0
|
%global glib2_version 2.56.0
|
||||||
|
|
||||||
Name: clapper
|
Name: clapper
|
||||||
Version: 0.4.1
|
Version: 0.4.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Simple and modern GNOME media player
|
Summary: Simple and modern GNOME media player
|
||||||
|
|
||||||
@@ -129,9 +129,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
|
|||||||
%{_libdir}/%{appname}/
|
%{_libdir}/%{appname}/
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Mon Dec 20 2021 Rafostar <rafostar.github@gmail.com> - 0.4.1-1
|
|
||||||
- New version
|
|
||||||
|
|
||||||
* Sun Sep 12 2021 Rafostar <rafostar.github@gmail.com> - 0.4.0-1
|
* Sun Sep 12 2021 Rafostar <rafostar.github@gmail.com> - 0.4.0-1
|
||||||
- New version
|
- New version
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
ca cs de es hu it nl pl pt pt_BR ru zh_CN
|
ca cs de es hu it nl pl pt_BR ru zh_CN
|
||||||
|
44
po/pt.po
44
po/pt.po
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: clapper\n"
|
"Project-Id-Version: clapper\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
"POT-Creation-Date: 2021-09-14 16:35+0200\n"
|
||||||
"PO-Revision-Date: 2021-10-21 00:29\n"
|
"PO-Revision-Date: 2021-09-14 15:25\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Portuguese\n"
|
"Language-Team: Portuguese\n"
|
||||||
"Language: pt_PT\n"
|
"Language: pt_PT\n"
|
||||||
@@ -19,88 +19,88 @@ msgstr ""
|
|||||||
|
|
||||||
#: ui/clapper.ui:6
|
#: ui/clapper.ui:6
|
||||||
msgid "Open Files..."
|
msgid "Open Files..."
|
||||||
msgstr "Abrir Arquivos..."
|
msgstr ""
|
||||||
|
|
||||||
#: ui/clapper.ui:10
|
#: ui/clapper.ui:10
|
||||||
msgid "Open URI..."
|
msgid "Open URI..."
|
||||||
msgstr "Abrir URI..."
|
msgstr ""
|
||||||
|
|
||||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr "Preferências"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/clapper.ui:20
|
#: ui/clapper.ui:20
|
||||||
msgid "Shortcuts"
|
msgid "Shortcuts"
|
||||||
msgstr "Atalhos"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/clapper.ui:26
|
#: ui/clapper.ui:26
|
||||||
msgid "About Clapper"
|
msgid "About Clapper"
|
||||||
msgstr "Sobre o Clapper"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/elapsed-time-button.ui:27
|
#: ui/elapsed-time-button.ui:27
|
||||||
msgid "Speed"
|
msgid "Speed"
|
||||||
msgstr "Velocidade"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:83
|
||||||
#: ui/preferences-window.ui:215
|
#: ui/preferences-window.ui:215
|
||||||
msgid "Normal"
|
msgid "Normal"
|
||||||
msgstr "Predefinido"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
#: ui/help-overlay.ui:10 ui/preferences-window.ui:12
|
||||||
msgid "General"
|
msgid "General"
|
||||||
msgstr "Geral"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:13
|
#: ui/help-overlay.ui:13
|
||||||
msgid "Show shortcuts"
|
msgid "Show shortcuts"
|
||||||
msgstr "Mostrar atalhos"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:19
|
#: ui/help-overlay.ui:19
|
||||||
msgid "Toggle fullscreen"
|
msgid "Toggle fullscreen"
|
||||||
msgstr "Mudar modo de ecrã"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:20
|
#: ui/help-overlay.ui:20
|
||||||
msgid "Double tap | Double click"
|
msgid "Double tap | Double click"
|
||||||
msgstr "Toque duplo duplo Clique duplo"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:26
|
#: ui/help-overlay.ui:26
|
||||||
msgid "Leave fullscreen"
|
msgid "Leave fullscreen"
|
||||||
msgstr "Sair do modo de ecrã completo"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:32
|
#: ui/help-overlay.ui:32
|
||||||
msgid "Reveal OSD (fullscreen only)"
|
msgid "Reveal OSD (fullscreen only)"
|
||||||
msgstr "Revelar OSD (apenas em tela cheia)"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:33
|
#: ui/help-overlay.ui:33
|
||||||
msgid "Tap"
|
msgid "Tap"
|
||||||
msgstr "Tocar"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:39
|
#: ui/help-overlay.ui:39
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
msgstr "Sair"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:47
|
#: ui/help-overlay.ui:47
|
||||||
msgid "Media"
|
msgid "Media"
|
||||||
msgstr "Multimédia"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:50
|
#: ui/help-overlay.ui:50
|
||||||
msgid "Open files"
|
msgid "Open files"
|
||||||
msgstr "Abrir ficheiro"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||||
msgid "Open URI"
|
msgid "Open URI"
|
||||||
msgstr "Abrir URI"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:64
|
#: ui/help-overlay.ui:64
|
||||||
msgid "Playlist"
|
msgid "Playlist"
|
||||||
msgstr "Lista de reprodução"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:67
|
#: ui/help-overlay.ui:67
|
||||||
msgid "Next item"
|
msgid "Next item"
|
||||||
msgstr "Próximo item"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:68
|
#: ui/help-overlay.ui:68
|
||||||
msgid "Double tap (right side)"
|
msgid "Double tap (right side)"
|
||||||
msgstr "Toque duplo (lado direito)"
|
msgstr ""
|
||||||
|
|
||||||
#: ui/help-overlay.ui:74
|
#: ui/help-overlay.ui:74
|
||||||
msgid "Previous item"
|
msgid "Previous item"
|
||||||
|
@@ -96,6 +96,13 @@ class ClapperAppBase extends Gtk.Application
|
|||||||
if(accels)
|
if(accels)
|
||||||
this.set_accels_for_action(`app.${name}`, accels);
|
this.set_accels_for_action(`app.${name}`, accels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gtkSettings = Gtk.Settings.get_default();
|
||||||
|
settings.bind(
|
||||||
|
'dark-theme', gtkSettings,
|
||||||
|
'gtk-application-prefer-dark-theme',
|
||||||
|
Gio.SettingsBindFlags.GET
|
||||||
|
);
|
||||||
this.doneFirstActivate = true;
|
this.doneFirstActivate = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,151 +0,0 @@
|
|||||||
/* Copyright (C) 2012-present by fent
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const jsVarStr = '[a-zA-Z_\\$][a-zA-Z_0-9]*';
|
|
||||||
const jsSingleQuoteStr = `'[^'\\\\]*(:?\\\\[\\s\\S][^'\\\\]*)*'`;
|
|
||||||
const jsDoubleQuoteStr = `"[^"\\\\]*(:?\\\\[\\s\\S][^"\\\\]*)*"`;
|
|
||||||
const jsQuoteStr = `(?:${jsSingleQuoteStr}|${jsDoubleQuoteStr})`;
|
|
||||||
const jsKeyStr = `(?:${jsVarStr}|${jsQuoteStr})`;
|
|
||||||
const jsPropStr = `(?:\\.${jsVarStr}|\\[${jsQuoteStr}\\])`;
|
|
||||||
const jsEmptyStr = `(?:''|"")`;
|
|
||||||
const reverseStr = ':function\\(a\\)\\{' +
|
|
||||||
'(?:return )?a\\.reverse\\(\\)' +
|
|
||||||
'\\}';
|
|
||||||
const sliceStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'return a\\.slice\\(b\\)' +
|
|
||||||
'\\}';
|
|
||||||
const spliceStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'a\\.splice\\(0,b\\)' +
|
|
||||||
'\\}';
|
|
||||||
const swapStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'var c=a\\[0\\];a\\[0\\]=a\\[b(?:%a\\.length)?\\];a\\[b(?:%a\\.length)?\\]=c(?:;return a)?' +
|
|
||||||
'\\}';
|
|
||||||
const actionsObjRegexp = new RegExp(
|
|
||||||
`var (${jsVarStr})=\\{((?:(?:${
|
|
||||||
jsKeyStr}${reverseStr}|${
|
|
||||||
jsKeyStr}${sliceStr}|${
|
|
||||||
jsKeyStr}${spliceStr}|${
|
|
||||||
jsKeyStr}${swapStr
|
|
||||||
}),?\\r?\\n?)+)\\};`);
|
|
||||||
const actionsFuncRegexp = new RegExp(`${`function(?: ${jsVarStr})?\\(a\\)\\{` +
|
|
||||||
`a=a\\.split\\(${jsEmptyStr}\\);\\s*` +
|
|
||||||
`((?:(?:a=)?${jsVarStr}`}${
|
|
||||||
jsPropStr
|
|
||||||
}\\(a,\\d+\\);)+)` +
|
|
||||||
`return a\\.join\\(${jsEmptyStr}\\)` +
|
|
||||||
`\\}`);
|
|
||||||
const reverseRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${reverseStr}`, 'm');
|
|
||||||
const sliceRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${sliceStr}`, 'm');
|
|
||||||
const spliceRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${spliceStr}`, 'm');
|
|
||||||
const swapRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${swapStr}`, 'm');
|
|
||||||
|
|
||||||
const swapHeadAndPosition = (arr, position) => {
|
|
||||||
const first = arr[0];
|
|
||||||
arr[0] = arr[position % arr.length];
|
|
||||||
arr[position] = first;
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decipher(sig, tokens) {
|
|
||||||
sig = sig.split('');
|
|
||||||
tokens = tokens.split(',');
|
|
||||||
|
|
||||||
for(let i = 0, len = tokens.length; i < len; i++) {
|
|
||||||
let token = tokens[i], pos;
|
|
||||||
switch (token[0]) {
|
|
||||||
case 'r':
|
|
||||||
sig = sig.reverse();
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig = swapHeadAndPosition(sig, pos);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig = sig.slice(pos);
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig.splice(0, pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig.join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
function extractActions(body) {
|
|
||||||
const objResult = actionsObjRegexp.exec(body);
|
|
||||||
const funcResult = actionsFuncRegexp.exec(body);
|
|
||||||
|
|
||||||
if(!objResult || !funcResult)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const obj = objResult[1].replace(/\$/g, '\\$');
|
|
||||||
const objBody = objResult[2].replace(/\$/g, '\\$');
|
|
||||||
const funcBody = funcResult[1].replace(/\$/g, '\\$');
|
|
||||||
|
|
||||||
let result = reverseRegexp.exec(objBody);
|
|
||||||
const reverseKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = sliceRegexp.exec(objBody);
|
|
||||||
const sliceKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = spliceRegexp.exec(objBody);
|
|
||||||
const spliceKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = swapRegexp.exec(objBody);
|
|
||||||
const swapKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
|
|
||||||
const keys = `(${[reverseKey, sliceKey, spliceKey, swapKey].join('|')})`;
|
|
||||||
const myreg = `(?:a=)?${obj
|
|
||||||
}(?:\\.${keys}|\\['${keys}'\\]|\\["${keys}"\\])` +
|
|
||||||
`\\(a,(\\d+)\\)`;
|
|
||||||
const tokenizeRegexp = new RegExp(myreg, 'g');
|
|
||||||
const tokens = [];
|
|
||||||
|
|
||||||
while((result = tokenizeRegexp.exec(funcBody)) !== null) {
|
|
||||||
const key = result[1] || result[2] || result[3];
|
|
||||||
const pos = result[4];
|
|
||||||
switch (key) {
|
|
||||||
case swapKey:
|
|
||||||
tokens.push(`w${result[4]}`);
|
|
||||||
break;
|
|
||||||
case reverseKey:
|
|
||||||
tokens.push('r');
|
|
||||||
break;
|
|
||||||
case sliceKey:
|
|
||||||
tokens.push(`s${result[4]}`);
|
|
||||||
break;
|
|
||||||
case spliceKey:
|
|
||||||
tokens.push(`p${result[4]}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens.join(',');
|
|
||||||
}
|
|
40
src/debug.js
40
src/debug.js
@@ -14,22 +14,13 @@ const clapperDebugger = new Debug.Debugger('Clapper', {
|
|||||||
}),
|
}),
|
||||||
high_precision: true,
|
high_precision: true,
|
||||||
});
|
});
|
||||||
clapperDebugger.enabled = (
|
|
||||||
|
var enabled = (
|
||||||
clapperDebugger.enabled
|
clapperDebugger.enabled
|
||||||
|| G_DEBUG_ENV != null
|
|| G_DEBUG_ENV != null
|
||||||
&& G_DEBUG_ENV.includes('Clapper')
|
&& G_DEBUG_ENV.includes('Clapper')
|
||||||
);
|
);
|
||||||
|
clapperDebugger.enabled = enabled;
|
||||||
const ytDebugger = new Debug.Debugger('YouTube', {
|
|
||||||
name_printer: new Ink.Printer({
|
|
||||||
font: Ink.Font.BOLD,
|
|
||||||
color: Ink.Color.RED
|
|
||||||
}),
|
|
||||||
time_printer: new Ink.Printer({
|
|
||||||
color: Ink.Color.LIGHT_BLUE
|
|
||||||
}),
|
|
||||||
high_precision: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
function _logStructured(debuggerName, msg, level)
|
function _logStructured(debuggerName, msg, level)
|
||||||
{
|
{
|
||||||
@@ -43,23 +34,12 @@ function _logStructured(debuggerName, msg, level)
|
|||||||
function _debug(debuggerName, msg)
|
function _debug(debuggerName, msg)
|
||||||
{
|
{
|
||||||
if(msg.message) {
|
if(msg.message) {
|
||||||
_logStructured(
|
_logStructured(debuggerName, msg.message,
|
||||||
debuggerName,
|
|
||||||
msg.message,
|
|
||||||
GLib.LogLevelFlags.LEVEL_CRITICAL
|
GLib.LogLevelFlags.LEVEL_CRITICAL
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
clapperDebugger.debug(msg);
|
||||||
switch(debuggerName) {
|
|
||||||
case 'Clapper':
|
|
||||||
clapperDebugger.debug(msg);
|
|
||||||
break;
|
|
||||||
case 'YouTube':
|
|
||||||
ytDebugger.debug(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function debug(msg)
|
function debug(msg)
|
||||||
@@ -67,12 +47,12 @@ function debug(msg)
|
|||||||
_debug('Clapper', msg);
|
_debug('Clapper', msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ytDebug(msg)
|
|
||||||
{
|
|
||||||
_debug('YouTube', msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function warn(msg)
|
function warn(msg)
|
||||||
{
|
{
|
||||||
_logStructured('Clapper', msg, GLib.LogLevelFlags.LEVEL_WARNING);
|
_logStructured('Clapper', msg, GLib.LogLevelFlags.LEVEL_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function message(msg)
|
||||||
|
{
|
||||||
|
_logStructured('Clapper', msg, GLib.LogLevelFlags.LEVEL_MESSAGE);
|
||||||
|
}
|
||||||
|
297
src/gtuber.js
Normal file
297
src/gtuber.js
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
const { Gio, GstClapper } = imports.gi;
|
||||||
|
const Debug = imports.src.debug;
|
||||||
|
const Misc = imports.src.misc;
|
||||||
|
const FileOps = imports.src.fileOps;
|
||||||
|
const Gtuber = Misc.tryImport('Gtuber');
|
||||||
|
|
||||||
|
const { debug, warn } = Debug;
|
||||||
|
const { settings } = Misc;
|
||||||
|
|
||||||
|
const best = {
|
||||||
|
video: null,
|
||||||
|
audio: null,
|
||||||
|
video_audio: null,
|
||||||
|
};
|
||||||
|
const codecPairs = [];
|
||||||
|
const qualityType = {
|
||||||
|
0: 30, // normal
|
||||||
|
1: 60, // hfr
|
||||||
|
};
|
||||||
|
|
||||||
|
var isAvailable = (Gtuber != null);
|
||||||
|
var cancellable = null;
|
||||||
|
let client = null;
|
||||||
|
|
||||||
|
function resetBestStreams()
|
||||||
|
{
|
||||||
|
best.video = null;
|
||||||
|
best.audio = null;
|
||||||
|
best.video_audio = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStreamAllowed(stream, opts)
|
||||||
|
{
|
||||||
|
const vcodec = stream.video_codec;
|
||||||
|
const acodec = stream.audio_codec;
|
||||||
|
|
||||||
|
if(
|
||||||
|
vcodec
|
||||||
|
&& (!vcodec.startsWith(opts.vcodec)
|
||||||
|
|| (stream.height < 240 || stream.height > opts.height)
|
||||||
|
|| stream.fps > qualityType[opts.quality])
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
acodec
|
||||||
|
&& (!acodec.startsWith(opts.acodec))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (vcodec != null || acodec != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBestStreams(streams, opts)
|
||||||
|
{
|
||||||
|
for(let stream of streams) {
|
||||||
|
if(!isStreamAllowed(stream, opts))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const type = (stream.video_codec && stream.audio_codec)
|
||||||
|
? 'video_audio'
|
||||||
|
: (stream.video_codec)
|
||||||
|
? 'video'
|
||||||
|
: 'audio';
|
||||||
|
|
||||||
|
if(!best[type] || best[type].bitrate < stream.bitrate)
|
||||||
|
best[type] = stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _streamFilter(opts, stream)
|
||||||
|
{
|
||||||
|
switch(stream) {
|
||||||
|
case best.video:
|
||||||
|
return (best.audio != null || best.video_audio == null);
|
||||||
|
case best.audio:
|
||||||
|
return (best.video != null || best.video_audio == null);
|
||||||
|
case best.video_audio:
|
||||||
|
return (best.video == null || best.audio == null);
|
||||||
|
default:
|
||||||
|
return (opts.adaptive)
|
||||||
|
? isStreamAllowed(stream, opts)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateManifest(info, opts)
|
||||||
|
{
|
||||||
|
const gen = new Gtuber.ManifestGenerator({
|
||||||
|
pretty: Debug.enabled,
|
||||||
|
});
|
||||||
|
gen.set_media_info(info);
|
||||||
|
gen.set_filter_func(_streamFilter.bind(this, opts));
|
||||||
|
|
||||||
|
debug('trying to get manifest');
|
||||||
|
|
||||||
|
for(let pair of codecPairs) {
|
||||||
|
opts.vcodec = pair[0];
|
||||||
|
opts.acodec = pair[1];
|
||||||
|
|
||||||
|
/* Find best streams among adaptive ones */
|
||||||
|
if (!opts.adaptive)
|
||||||
|
updateBestStreams(info.get_adaptive_streams(), opts);
|
||||||
|
|
||||||
|
const data = gen.to_data();
|
||||||
|
|
||||||
|
/* Release our ref */
|
||||||
|
if (!opts.adaptive)
|
||||||
|
resetBestStreams();
|
||||||
|
|
||||||
|
if(data) {
|
||||||
|
debug('got manifest');
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('manifest not generated');
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBestCombinedUri(info, opts)
|
||||||
|
{
|
||||||
|
const streams = info.get_streams();
|
||||||
|
|
||||||
|
debug('searching for best combined URI');
|
||||||
|
|
||||||
|
for(let pair of codecPairs) {
|
||||||
|
opts.vcodec = pair[0];
|
||||||
|
opts.acodec = pair[1];
|
||||||
|
|
||||||
|
/* Find best non-adaptive stream */
|
||||||
|
updateBestStreams(streams, opts);
|
||||||
|
|
||||||
|
const bestUri = (best.video_audio)
|
||||||
|
? best.video_audio.get_uri()
|
||||||
|
: (best.audio)
|
||||||
|
? best.audio.get_uri()
|
||||||
|
: (best.video)
|
||||||
|
? best.video.get_uri()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
/* Release our ref */
|
||||||
|
resetBestStreams();
|
||||||
|
|
||||||
|
if(bestUri) {
|
||||||
|
debug('got best possible URI');
|
||||||
|
return bestUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If still nothing find stream by height */
|
||||||
|
for(let stream of streams) {
|
||||||
|
const height = stream.get_height();
|
||||||
|
if(!height || height > opts.height)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!best.video_audio || best.video_audio.height < stream.height)
|
||||||
|
best.video_audio = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
const anyUri = (best.video_audio)
|
||||||
|
? best.video_audio.get_uri()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
/* Release our ref */
|
||||||
|
resetBestStreams();
|
||||||
|
|
||||||
|
if (anyUri)
|
||||||
|
debug('got any URI');
|
||||||
|
|
||||||
|
return anyUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _parseMediaInfoAsync(info, player)
|
||||||
|
{
|
||||||
|
const resp = {
|
||||||
|
uri: null,
|
||||||
|
title: info.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { root } = player.widget;
|
||||||
|
const surface = root.get_surface();
|
||||||
|
const monitor = root.display.get_monitor_at_surface(surface);
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
width: monitor.geometry.width * monitor.scale_factor,
|
||||||
|
height: monitor.geometry.height * monitor.scale_factor,
|
||||||
|
vcodec: null,
|
||||||
|
acodec: null,
|
||||||
|
quality: settings.get_int('yt-quality-type'),
|
||||||
|
adaptive: settings.get_boolean('yt-adaptive-enabled'),
|
||||||
|
};
|
||||||
|
|
||||||
|
if(info.has_adaptive_streams) {
|
||||||
|
const data = generateManifest(info, opts);
|
||||||
|
if(data) {
|
||||||
|
const manifestFile = await FileOps.saveFilePromise(
|
||||||
|
'tmp', null, 'manifest', data
|
||||||
|
).catch(debug);
|
||||||
|
|
||||||
|
if(!manifestFile)
|
||||||
|
throw new Error('Gtuber: no manifest file was generated');
|
||||||
|
|
||||||
|
resp.uri = manifestFile.get_uri();
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.uri = getBestCombinedUri(info, opts);
|
||||||
|
|
||||||
|
if(!resp.uri)
|
||||||
|
throw new Error("Gtuber: no compatible stream found");
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createClient(player)
|
||||||
|
{
|
||||||
|
client = new Gtuber.Client();
|
||||||
|
debug('created new gtuber client');
|
||||||
|
|
||||||
|
/* TODO: config based on what HW supports */
|
||||||
|
//codecPairs.push(['vp9', 'opus']);
|
||||||
|
|
||||||
|
codecPairs.push(['avc', 'mp4a']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mightHandleUri(uri)
|
||||||
|
{
|
||||||
|
const unsupported = [
|
||||||
|
'file', 'fd', 'dvd', 'cdda',
|
||||||
|
'dvb', 'v4l2', 'gs'
|
||||||
|
];
|
||||||
|
return !unsupported.includes(Misc.getUriProtocol(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelFetching()
|
||||||
|
{
|
||||||
|
if(cancellable && !cancellable.is_cancelled())
|
||||||
|
cancellable.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseUriPromise(uri, player)
|
||||||
|
{
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if(!client) {
|
||||||
|
if(!isAvailable) {
|
||||||
|
debug('gtuber is not installed');
|
||||||
|
return resolve({ uri, title: null });
|
||||||
|
}
|
||||||
|
_createClient(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop to show reaction and restore internet bandwidth */
|
||||||
|
if(player.state !== GstClapper.ClapperState.STOPPED)
|
||||||
|
player.stop();
|
||||||
|
|
||||||
|
cancellable = new Gio.Cancellable();
|
||||||
|
debug('gtuber is fetching media info...');
|
||||||
|
|
||||||
|
client.fetch_media_info_async(uri, cancellable, (client, task) => {
|
||||||
|
cancellable = null;
|
||||||
|
let info = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
info = client.fetch_media_info_finish(task);
|
||||||
|
debug('gtuber successfully fetched media info');
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
const taskCancellable = task.get_cancellable();
|
||||||
|
|
||||||
|
if(taskCancellable.is_cancelled())
|
||||||
|
return reject(err);
|
||||||
|
|
||||||
|
const gtuberNoPlugin = (
|
||||||
|
err.domain === Gtuber.ClientError.quark()
|
||||||
|
&& err.code === Gtuber.ClientError.NO_PLUGIN
|
||||||
|
);
|
||||||
|
if(!gtuberNoPlugin)
|
||||||
|
return reject(err);
|
||||||
|
|
||||||
|
warn(`Gtuber: ${err.message}, trying URI as is...`);
|
||||||
|
|
||||||
|
/* Allow handling URI as is via GStreamer plugins */
|
||||||
|
return resolve({ uri, title: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseMediaInfoAsync(info, player)
|
||||||
|
.then(resp => resolve(resp))
|
||||||
|
.catch(err => reject(err));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
imports.gi.versions.Gdk = '4.0';
|
imports.gi.versions.Gdk = '4.0';
|
||||||
imports.gi.versions.Gtk = '4.0';
|
imports.gi.versions.Gtk = '4.0';
|
||||||
imports.gi.versions.Soup = '2.4';
|
imports.gi.versions.Soup = '2.4';
|
||||||
|
imports.gi.versions.Gtuber = '0.0';
|
||||||
|
|
||||||
pkg.initGettext();
|
pkg.initGettext();
|
||||||
pkg.initFormat();
|
pkg.initFormat();
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
imports.gi.versions.Gdk = '4.0';
|
imports.gi.versions.Gdk = '4.0';
|
||||||
imports.gi.versions.Gtk = '4.0';
|
imports.gi.versions.Gtk = '4.0';
|
||||||
imports.gi.versions.Soup = '2.4';
|
imports.gi.versions.Soup = '2.4';
|
||||||
|
imports.gi.versions.Gtuber = '0.0';
|
||||||
|
|
||||||
pkg.initGettext();
|
pkg.initGettext();
|
||||||
|
|
||||||
|
39
src/misc.js
39
src/misc.js
@@ -1,7 +1,8 @@
|
|||||||
const { Gio, GLib, Gdk, Gtk } = imports.gi;
|
const { Gio, GLib, Gdk, Gtk } = imports.gi;
|
||||||
const Debug = imports.src.debug;
|
const Debug = imports.src.debug;
|
||||||
|
|
||||||
const { debug } = Debug;
|
const { debug, message } = Debug;
|
||||||
|
const failedImports = [];
|
||||||
|
|
||||||
var appName = 'Clapper';
|
var appName = 'Clapper';
|
||||||
var appId = 'com.github.rafostar.Clapper';
|
var appId = 'com.github.rafostar.Clapper';
|
||||||
@@ -28,6 +29,23 @@ const subsKeys = Object.keys(subsTitles);
|
|||||||
|
|
||||||
let inhibitCookie;
|
let inhibitCookie;
|
||||||
|
|
||||||
|
function tryImport(libName)
|
||||||
|
{
|
||||||
|
let lib = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
lib = imports.gi[libName];
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
if(!failedImports.includes(libName)) {
|
||||||
|
failedImports.push(libName);
|
||||||
|
message(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
function getResourceUri(path)
|
function getResourceUri(path)
|
||||||
{
|
{
|
||||||
const res = `file://${pkg.pkgdatadir}/${path}`;
|
const res = `file://${pkg.pkgdatadir}/${path}`;
|
||||||
@@ -224,22 +242,3 @@ function getIsTouch(gesture)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeHTML(text)
|
|
||||||
{
|
|
||||||
return text.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeURIPlus(uri)
|
|
||||||
{
|
|
||||||
return decodeURI(uri.replace(/\+/g, ' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHex(num)
|
|
||||||
{
|
|
||||||
return Boolean(num.match(/[0-9a-f]+$/i));
|
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
const { Adw, Gdk, Gio, GObject, Gst, GstClapper, Gtk } = imports.gi;
|
const { Gdk, Gio, GObject, Gst, GstClapper, Gtk } = imports.gi;
|
||||||
const ByteArray = imports.byteArray;
|
const ByteArray = imports.byteArray;
|
||||||
const Debug = imports.src.debug;
|
const Debug = imports.src.debug;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
const YouTube = imports.src.youtube;
|
const Gtuber = imports.src.gtuber;
|
||||||
const { PlaylistWidget } = imports.src.playlist;
|
const { PlaylistWidget } = imports.src.playlist;
|
||||||
const { WebApp } = imports.src.webApp;
|
const { WebApp } = imports.src.webApp;
|
||||||
|
|
||||||
@@ -45,7 +45,6 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
|
|
||||||
this.webserver = null;
|
this.webserver = null;
|
||||||
this.webapp = null;
|
this.webapp = null;
|
||||||
this.ytClient = null;
|
|
||||||
this.playlistWidget = new PlaylistWidget();
|
this.playlistWidget = new PlaylistWidget();
|
||||||
|
|
||||||
this.seekDone = true;
|
this.seekDone = true;
|
||||||
@@ -74,7 +73,6 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
set_and_bind_settings()
|
set_and_bind_settings()
|
||||||
{
|
{
|
||||||
const settingsToSet = [
|
const settingsToSet = [
|
||||||
'dark-theme',
|
|
||||||
'after-playback',
|
'after-playback',
|
||||||
'seeking-mode',
|
'seeking-mode',
|
||||||
'audio-offset',
|
'audio-offset',
|
||||||
@@ -143,24 +141,13 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
set_uri(uri)
|
set_uri(uri)
|
||||||
{
|
{
|
||||||
this.customVideoTitle = null;
|
this.customVideoTitle = null;
|
||||||
|
Gtuber.cancelFetching();
|
||||||
|
|
||||||
if(Misc.getUriProtocol(uri) !== 'file') {
|
if(Gtuber.mightHandleUri(uri)) {
|
||||||
const [isYouTubeUri, videoId] = YouTube.checkYouTubeUri(uri);
|
Gtuber.parseUriPromise(uri, this)
|
||||||
|
.then(res => {
|
||||||
if(!isYouTubeUri)
|
this.customVideoTitle = res.title;
|
||||||
return super.set_uri(uri);
|
super.set_uri(res.uri);
|
||||||
|
|
||||||
if(!this.ytClient)
|
|
||||||
this.ytClient = new YouTube.YouTubeClient();
|
|
||||||
|
|
||||||
const { root } = this.widget;
|
|
||||||
const surface = root.get_surface();
|
|
||||||
const monitor = root.display.get_monitor_at_surface(surface);
|
|
||||||
|
|
||||||
this.ytClient.getPlaybackDataAsync(videoId, monitor)
|
|
||||||
.then(data => {
|
|
||||||
this.customVideoTitle = data.title;
|
|
||||||
super.set_uri(data.uri);
|
|
||||||
})
|
})
|
||||||
.catch(debug);
|
.catch(debug);
|
||||||
|
|
||||||
@@ -658,19 +645,6 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'dark-theme':
|
|
||||||
/* TODO: Remove libadwaita alpha2 compat someday */
|
|
||||||
if (Adw.StyleManager != null) {
|
|
||||||
const styleManager = Adw.StyleManager.get_default();
|
|
||||||
styleManager.color_scheme = (settings.get_boolean(key))
|
|
||||||
? Adw.ColorScheme.FORCE_DARK
|
|
||||||
: Adw.ColorScheme.FORCE_LIGHT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const gtkSettings = Gtk.Settings.get_default();
|
|
||||||
gtkSettings.gtk_application_prefer_dark_theme = settings.get_boolean(key);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'render-shadows':
|
case 'render-shadows':
|
||||||
root = this.widget.get_root();
|
root = this.widget.get_root();
|
||||||
if(!root) break;
|
if(!root) break;
|
||||||
|
19
src/prefs.js
19
src/prefs.js
@@ -1,6 +1,7 @@
|
|||||||
const { Adw, GObject, Gio, Gst, Gtk } = imports.gi;
|
const { Adw, GObject, Gio, Gst, Gtk } = imports.gi;
|
||||||
const Debug = imports.src.debug;
|
const Debug = imports.src.debug;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
|
const Gtuber = imports.src.gtuber;
|
||||||
|
|
||||||
const { debug } = Debug;
|
const { debug } = Debug;
|
||||||
const { settings } = Misc;
|
const { settings } = Misc;
|
||||||
@@ -440,15 +441,8 @@ class ClapperPrefsPluginExpander extends Adw.ExpanderRow
|
|||||||
const featuresNames = Object.keys(pluginsData[this.title]);
|
const featuresNames = Object.keys(pluginsData[this.title]);
|
||||||
debug(`Adding ${featuresNames.length} features to the list of plugin: ${this.title}`);
|
debug(`Adding ${featuresNames.length} features to the list of plugin: ${this.title}`);
|
||||||
|
|
||||||
for(let featureObj of pluginsData[this.title]) {
|
for(let featureObj of pluginsData[this.title])
|
||||||
const prefsPluginFeature = new PrefsPluginFeature(featureObj);
|
this.add(new PrefsPluginFeature(featureObj));
|
||||||
|
|
||||||
/* TODO: Remove old libadwaita compat */
|
|
||||||
if(this.add_row)
|
|
||||||
this.add_row(prefsPluginFeature);
|
|
||||||
else
|
|
||||||
this.add(prefsPluginFeature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -544,6 +538,7 @@ class ClapperPrefsPluginRankingSubpage extends Gtk.Box
|
|||||||
var PrefsWindow = GObject.registerClass({
|
var PrefsWindow = GObject.registerClass({
|
||||||
GTypeName: 'ClapperPrefsWindow',
|
GTypeName: 'ClapperPrefsWindow',
|
||||||
Template: Misc.getResourceUri('ui/preferences-window.ui'),
|
Template: Misc.getResourceUri('ui/preferences-window.ui'),
|
||||||
|
InternalChildren: ['gtuber_group'],
|
||||||
},
|
},
|
||||||
class ClapperPrefsWindow extends Adw.PreferencesWindow
|
class ClapperPrefsWindow extends Adw.PreferencesWindow
|
||||||
{
|
{
|
||||||
@@ -553,11 +548,7 @@ class ClapperPrefsWindow extends Adw.PreferencesWindow
|
|||||||
transient_for: window,
|
transient_for: window,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* FIXME: old libadwaita compat, should be
|
this._gtuber_group.visible = Gtuber.isAvailable;
|
||||||
* normally in prefs UI file */
|
|
||||||
this.can_swipe_back = true;
|
|
||||||
this.can_navigate_back = true;
|
|
||||||
|
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -321,8 +321,6 @@ class ClapperControlsRevealer extends Gtk.Revealer
|
|||||||
|
|
||||||
const isStick = (isFloating && settings.get_boolean('floating-stick'));
|
const isStick = (isFloating && settings.get_boolean('floating-stick'));
|
||||||
DBus.shellWindowEval('stick', isStick);
|
DBus.shellWindowEval('stick', isStick);
|
||||||
|
|
||||||
this.root.child.refreshWindowTitle(this.root.title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onControlsRevealed()
|
_onControlsRevealed()
|
||||||
|
@@ -4,7 +4,6 @@ const Debug = imports.src.debug;
|
|||||||
const Dialogs = imports.src.dialogs;
|
const Dialogs = imports.src.dialogs;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
const { Player } = imports.src.player;
|
const { Player } = imports.src.player;
|
||||||
const YouTube = imports.src.youtube;
|
|
||||||
const Revealers = imports.src.revealers;
|
const Revealers = imports.src.revealers;
|
||||||
|
|
||||||
const { debug } = Debug;
|
const { debug } = Debug;
|
||||||
@@ -278,8 +277,7 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
|
|
||||||
if(currStream && type !== 'subtitle') {
|
if(currStream && type !== 'subtitle') {
|
||||||
const caps = currStream.get_caps();
|
const caps = currStream.get_caps();
|
||||||
if (caps)
|
debug(`${type} caps: ${caps.to_string()}`);
|
||||||
debug(`${type} caps: ${caps.to_string()}`);
|
|
||||||
}
|
}
|
||||||
if(type === 'video') {
|
if(type === 'video') {
|
||||||
const isShowVis = (
|
const isShowVis = (
|
||||||
@@ -319,24 +317,11 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
title = item.filename;
|
title = item.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshWindowTitle(title);
|
this.root.title = title;
|
||||||
this.revealerTop.title = title;
|
this.revealerTop.title = title;
|
||||||
this.revealerTop.showTitle = true;
|
this.revealerTop.showTitle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshWindowTitle(title)
|
|
||||||
{
|
|
||||||
const isFloating = !this.controlsRevealer.reveal_child;
|
|
||||||
const pipSuffix = ' - PiP';
|
|
||||||
const hasPipSuffix = title.endsWith(pipSuffix);
|
|
||||||
|
|
||||||
this.root.title = (isFloating && !hasPipSuffix)
|
|
||||||
? title + pipSuffix
|
|
||||||
: (!isFloating && hasPipSuffix)
|
|
||||||
? title.substring(0, title.length - pipSuffix.length)
|
|
||||||
: title;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTime()
|
updateTime()
|
||||||
{
|
{
|
||||||
if(
|
if(
|
||||||
@@ -817,12 +802,10 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
{
|
{
|
||||||
const dropTarget = new Gtk.DropTarget({
|
const dropTarget = new Gtk.DropTarget({
|
||||||
actions: Gdk.DragAction.COPY | Gdk.DragAction.MOVE,
|
actions: Gdk.DragAction.COPY | Gdk.DragAction.MOVE,
|
||||||
preload: true,
|
|
||||||
});
|
});
|
||||||
dropTarget.set_gtypes([GObject.TYPE_STRING]);
|
dropTarget.set_gtypes([GObject.TYPE_STRING]);
|
||||||
dropTarget.connect('motion', this._onDataMotion.bind(this));
|
dropTarget.connect('motion', this._onDataMotion.bind(this));
|
||||||
dropTarget.connect('drop', this._onDataDrop.bind(this));
|
dropTarget.connect('drop', this._onDataDrop.bind(this));
|
||||||
dropTarget.connect('notify::value', this._onDropValueNotify.bind(this));
|
|
||||||
|
|
||||||
return dropTarget;
|
return dropTarget;
|
||||||
}
|
}
|
||||||
@@ -1023,36 +1006,6 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
this.posY = posY;
|
this.posY = posY;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDropValueNotify(dropTarget)
|
|
||||||
{
|
|
||||||
if(!dropTarget.value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const uris = dropTarget.value.split(/\r?\n/);
|
|
||||||
const firstUri = uris[0];
|
|
||||||
|
|
||||||
if(uris.length > 1 || !Gst.uri_is_valid(firstUri))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Check if user is dragging a YouTube link */
|
|
||||||
const [isYouTubeUri, videoId] = YouTube.checkYouTubeUri(firstUri);
|
|
||||||
if(!isYouTubeUri) return;
|
|
||||||
|
|
||||||
/* Since this is a YouTube video,
|
|
||||||
* create YT client if it was not created yet */
|
|
||||||
if(!this.player.ytClient)
|
|
||||||
this.player.ytClient = new YouTube.YouTubeClient();
|
|
||||||
|
|
||||||
const { ytClient } = this.player;
|
|
||||||
|
|
||||||
/* Speed up things by prefetching new video info before drop */
|
|
||||||
if(
|
|
||||||
!ytClient.compareLastVideoId(videoId)
|
|
||||||
&& ytClient.downloadingVideoId !== videoId
|
|
||||||
)
|
|
||||||
ytClient.getVideoInfoPromise(videoId).catch(debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDataMotion(dropTarget, x, y)
|
_onDataMotion(dropTarget, x, y)
|
||||||
{
|
{
|
||||||
return Gdk.DragAction.MOVE;
|
return Gdk.DragAction.MOVE;
|
||||||
|
1013
src/youtube.js
1013
src/youtube.js
File diff suppressed because it is too large
Load Diff
@@ -1,85 +0,0 @@
|
|||||||
var QualityType = {
|
|
||||||
0: 'normal',
|
|
||||||
1: 'hfr',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Itags = {
|
|
||||||
video: {
|
|
||||||
h264: {
|
|
||||||
normal: {
|
|
||||||
240: 133,
|
|
||||||
360: 134,
|
|
||||||
480: 135,
|
|
||||||
720: 136,
|
|
||||||
1080: 137,
|
|
||||||
},
|
|
||||||
hfr: {
|
|
||||||
720: 298,
|
|
||||||
1080: 299,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
audio: {
|
|
||||||
aac: [140],
|
|
||||||
opus: [249, 250, 251],
|
|
||||||
},
|
|
||||||
combined: {
|
|
||||||
360: 18,
|
|
||||||
720: 22,
|
|
||||||
},
|
|
||||||
hls: {
|
|
||||||
240: 92,
|
|
||||||
360: 93,
|
|
||||||
480: 94,
|
|
||||||
720: 95,
|
|
||||||
1080: 96,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function _appendItagArray(arr, opts, formats)
|
|
||||||
{
|
|
||||||
const keys = Object.keys(formats);
|
|
||||||
|
|
||||||
for(let fmt of keys) {
|
|
||||||
arr.push(formats[fmt]);
|
|
||||||
|
|
||||||
if(
|
|
||||||
fmt >= opts.height
|
|
||||||
|| Math.floor(fmt * 16 / 9) >= opts.width
|
|
||||||
)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDashItags(opts)
|
|
||||||
{
|
|
||||||
const allowed = {
|
|
||||||
video: [],
|
|
||||||
audio: (opts.codec === 'h264')
|
|
||||||
? Itags.audio.aac
|
|
||||||
: Itags.audio.opus
|
|
||||||
};
|
|
||||||
const types = Object.keys(Itags.video[opts.codec]);
|
|
||||||
|
|
||||||
for(let type of types) {
|
|
||||||
const formats = Itags.video[opts.codec][type];
|
|
||||||
_appendItagArray(allowed.video, opts, formats);
|
|
||||||
|
|
||||||
if(type === opts.type)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCombinedItags(opts)
|
|
||||||
{
|
|
||||||
return _appendItagArray([], opts, Itags.combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHLSItags(opts)
|
|
||||||
{
|
|
||||||
return _appendItagArray([], opts, Itags.hls);
|
|
||||||
}
|
|
@@ -5,6 +5,7 @@
|
|||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
<property name="search-enabled">True</property>
|
<property name="search-enabled">True</property>
|
||||||
<property name="destroy-with-parent">True</property>
|
<property name="destroy-with-parent">True</property>
|
||||||
|
<property name="can-swipe-back">True</property>
|
||||||
<property name="modal">True</property>
|
<property name="modal">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesPage">
|
<object class="AdwPreferencesPage">
|
||||||
@@ -196,8 +197,8 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup" id="gtuber_group">
|
||||||
<property name="title" translatable="no">YouTube</property>
|
<property name="title" translatable="no">Gtuber</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="ClapperPrefsSwitch">
|
<object class="ClapperPrefsSwitch">
|
||||||
<property name="title" translatable="yes">Prefer adaptive streaming</property>
|
<property name="title" translatable="yes">Prefer adaptive streaming</property>
|
||||||
|
Reference in New Issue
Block a user