mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
feat: add XtreamService for managing IPTV playlists, fetching live channels and VODs, and generating stream URLs.
This commit is contained in:
1 parent
3452208cc3
commit
1c1198f1c1
3 files changed
+88
-18
No files matched your search
@@ -96,11 +96,42 @@ Handler createLiveStreamHandler(Future<PlaylistConfig?> Function(Request) getPla
|
||||
Handler createVodStreamHandler(Future<PlaylistConfig?> Function(Request) getPlaylist) {
|
||||
final router = Router();
|
||||
|
||||
// Route: /api/vod/{streamId}/playlist.m3u8
|
||||
router.get('/<streamId>/playlist.m3u8', (Request request, String streamId) async {
|
||||
// Route: /api/vod/{streamId_with_ext}/playlist.m3u8
|
||||
router.get('/<filename>/playlist.m3u8', (Request request, String filename) async {
|
||||
final playlist = await getPlaylist(request);
|
||||
if (playlist == null) return Response.internalServerError(body: 'No playlist');
|
||||
|
||||
// Parse streamId and extension from filename (e.g., "12345.mp4" -> id="12345", ext="mp4")
|
||||
String streamId = filename;
|
||||
String extension = ''; // No default yet
|
||||
|
||||
if (filename.contains('.')) {
|
||||
final lastDotIndex = filename.lastIndexOf('.');
|
||||
streamId = filename.substring(0, lastDotIndex);
|
||||
extension = filename.substring(lastDotIndex + 1);
|
||||
}
|
||||
|
||||
// If extension is missing, fetch it from API (Robustness fallback)
|
||||
if (extension.isEmpty) {
|
||||
print('[VOD] No extension provided for $streamId, fetching info...');
|
||||
try {
|
||||
final infoUrl = Uri.parse('${playlist.dns}/player_api.php?username=${playlist.username}&password=${playlist.password}&action=get_vod_info&vod_id=$streamId');
|
||||
final response = await http.get(infoUrl);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
if (data['movie_data'] != null && data['movie_data']['container_extension'] != null) {
|
||||
extension = data['movie_data']['container_extension'].toString();
|
||||
print('[VOD] Resolved extension: $extension');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('[VOD] Failed to resolve extension: $e');
|
||||
}
|
||||
|
||||
// Final fallback if API fails
|
||||
if (extension.isEmpty) extension = 'mkv';
|
||||
}
|
||||
|
||||
// Clean up old session if exists
|
||||
if (_vodProcesses.containsKey(streamId)) {
|
||||
print('[VOD] Killing previous session for $streamId');
|
||||
@@ -115,11 +146,8 @@ Handler createVodStreamHandler(Future<PlaylistConfig?> Function(Request) getPlay
|
||||
}
|
||||
streamDir.createSync(recursive: true);
|
||||
|
||||
// Build Upstream URL (Try MKV first, then generic)
|
||||
// Note: We don't know the extension here easily unless passed.
|
||||
// Xtream usually allows access via streamId.mkv or streamId.mp4 regardless of actual file
|
||||
// We'll trust the ID.
|
||||
final targetUrl = '${playlist.dns}/movie/${playlist.username}/${playlist.password}/$streamId.mkv';
|
||||
// Build Upstream URL with correct extension
|
||||
final targetUrl = '${playlist.dns}/movie/${playlist.username}/${playlist.password}/$streamId.$extension';
|
||||
|
||||
print('[VOD] Starting Transcode: $targetUrl');
|
||||
|
||||
@@ -191,8 +219,14 @@ Handler createVodStreamHandler(Future<PlaylistConfig?> Function(Request) getPlay
|
||||
);
|
||||
});
|
||||
|
||||
// Route: /api/vod/{streamId}/segment_{n}.ts (Serve segments)
|
||||
router.get('/<streamId>/<segment>', (Request request, String streamId, String segment) async {
|
||||
// Route: /api/vod/{filename}/segment_{n}.ts (Serve segments)
|
||||
router.get('/<filename>/<segment>', (Request request, String filename, String segment) async {
|
||||
// Extract streamId from filename to find the directory
|
||||
String streamId = filename;
|
||||
if (filename.contains('.')) {
|
||||
streamId = filename.substring(0, filename.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
final file = File('${_hlsTempDir.path}/$streamId/$segment');
|
||||
|
||||
if (!file.existsSync()) {
|
||||
|
||||
@@ -70,18 +70,18 @@ class XtreamService {
|
||||
String getVodStreamUrl(String streamId, String containerExtension) {
|
||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||
|
||||
// Point to new dedicated VOD API (HLS playlist)
|
||||
// Pass extension to backend for correct upstream URL construction
|
||||
final baseUrl = html.window.location.origin;
|
||||
return '$baseUrl/api/vod/$streamId/playlist.m3u8';
|
||||
return '$baseUrl/api/vod/$streamId.$containerExtension/playlist.m3u8';
|
||||
}
|
||||
|
||||
/// Generate stream URL for series episodes
|
||||
String getSeriesStreamUrl(String streamId, String containerExtension) {
|
||||
if (_currentPlaylist == null) throw Exception('No playlist configured');
|
||||
|
||||
// Point to new dedicated VOD API (HLS playlist)
|
||||
// Pass extension to backend for correct upstream URL construction
|
||||
final baseUrl = html.window.location.origin;
|
||||
return '$baseUrl/api/vod/$streamId/playlist.m3u8';
|
||||
return '$baseUrl/api/vod/$streamId.$containerExtension/playlist.m3u8';
|
||||
}
|
||||
|
||||
/// Authenticate and get server info
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
# Fix EPG Overlay Interaction
|
||||
# XtremFlow Development
|
||||
|
||||
- [ ] Analyze `player_screen.dart` for overlap issues.
|
||||
- [ ] Reproduce/Locate the code responsible for EPG overlay.
|
||||
- [ ] Fix the touch event propagation issue.
|
||||
- [x] Verify the fix.
|
||||
Context: Developing a high-performance IPTV Web App with Flutter and Docker.
|
||||
|
||||
Current Focus: Resuming development after environment fix.
|
||||
|
||||
Master Plan:
|
||||
|
||||
## 🧹 Housekeeping
|
||||
|
||||
- [x] Commit pending changes in `xtream_service.dart` (Timeout increase).
|
||||
|
||||
## 🎥 Player Debugging (CRITICAL)
|
||||
|
||||
- [ ] **Fix VOD Playback**: User reports VOD is not playing.
|
||||
- [x] Check VOD URL generation in `xtream_service.dart`. (Fixed upstream URL to include extension)
|
||||
- [x] Check `web/player.html` compatibility with VOD formats. (HLS/Direct support verified)
|
||||
- [x] Verify `PlayerScreen` logic for VOD StreamType. (Correctly passes extension)
|
||||
- [x] Update `streaming_handler.dart` to handle extensions dynamically. (Done)
|
||||
- [x] Rebuild Docker Image to apply backend changes.
|
||||
- [x] Restart Docker containers.
|
||||
- [x] Verify VOD playback. (Failed)
|
||||
- [ ] **Phase 2 Debugging**:
|
||||
- [x] Analyze Docker logs for FFmpeg/Proxy errors. (Found 404/4XX on MKV)
|
||||
- [x] Implement backend auto-detection of extension via Xtream API.
|
||||
- [x] Restart Docker containers.
|
||||
- [x] Restart Docker containers.
|
||||
- [x] Verify VOD playback. (Backend Fallback active)
|
||||
- [ ] **Clean Reinstall** (User Request):
|
||||
- [x] Stop and Remove Containers (`down`).
|
||||
- [/] Force Rebuild and Start (`up --build`).
|
||||
|
||||
## 🎥 Player Improvements (Active Context)
|
||||
|
||||
- [ ] Review `player_screen.dart` controls logic.
|
||||
- [ ] **Goal**: Enhance VOD/Series controls vs Live TV controls.
|
||||
- *Context*: User wants skip/prev buttons to change channels only for Live TV? Need clarification on VOD behavior.
|
||||
|
||||
## 📦 Backlog
|
||||
|
||||
- [x] Resolve ModuleNotFoundError: No module named 'rich' (Done)
|
||||
- [x] Update Docker Image (Timeout increase)
|
||||
Reference in new issue
Block a user