Skip to content

Commit b8c62ef

Browse files
committed
XC sources: option to use HLS (.m3u8) for live streams
Some providers drop raw .ts live connections after a few seconds but serve the same channel fine as HLS. Adds a Live Stream Format dropdown to the XC source editor, saved as liveFormat in xc_data; the live URL builder uses it for the extension. Default stays .ts, existing sources are unchanged.
1 parent 7a241c5 commit b8c62ef

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

public/index.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,6 +2592,16 @@ <h3 id="source-editor-title" class="text-xl font-bold text-white mb-4"></h3>
25922592
<input type="password" id="source-editor-xc-password"
25932593
class="mt-1 block w-full bg-gray-700 border border-gray-600 rounded-md px-3 py-2 text-white focus:ring-blue-500 focus:border-blue-500">
25942594
</div>
2595+
<div>
2596+
<label for="source-editor-xc-live-format"
2597+
class="block text-sm font-medium text-gray-400">Live Stream Format</label>
2598+
<select id="source-editor-xc-live-format"
2599+
class="mt-1 block w-full bg-gray-700 border border-gray-600 rounded-md px-3 py-2 text-white focus:ring-blue-500 focus:border-blue-500">
2600+
<option value="ts" selected>MPEG-TS (.ts, default)</option>
2601+
<option value="m3u8">HLS (.m3u8)</option>
2602+
</select>
2603+
<p class="text-xs text-gray-500 mt-1">Try HLS if live channels stop after a few seconds while VOD works.</p>
2604+
</div>
25952605
</div>
25962606
<div id="source-editor-refresh-container">
25972607
<label for="source-editor-refresh-interval"

public/js/modules/settings.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ const openSourceEditor = (sourceType, source = null) => {
10211021
server: UIElements.sourceEditorXcUrl.value,
10221022
username: UIElements.sourceEditorXcUsername.value,
10231023
password: UIElements.sourceEditorXcPassword.value,
1024+
liveFormat: UIElements.sourceEditorXcLiveFormat.value,
10241025
});
10251026
}
10261027
// File sources: fetch-groups only works if file is already on server (has ID or path knewn)
@@ -1084,12 +1085,14 @@ const openSourceEditor = (sourceType, source = null) => {
10841085
UIElements.sourceEditorXcUrl.value = xcData.server || '';
10851086
UIElements.sourceEditorXcUsername.value = xcData.username || '';
10861087
UIElements.sourceEditorXcPassword.value = xcData.password || '';
1088+
UIElements.sourceEditorXcLiveFormat.value = xcData.liveFormat === 'm3u8' ? 'm3u8' : 'ts';
10871089
} catch (e) {
10881090
console.error("Could not parse XC data for editing:", e);
10891091
// Clear fields if data is corrupt
10901092
UIElements.sourceEditorXcUrl.value = '';
10911093
UIElements.sourceEditorXcUsername.value = '';
10921094
UIElements.sourceEditorXcPassword.value = '';
1095+
UIElements.sourceEditorXcLiveFormat.value = 'ts';
10931096
}
10941097
}
10951098
break;
@@ -1337,6 +1340,7 @@ export function setupSettingsEventListeners() {
13371340
server: UIElements.sourceEditorXcUrl.value,
13381341
username: UIElements.sourceEditorXcUsername.value,
13391342
password: UIElements.sourceEditorXcPassword.value,
1343+
liveFormat: UIElements.sourceEditorXcLiveFormat.value,
13401344
}));
13411345
formData.append('refreshHours', UIElements.sourceEditorRefreshInterval.value);
13421346
}
@@ -2019,6 +2023,7 @@ export function setupSettingsEventListeners() {
20192023
server: UIElements.sourceEditorXcUrl.value,
20202024
username: UIElements.sourceEditorXcUsername.value,
20212025
password: UIElements.sourceEditorXcPassword.value,
2026+
liveFormat: UIElements.sourceEditorXcLiveFormat.value,
20222027
}) : null,
20232028
sourceId: sourceId // <-- ADD THIS LINE
20242029
};
@@ -2096,6 +2101,7 @@ export function setupSettingsEventListeners() {
20962101
server: UIElements.sourceEditorXcUrl.value,
20972102
username: UIElements.sourceEditorXcUsername.value,
20982103
password: UIElements.sourceEditorXcPassword.value,
2104+
liveFormat: UIElements.sourceEditorXcLiveFormat.value,
20992105
}) : null,
21002106
refresh: true, // Tell the backend to force refresh
21012107
sourceId: sourceId // Pass sourceId for cache update

server.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,10 @@ async function processAndMergeSources(req) {
12901290
}
12911291
const xcInfo = JSON.parse(source.xc_data);
12921292
const { server, username, password } = xcInfo;
1293+
// Live stream container: 'ts' (default, raw MPEG-TS) or 'm3u8' (HLS).
1294+
// Some providers close raw .ts connections after a few seconds but
1295+
// serve the same channel fine as HLS.
1296+
const liveExt = xcInfo.liveFormat === 'm3u8' ? 'm3u8' : 'ts';
12931297

12941298
if (!server || !username || !password) {
12951299
throw new Error("XC source is missing server, username, or password.");
@@ -1320,7 +1324,7 @@ async function processAndMergeSources(req) {
13201324
for (const stream of liveStreams) {
13211325
if (stream.stream_type === 'live') {
13221326
liveStreamCount++;
1323-
const streamUrl = `${server}/live/${username}/${password}/${stream.stream_id}.ts`;
1327+
const streamUrl = `${server}/live/${username}/${password}/${stream.stream_id}.${liveExt}`;
13241328

13251329
// Find category name from categories array
13261330
const categoryName = Array.isArray(liveCategories)

0 commit comments

Comments
 (0)