From 2d850945082543ed87558cb32b51dbbede1524b1 Mon Sep 17 00:00:00 2001 From: Darkrock04 Date: Mon, 16 Mar 2026 16:23:06 +0530 Subject: [PATCH] feat: implement torrent streaming support Added react-native-torrent-streamer and TorrentService. Enabled magnet link forwarding to external players. --- package.json | 1 + src/screens/streams/useStreamsScreen.ts | 25 +++++++-- src/services/torrentService.ts | 71 +++++++++++++++++++++++++ src/services/videoPlayerService.ts | 6 ++- 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/services/torrentService.ts diff --git a/package.json b/package.json index 0d691e38..c181b9b7 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "react-native-safe-area-context": "~5.7.0", "react-native-screens": "^4.24.0", "react-native-svg": "^15.15.3", + "react-native-torrent-streamer": "^0.2.3", "react-native-url-polyfill": "^3.0.0", "react-native-vector-icons": "^10.3.0", "react-native-video": "^6.19.0", diff --git a/src/screens/streams/useStreamsScreen.ts b/src/screens/streams/useStreamsScreen.ts index 1dd04336..27ca0419 100644 --- a/src/screens/streams/useStreamsScreen.ts +++ b/src/screens/streams/useStreamsScreen.ts @@ -17,6 +17,7 @@ import { localScraperService } from '../../services/pluginService'; import { VideoPlayerService } from '../../services/videoPlayerService'; import { streamCacheService } from '../../services/streamCacheService'; import { tmdbService } from '../../services/tmdbService'; +import { torrentService } from '../../services/torrentService'; import { logger } from '../../utils/logger'; import { TABLET_BREAKPOINT } from './constants'; import { @@ -463,10 +464,26 @@ export const useStreamsScreen = () => { }); } - // Block magnet links - if (typeof stream.url === 'string' && stream.url.startsWith('magnet:')) { - openAlert('Not supported', 'Torrent streaming is not supported yet.'); - return; + // Let external players handle magnet links + const isMagnet = typeof stream.url === 'string' && stream.url.startsWith('magnet:'); + + // Process magnet links via the torrent service if using internal player + if (isMagnet && settings.preferredPlayer === 'internal' && !settings.useExternalPlayer) { + if (Platform.OS === 'android') { + try { + if (showInfo) showInfo('Starting Torrent Stream... Please wait.'); + const localUrl = await torrentService.startStreaming(stream.url as string); + const torrentStream = { ...stream, url: localUrl }; + navigateToPlayer(torrentStream); + return; + } catch (err) { + openAlert('Torrent Error', 'Failed to start the torrent stream.'); + return; + } + } else { + openAlert('Not supported internally', 'Internal torrent streaming is only supported on Android. Please use an external player.'); + return; + } } // iOS external player diff --git a/src/services/torrentService.ts b/src/services/torrentService.ts new file mode 100644 index 00000000..111cac39 --- /dev/null +++ b/src/services/torrentService.ts @@ -0,0 +1,71 @@ +import { Platform } from 'react-native'; +import { logger } from '../utils/logger'; + +// @ts-ignore - Module might not have types +import TorrentStreamer from 'react-native-torrent-streamer'; + +export const torrentService = { + startStreaming: (url: string): Promise => { + return new Promise((resolve, reject) => { + if (Platform.OS !== 'android') { + reject(new Error('Torrent streaming natively is only supported on Android.')); + return; + } + + try { + TorrentStreamer.start(url); + + const onReady = (data: any) => { + logger.log('[TorrentService] Torrent is ready at:', data.url); + // Remove listener once resolved to prevent memory leaks + TorrentStreamer.removeEventListener('ready', onReady); + resolve(data.url); + }; + + const onError = (error: any) => { + logger.error('[TorrentService] Error streaming torrent:', error); + TorrentStreamer.removeEventListener('error', onError); + reject(error); + }; + + TorrentStreamer.addEventListener('ready', onReady); + TorrentStreamer.addEventListener('error', onError); + + } catch (err) { + logger.error('[TorrentService] Initialization error:', err); + reject(err); + } + }); + }, + + stopStreaming: () => { + if (Platform.OS === 'android') { + try { + TorrentStreamer.stop(); + logger.log('[TorrentService] Stopped torrent stream.'); + } catch (err) { + logger.error('[TorrentService] Failed to stop stream:', err); + } + } + }, + + addListener: (event: 'status' | 'ready' | 'error', callback: (data: any) => void) => { + if (Platform.OS === 'android') { + try { + TorrentStreamer.addEventListener(event, callback); + } catch (err) { + logger.warn('[TorrentService] Could not add listener:', err); + } + } + }, + + removeListener: (event: 'status' | 'ready' | 'error', callback: (data: any) => void) => { + if (Platform.OS === 'android') { + try { + TorrentStreamer.removeEventListener(event, callback); + } catch (err) { + logger.warn('[TorrentService] Could not remove listener:', err); + } + } + } +}; diff --git a/src/services/videoPlayerService.ts b/src/services/videoPlayerService.ts index 055122b2..81a2f0b4 100644 --- a/src/services/videoPlayerService.ts +++ b/src/services/videoPlayerService.ts @@ -29,11 +29,13 @@ export const VideoPlayerService = { options.releaseDate ].filter(Boolean).join(' - '); - // Launch the intent to play the video + // Launch the intent to play the video or handle the magnet link + const isMagnet = url.startsWith('magnet:'); + await IntentLauncher.startActivityAsync('android.intent.action.VIEW', { data: url, flags: 1, // FLAG_ACTIVITY_NEW_TASK - type: 'video/*', + type: isMagnet ? 'application/x-bittorrent' : 'video/*', extra: { 'android.intent.extra.TITLE': fullTitle, 'position': 0, // Start from beginning