From f1e27eb7f3491ee4b80bd0cb6e7684d14d3bffb5 Mon Sep 17 00:00:00 2001
From: d-k-bo <47948262+d-k-bo@users.noreply.github.com>
Date: Sun, 29 Oct 2023 18:21:31 +0100
Subject: [PATCH] Support custom video players
---
.../icons/scalable/actions/error-symbolic.svg | 2 +
.../scalable/actions/test-pass-symbolic.svg | 2 +
po/de.po | 45 +++--
po/televido.pot | 41 ++--
src/application.rs | 41 ++--
src/launcher/mod.rs | 113 ++++++-----
src/launcher/selector.blp | 15 ++
src/launcher/selector.rs | 175 +++++++++++++-----
src/preferences.rs | 25 ++-
9 files changed, 317 insertions(+), 142 deletions(-)
create mode 100644 data/resources/icons/scalable/actions/error-symbolic.svg
create mode 100644 data/resources/icons/scalable/actions/test-pass-symbolic.svg
diff --git a/data/resources/icons/scalable/actions/error-symbolic.svg b/data/resources/icons/scalable/actions/error-symbolic.svg
new file mode 100644
index 0000000..acc96ed
--- /dev/null
+++ b/data/resources/icons/scalable/actions/error-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/data/resources/icons/scalable/actions/test-pass-symbolic.svg b/data/resources/icons/scalable/actions/test-pass-symbolic.svg
new file mode 100644
index 0000000..005be01
--- /dev/null
+++ b/data/resources/icons/scalable/actions/test-pass-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/po/de.po b/po/de.po
index fe04dff..4a6f60f 100644
--- a/po/de.po
+++ b/po/de.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Televido main\n"
"Report-Msgid-Bugs-To: https://github.com/d-k-bo/televido/issues\n"
-"POT-Creation-Date: 2023-10-26 21:32+0200\n"
+"POT-Creation-Date: 2023-10-29 18:20+0100\n"
"PO-Revision-Date: 2023-10-20 20:55+0200\n"
"Last-Translator: David Cabot \n"
"Language-Team: \n"
@@ -72,7 +72,7 @@ msgstr ""
msgid "Initital release"
msgstr "Erstveröffentlichung"
-#: src/application.rs:116
+#: src/application.rs:129
msgid "Failed to play video stream"
msgstr "Videostream konnte nicht abgespielt werden"
@@ -104,38 +104,53 @@ msgstr "Abbrechen"
msgid "Confirm"
msgstr "Bestätigen"
-#: src/launcher/selector.rs:76
+#: src/launcher/selector.blp:38
+msgid "Custom program"
+msgstr "Benutzerdefiniertes Programm"
+
+#. translators: `{}` is replaced by the application ID, e.g. `org.example.Application`
+#: src/launcher/selector.rs:115
+msgid "Could not find application “{}”"
+msgstr "Anwendung „{}“ konnte nicht gefunden werden"
+
+#: src/launcher/selector.rs:169
msgid "Failed to load external applications"
msgstr "Externe Anwendungen konnten nicht geladen werden."
-#: src/launcher/selector.rs:80 src/utils.rs:183
+#: src/launcher/selector.rs:173 src/utils.rs:183
msgid "See the terminal output for details."
msgstr "In den Programm-Logs finden Sie weitere Details."
-#. translators: `{}` is replaced by given ID, a valid one would be e.g. `org.gnome.Totem`
-#: src/launcher/selector.rs:146
-msgid "Invalid program ID: “{}”"
-msgstr "Ungültige Programm-ID: „{}“"
-
-#: src/launcher/selector.rs:196
+#: src/launcher/selector.rs:280
msgid "Select video player"
msgstr "Videoplayer auswählen"
-#: src/launcher/selector.rs:198
-msgid "Select one of the following external programs to stream content"
+#: src/launcher/selector.rs:281
+msgid "Select one of the following external programs to stream content."
msgstr ""
"Wählen Sie eines der folgenden externen Programme zum Streamen von Inhalten"
-#: src/launcher/selector.rs:202
+#: src/launcher/selector.rs:284
msgid "Select video downloader"
msgstr "Video-Downloader auswählen"
-#: src/launcher/selector.rs:204
-msgid "Select one of the following external programs to download content"
+#: src/launcher/selector.rs:285
+msgid "Select one of the following external programs to download content."
msgstr ""
"Wählen Sie eines der folgenden externen Programme zum Herunterladen von "
"Inhalten"
+#: src/launcher/selector.rs:292
+msgid ""
+"You can also specify a custom application ID (e.g. org.example.Application) "
+"of a different program that supports DBus activation, is able to open a "
+"https:// URI and is accessible from the context of this application."
+msgstr ""
+"Sie können auch eine benutzerdefinierte Anwendungs-ID (z.B. org.example."
+"Application) angeben, um ein anderes Programm zu nutzen, das DBus-"
+"Aktivierung unterstützt, https://-URIs öffnen kann und aus dem Kontext "
+"dieser Anwendung erreicht werden kann."
+
#: src/live/card.blp:46 src/mediathek/card.blp:86
msgid "Play"
msgstr "Abspielen"
diff --git a/po/televido.pot b/po/televido.pot
index 2c7ee51..68f92c2 100644
--- a/po/televido.pot
+++ b/po/televido.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: televido\n"
"Report-Msgid-Bugs-To: https://github.com/d-k-bo/televido/issues\n"
-"POT-Creation-Date: 2023-10-26 21:32+0200\n"
+"POT-Creation-Date: 2023-10-29 18:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -57,7 +57,7 @@ msgstr ""
msgid "Initital release"
msgstr ""
-#: src/application.rs:116
+#: src/application.rs:129
msgid "Failed to play video stream"
msgstr ""
@@ -89,33 +89,44 @@ msgstr ""
msgid "Confirm"
msgstr ""
-#: src/launcher/selector.rs:76
-msgid "Failed to load external applications"
+#: src/launcher/selector.blp:38
+msgid "Custom program"
msgstr ""
-#: src/launcher/selector.rs:80 src/utils.rs:183
-msgid "See the terminal output for details."
+#. translators: `{}` is replaced by the application ID, e.g. `org.example.Application`
+#: src/launcher/selector.rs:115
+msgid "Could not find application “{}”"
msgstr ""
-#. translators: `{}` is replaced by given ID, a valid one would be e.g. `org.gnome.Totem`
-#: src/launcher/selector.rs:146
-msgid "Invalid program ID: “{}”"
+#: src/launcher/selector.rs:169
+msgid "Failed to load external applications"
msgstr ""
-#: src/launcher/selector.rs:196
+#: src/launcher/selector.rs:173 src/utils.rs:183
+msgid "See the terminal output for details."
+msgstr ""
+
+#: src/launcher/selector.rs:280
msgid "Select video player"
msgstr ""
-#: src/launcher/selector.rs:198
-msgid "Select one of the following external programs to stream content"
+#: src/launcher/selector.rs:281
+msgid "Select one of the following external programs to stream content."
msgstr ""
-#: src/launcher/selector.rs:202
+#: src/launcher/selector.rs:284
msgid "Select video downloader"
msgstr ""
-#: src/launcher/selector.rs:204
-msgid "Select one of the following external programs to download content"
+#: src/launcher/selector.rs:285
+msgid "Select one of the following external programs to download content."
+msgstr ""
+
+#: src/launcher/selector.rs:292
+msgid ""
+"You can also specify a custom application ID (e.g. org.example.Application) "
+"of a different program that supports DBus activation, is able to open a "
+"https:// URI and is accessible from the context of this application."
msgstr ""
#: src/live/card.blp:46 src/mediathek/card.blp:86
diff --git a/src/application.rs b/src/application.rs
index 937a639..d411d54 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -8,7 +8,7 @@ use gettextrs::gettext;
use crate::{
config::{APP_ID, APP_NAME, AUTHOR, ISSUE_URL, PROJECT_URL, VERSION},
- launcher::{ExternalProgramType, ProgramSelector},
+ launcher::{ExternalProgram, ExternalProgramType, ProgramSelector},
preferences::TvPreferencesWindow,
settings::TvSettings,
utils::{show_error, spawn_clone, tokio},
@@ -93,22 +93,35 @@ impl TvApplication {
pub async fn play(&self, uri: String) {
let settings = TvSettings::get();
+ let player_name = settings.video_player_name();
let player_id = settings.video_player_id();
- let player = if player_id.is_empty() {
- let Some(program) = ProgramSelector::select_program(ExternalProgramType::Player).await
- else {
- return;
- };
-
- settings.set_video_player_name(program.name);
- settings.set_video_player_id(program.id);
- program
+ let player = if player_id.is_empty() {
+ None
} else {
- let Some(program) = ExternalProgramType::Player.find(&player_id) else {
- return;
- };
- program
+ match ExternalProgram::find(player_name, player_id.clone()).await {
+ Ok(player) => player,
+ Err(e) => {
+ show_error(e);
+ None
+ }
+ }
+ };
+
+ let player = match player {
+ Some(player) => player,
+ None => {
+ match ProgramSelector::select_program(ExternalProgramType::Player, player_id).await
+ {
+ Some(player) => {
+ settings.set_video_player_name(&player.name);
+ settings.set_video_player_id(&player.id);
+
+ player
+ }
+ None => return,
+ }
+ }
};
match player.play(uri).await {
diff --git a/src/launcher/mod.rs b/src/launcher/mod.rs
index 5136108..5d764f0 100644
--- a/src/launcher/mod.rs
+++ b/src/launcher/mod.rs
@@ -1,6 +1,8 @@
// Copyright 2023 David Cabot
// SPDX-License-Identifier: GPL-3.0-or-later
+use std::borrow::Cow;
+
use crate::{application::TvApplication, utils::tokio};
use self::application_proxy::ApplicationProxy;
@@ -10,47 +12,48 @@ mod application_proxy;
mod selector;
pub static PLAYERS: &[ExternalProgram] = &[
- ExternalProgram {
- name: "Videos",
- id: "org.gnome.Totem",
- },
- ExternalProgram {
- name: "Celluloid",
- id: "io.github.celluloid_player.Celluloid",
- },
- ExternalProgram {
- name: "Clapper",
- id: "com.github.rafostar.Clapper",
- },
+ ExternalProgram::new("Videos", "org.gnome.Totem"),
+ ExternalProgram::new("Celluloid", "io.github.celluloid_player.Celluloid"),
+ ExternalProgram::new("Clapper", "com.github.rafostar.Clapper"),
+ ExternalProgram::new("Daikhan", "io.gitlab.daikhan.stable"),
// not dbus-activatable
- // ExternalProgram { name: "µPlayer", id: "org.sigxcpu.Livi"},
- // ExternalProgram { name: "Glide", id: "net.baseart.Glide"},
- // ExternalProgram { name: "Daikhan", id: "io.gitlab.daikhan.stable"},
+ // ExternalProgram::new("µPlayer", "org.sigxcpu.Livi"),
+ // ExternalProgram::new("Glide", "net.baseart.Glide"),
// doesn't implement org.freedesktop.Application
- // ExternalProgram { name: "VLC", id: "org.videolan.VLC"},
- // ExternalProgram { name: "mpv", id: "io.mpv.Mpv"},
- // ExternalProgram { name: "Haruna Media µPlayer", id: "org.kde.haruna"},
+ // ExternalProgram::new("VLC", "org.videolan.VLC"),
+ // ExternalProgram::new("mpv", "io.mpv.Mpv"),
+ // ExternalProgram::new("Haruna Media µPlayer", "org.kde.haruna"),
];
-pub static DOWNLOADERS: &[ExternalProgram] = &[ExternalProgram {
- name: "Parabolic",
- id: "org.nickvision.tubeconverter",
-}];
+pub static DOWNLOADERS: &[ExternalProgram] = &[ExternalProgram::new(
+ "Parabolic",
+ "org.nickvision.tubeconverter",
+)];
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
pub struct ExternalProgram {
- pub name: &'static str,
- pub id: &'static str,
+ pub name: Cow<'static, str>,
+ pub id: Cow<'static, str>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum ExternalProgramType {
+ Player,
+ Downloader,
}
+
impl ExternalProgram {
pub async fn play(self, uri: impl Into) -> eyre::Result<()> {
let conn = TvApplication::dbus().await;
let uri = uri.into();
tokio(async move {
- let proxy =
- ApplicationProxy::new(&conn, self.id, format!("/{}", self.id.replace('.', "/")))
- .await?;
+ let proxy = ApplicationProxy::new(
+ &conn,
+ self.id.clone(),
+ format!("/{}", self.id.replace('.', "/")),
+ )
+ .await?;
proxy.open(&[&uri], Default::default()).await?;
@@ -60,27 +63,46 @@ impl ExternalProgram {
}
}
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum ExternalProgramType {
- Player,
- Downloader,
-}
-
-impl ExternalProgramType {
- pub fn all(self) -> &'static [ExternalProgram] {
- match self {
- ExternalProgramType::Player => PLAYERS,
- ExternalProgramType::Downloader => DOWNLOADERS,
+impl ExternalProgram {
+ const fn new(name: &'static str, id: &'static str) -> Self {
+ ExternalProgram {
+ name: Cow::Borrowed(name),
+ id: Cow::Borrowed(id),
}
}
- pub fn find(self, id: &str) -> Option {
- self.all().iter().find(|program| program.id == id).copied()
+ pub async fn find(
+ name: impl Into>,
+ id: impl Into>,
+ ) -> eyre::Result