Skip to content

Commit 7ebbf33

Browse files
committed
Fix AceStream playback URLs in content detail streams API.
The catalog streams endpoint never built MediaFlow acestream URLs, so the UI showed no playable URL even when MediaFlow was configured.
1 parent 4418fc0 commit 7ebbf33

4 files changed

Lines changed: 118 additions & 36 deletions

File tree

backend/src/routes/content/catalog_browse.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,10 @@ pub async fn get_media_streams(
11791179
.mediaflow_config
11801180
.as_ref()
11811181
.filter(|m| m.enable_web_playback && m.proxy_url.as_deref().is_some_and(|s| !s.is_empty()));
1182+
let mediaflow_config = ud
1183+
.mediaflow_config
1184+
.as_ref()
1185+
.filter(|m| m.proxy_url.as_deref().is_some_and(|s| !s.is_empty()));
11821186
let web_playback_enabled = mediaflow_web_config.is_some();
11831187
let season = effective_season;
11841188
let episode = effective_episode;
@@ -1231,6 +1235,8 @@ pub async fn get_media_streams(
12311235
tgs.chat_id AS tg_chat_id,
12321236
tgs.message_id AS tg_message_id,
12331237
tgs.id AS telegram_stream_id,
1238+
ace.content_id AS ace_content_id,
1239+
ace.info_hash AS ace_info_hash,
12341240
{STREAM_LINK_AGG_COLS},
12351241
ts.created_at
12361242
FROM stream s
@@ -1243,6 +1249,7 @@ pub async fn get_media_streams(
12431249
LEFT JOIN usenet_stream us ON us.stream_id = s.id
12441250
LEFT JOIN youtube_stream ys ON ys.stream_id = s.id
12451251
LEFT JOIN telegram_stream tgs ON tgs.stream_id = s.id
1252+
LEFT JOIN acestream_stream ace ON ace.stream_id = s.id
12461253
WHERE {stream_visibility}
12471254
AND fml.media_id = $1
12481255
AND fml.season_number = $2
@@ -1272,6 +1279,8 @@ pub async fn get_media_streams(
12721279
tgs.chat_id AS tg_chat_id,
12731280
tgs.message_id AS tg_message_id,
12741281
tgs.id AS telegram_stream_id,
1282+
ace.content_id AS ace_content_id,
1283+
ace.info_hash AS ace_info_hash,
12751284
{STREAM_LINK_AGG_COLS},
12761285
ts.created_at
12771286
FROM stream s
@@ -1280,6 +1289,7 @@ pub async fn get_media_streams(
12801289
LEFT JOIN usenet_stream us ON us.stream_id = s.id
12811290
LEFT JOIN youtube_stream ys ON ys.stream_id = s.id
12821291
LEFT JOIN telegram_stream tgs ON tgs.stream_id = s.id
1292+
LEFT JOIN acestream_stream ace ON ace.stream_id = s.id
12831293
WHERE sml.media_id = $1
12841294
AND {stream_visibility}"#
12851295
)))
@@ -1496,8 +1506,23 @@ pub async fn get_media_streams(
14961506
};
14971507
let keyword_blocked = kf.is_stream_text_blocked(&r.name, r.filename.as_deref());
14981508

1499-
// Build playback URL for torrent/usenet/telegram streams when configured.
1500-
let raw_playback_url: Option<String> = if rd_blocked || secret_str.is_empty() {
1509+
// Build playback URL for torrent/usenet/telegram/acestream streams when configured.
1510+
let raw_playback_url: Option<String> = if rd_blocked {
1511+
None
1512+
} else if r.stream_type == StreamType::Acestream {
1513+
if ud.enable_acestream_streams {
1514+
mediaflow_config.and_then(|mf| {
1515+
mediaflow::encode_mediaflow_acestream_url(
1516+
mf.proxy_url.as_deref()?,
1517+
r.ace_content_id.as_deref(),
1518+
r.ace_info_hash.as_deref(),
1519+
mf.api_password.as_deref(),
1520+
)
1521+
})
1522+
} else {
1523+
None
1524+
}
1525+
} else if secret_str.is_empty() {
15011526
None
15021527
} else if r.stream_type == StreamType::Telegram {
15031528
if let Some(tg_id) = r.telegram_stream_id {
@@ -1564,25 +1589,29 @@ pub async fn get_media_streams(
15641589
};
15651590

15661591
// Build browser_url by wrapping raw_playback_url through MediaFlow when configured.
1567-
// raw_playback_url is kept for external players; browser_url is for in-browser playback.
1568-
let browser_url: Option<String> = raw_playback_url.as_deref().and_then(|raw| {
1569-
mediaflow_web_config.and_then(|mf| {
1570-
let proxy_url = mf.proxy_url.as_deref()?;
1571-
let mut params = std::collections::BTreeMap::new();
1572-
if let Some(ap) = mf.api_password.as_deref().filter(|s| !s.is_empty()) {
1573-
params.insert("api_password".into(), ap.to_string());
1574-
}
1575-
mediaflow::encode_mediaflow_proxy_url(
1576-
proxy_url,
1577-
"/proxy/stream",
1578-
Some(raw),
1579-
params,
1580-
None,
1581-
None,
1582-
)
1583-
.ok()
1592+
// AceStream URLs already target MediaFlow directly and must not be double-wrapped.
1593+
let browser_url: Option<String> = if r.stream_type == StreamType::Acestream {
1594+
raw_playback_url.clone()
1595+
} else {
1596+
raw_playback_url.as_deref().and_then(|raw| {
1597+
mediaflow_web_config.and_then(|mf| {
1598+
let proxy_url = mf.proxy_url.as_deref()?;
1599+
let mut params = std::collections::BTreeMap::new();
1600+
if let Some(ap) = mf.api_password.as_deref().filter(|s| !s.is_empty()) {
1601+
params.insert("api_password".into(), ap.to_string());
1602+
}
1603+
mediaflow::encode_mediaflow_proxy_url(
1604+
proxy_url,
1605+
"/proxy/stream",
1606+
Some(raw),
1607+
params,
1608+
None,
1609+
None,
1610+
)
1611+
.ok()
1612+
})
15841613
})
1585-
});
1614+
};
15861615

15871616
let output = json!({
15881617
"id": r.id,

backend/src/routes/content/stream_rows.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub struct BrowseStreamRow {
6565
pub tg_chat_id: Option<String>,
6666
pub tg_message_id: Option<i32>,
6767
pub telegram_stream_id: Option<i32>,
68+
pub ace_content_id: Option<String>,
69+
pub ace_info_hash: Option<String>,
6870
pub audio_formats: Option<String>,
6971
pub channels: Option<String>,
7072
pub hdr_formats: Option<String>,

backend/src/routes/stream.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,22 +3074,12 @@ fn format_acestream_stream(
30743074
.get("info_hash")
30753075
.and_then(|v| v.as_str())
30763076
.filter(|s| !s.is_empty());
3077-
if content_id.is_none() && info_hash.is_none() {
3078-
return None;
3079-
}
3080-
3081-
// Build MediaFlow acestream URL: {proxy_url}/proxy/acestream/stream?id=...&api_password=...
3082-
let mut params = Vec::new();
3083-
if let Some(id) = content_id {
3084-
params.push(format!("id={}", urlencoding::encode(id)));
3085-
} else if let Some(ih) = info_hash {
3086-
params.push(format!("infohash={}", urlencoding::encode(ih)));
3087-
}
3088-
if let Some(ap) = mf.api_password.as_deref().filter(|s| !s.is_empty()) {
3089-
params.push(format!("api_password={}", urlencoding::encode(ap)));
3090-
}
3091-
let base = proxy_url.trim_end_matches('/');
3092-
let url = format!("{base}/proxy/acestream/stream?{}", params.join("&"));
3077+
let url = mediaflow::encode_mediaflow_acestream_url(
3078+
proxy_url,
3079+
content_id,
3080+
info_hash,
3081+
mf.api_password.as_deref(),
3082+
)?;
30933083

30943084
let quality = row.get("quality").and_then(|v| v.as_str()).unwrap_or("");
30953085
let resolution = row.get("resolution").and_then(|v| v.as_str()).unwrap_or("");

backend/src/util/mediaflow.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,40 @@ pub fn encode_mediaflow_proxy_url(
4747
Ok(format!("{base_url}?{query}"))
4848
}
4949

50+
/// Build a MediaFlow proxy URL for AceStream MPEG-TS playback.
51+
pub fn encode_mediaflow_acestream_url(
52+
mediaflow_proxy_url: &str,
53+
content_id: Option<&str>,
54+
info_hash: Option<&str>,
55+
api_password: Option<&str>,
56+
) -> Option<String> {
57+
let content_id = content_id.filter(|s| !s.is_empty());
58+
let info_hash = info_hash.filter(|s| !s.is_empty());
59+
if content_id.is_none() && info_hash.is_none() {
60+
return None;
61+
}
62+
63+
let mut params = BTreeMap::new();
64+
if let Some(id) = content_id {
65+
params.insert("id".into(), id.to_string());
66+
} else if let Some(ih) = info_hash {
67+
params.insert("infohash".into(), ih.to_string());
68+
}
69+
if let Some(ap) = api_password.filter(|s| !s.is_empty()) {
70+
params.insert("api_password".into(), ap.to_string());
71+
}
72+
73+
encode_mediaflow_proxy_url(
74+
mediaflow_proxy_url,
75+
"/proxy/acestream/stream",
76+
None,
77+
params,
78+
None,
79+
None,
80+
)
81+
.ok()
82+
}
83+
5084
/// Returns true when `url` has a usable scheme and host.
5185
pub fn is_valid_url(url: &str) -> bool {
5286
Url::parse(url)
@@ -65,6 +99,33 @@ mod tests {
6599
assert!(!is_valid_url(""));
66100
}
67101

102+
#[test]
103+
fn encode_acestream_url_prefers_content_id() {
104+
let url = encode_mediaflow_acestream_url(
105+
"https://proxy.example.com",
106+
Some("0123456789abcdef0123456789abcdef01234567"),
107+
Some("fedcba9876543210fedcba9876543210fedcba98"),
108+
Some("secret"),
109+
)
110+
.unwrap();
111+
assert!(url.contains("/proxy/acestream/stream?"));
112+
assert!(url.contains("id=0123456789abcdef0123456789abcdef01234567"));
113+
assert!(url.contains("api_password=secret"));
114+
assert!(!url.contains("infohash="));
115+
}
116+
117+
#[test]
118+
fn encode_acestream_url_falls_back_to_info_hash() {
119+
let url = encode_mediaflow_acestream_url(
120+
"https://proxy.example.com",
121+
None,
122+
Some("fedcba9876543210fedcba9876543210fedcba98"),
123+
None,
124+
)
125+
.unwrap();
126+
assert!(url.contains("infohash=fedcba9876543210fedcba9876543210fedcba98"));
127+
}
128+
68129
#[test]
69130
fn encode_plain_proxy_url_includes_destination() {
70131
let url = encode_mediaflow_proxy_url(

0 commit comments

Comments
 (0)