Skip to content

Commit c9f9149

Browse files
committed
fix: external player m3u8 playlist opening
1 parent be4013c commit c9f9149

7 files changed

Lines changed: 122 additions & 21 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ chrono = "0.4.45"
3737
anyhow = "1.0.102"
3838
url = "2.5.8"
3939
dirs = "6.0.0"
40+
base64 = "0.22.1"
4041

4142
[build-dependencies]
4243
anyhow = "1.0.102"

src/app/imp.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ use std::cell::{Cell, RefCell};
22

33
use adw::{prelude::*, subclass::prelude::*};
44
use gtk::glib::{self, Properties, clone};
5-
6-
use crate::app::{
7-
config::{APP_ID, APP_NAME, URI_SCHEME},
8-
ipc::{
9-
self,
10-
event::{IpcEvent, IpcEventMpv},
5+
use tracing::error;
6+
7+
use crate::{
8+
app::{
9+
config::{APP_ID, APP_NAME, URI_SCHEME},
10+
ipc::{
11+
self,
12+
event::{IpcEvent, IpcEventMpv},
13+
},
14+
mpris::Mpris,
15+
tray::Tray,
16+
video::Video,
17+
webview::WebView,
18+
window::Window,
1119
},
12-
mpris::Mpris,
13-
tray::Tray,
14-
video::Video,
15-
webview::WebView,
16-
window::Window,
20+
spawn_local, utils,
1721
};
1822

1923
const PRELOAD_SCRIPT: &str = include_str!("ipc/preload.js");
@@ -182,8 +186,17 @@ impl ApplicationImpl for Application {
182186
webview.connect_open_external(clone!(
183187
#[weak]
184188
window,
185-
move |uri| {
186-
window.open_uri(uri);
189+
move |data| {
190+
if data.starts_with("application/octet-stream") {
191+
spawn_local!(async move {
192+
match utils::download_file("playlist.m3u8", data).await {
193+
Ok(file_path) => window.open_file(file_path),
194+
Err(e) => error!("Failed to download file: {e}"),
195+
}
196+
});
197+
} else {
198+
window.open_uri(data);
199+
}
187200
}
188201
));
189202

src/app/webview/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,21 @@ impl WebView {
129129
widget
130130
.webview
131131
.connect_decide_policy(move |_, decision, decision_type| {
132-
if let PolicyDecisionType::NewWindowAction = decision_type
133-
&& let Some(decision) = decision.downcast_ref::<NavigationPolicyDecision>()
134-
&& let Some(action) = decision.navigation_action()
135-
&& let Some(request) = action.request()
136-
&& let Some(uri) = request.uri()
132+
if let Some(uri) = decision
133+
.downcast_ref::<NavigationPolicyDecision>()
134+
.and_then(|decision| decision.navigation_action())
135+
.and_then(|action| action.request())
136+
.and_then(|request| request.uri())
137137
{
138-
callback(uri.to_string());
138+
match decision_type {
139+
PolicyDecisionType::NavigationAction if uri.starts_with("data:") => {
140+
callback(uri.replace("data:", ""));
141+
}
142+
PolicyDecisionType::NewWindowAction => {
143+
callback(uri.to_string());
144+
}
145+
_ => {}
146+
}
139147
}
140148

141149
true

src/app/window/imp.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{cell::Cell, sync::Arc};
1+
use std::{cell::Cell, fs::File, os::fd::AsFd, sync::Arc};
22

33
use adw::prelude::*;
44
use adw::subclass::prelude::*;
@@ -120,6 +120,28 @@ impl Window {
120120
));
121121
}
122122

123+
pub fn open_file(&self, file_path: String) {
124+
let object = self.obj();
125+
126+
spawn_local!(clone!(
127+
#[weak]
128+
object,
129+
async move {
130+
if let Some(identifier) = WindowIdentifier::from_native(&object).await {
131+
let request = OpenFileRequest::default().identifier(identifier);
132+
133+
if let Ok(file) = File::open(&file_path) {
134+
request
135+
.send_file(&file.as_fd())
136+
.await
137+
.map_err(|e| error!("Failed to open file: {e}"))
138+
.ok();
139+
}
140+
}
141+
}
142+
));
143+
}
144+
123145
pub fn show_header(&self, state: bool) {
124146
self.header.set_visible(self.decorations.get() && state);
125147
}

src/app/window/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ impl Window {
6060
pub fn open_uri(&self, uri: String) {
6161
self.imp().open_uri(uri);
6262
}
63+
64+
pub fn open_file(&self, file_path: String) {
65+
self.imp().open_file(file_path);
66+
}
6367
}
6468

6569
fn graphics_offload(widget: &impl IsA<Widget>) -> gtk::GraphicsOffload {

src/utils.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
use std::sync::LazyLock;
1+
use std::{
2+
io::{self, Cursor},
3+
sync::LazyLock,
4+
};
5+
6+
use anyhow::{Context, Error, Result, anyhow};
7+
use base64::{engine::general_purpose, read};
8+
use tokio::fs;
29

310
#[macro_export]
411
macro_rules! spawn_local {
@@ -12,3 +19,42 @@ pub static IS_DESKTOP_KDE: LazyLock<bool> = LazyLock::new(|| {
1219
.ok()
1320
.is_some_and(|value| value == "KDE")
1421
});
22+
23+
pub fn decode_base64(data: &str) -> Result<Vec<u8>> {
24+
let mut input = Cursor::new(data);
25+
let mut output = Vec::new();
26+
27+
let engine = general_purpose::STANDARD;
28+
let mut decoder = read::DecoderReader::new(&mut input, &engine);
29+
30+
if let Err(e) = io::copy(&mut decoder, &mut output) {
31+
return Err(Error::msg(format!("Failed to decode base64: {e}")));
32+
}
33+
34+
Ok(output)
35+
}
36+
37+
pub async fn download_file(file_name: &str, data: String) -> Result<String> {
38+
let base_64 = data
39+
.strip_prefix("application/octet-stream;charset=utf-8;base64,")
40+
.ok_or_else(|| anyhow!("Failed to parse data URL"))?;
41+
let bytes = decode_base64(base_64)?;
42+
43+
let out_dir = dirs::download_dir()
44+
.ok_or_else(|| anyhow!("Failed to get download dir"))?
45+
.join("Stremio");
46+
47+
fs::create_dir_all(&out_dir)
48+
.await
49+
.context("Failed to create download dir")?;
50+
51+
let file_name = out_dir.join(file_name);
52+
fs::write(&file_name, bytes)
53+
.await
54+
.context("Failed to write file to download dir")?;
55+
56+
file_name
57+
.into_os_string()
58+
.into_string()
59+
.map_err(|os_str| anyhow!("Failed to convert path to string: {:?}", os_str))
60+
}

0 commit comments

Comments
 (0)