mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-18 07:51:46 +00:00
feat: implement torrent streaming support
Added react-native-torrent-streamer and TorrentService. Enabled magnet link forwarding to external players.
This commit is contained in:
parent
5f49a7f2ab
commit
2d85094508
4 changed files with 97 additions and 6 deletions
|
|
@ -92,6 +92,7 @@
|
||||||
"react-native-safe-area-context": "~5.7.0",
|
"react-native-safe-area-context": "~5.7.0",
|
||||||
"react-native-screens": "^4.24.0",
|
"react-native-screens": "^4.24.0",
|
||||||
"react-native-svg": "^15.15.3",
|
"react-native-svg": "^15.15.3",
|
||||||
|
"react-native-torrent-streamer": "^0.2.3",
|
||||||
"react-native-url-polyfill": "^3.0.0",
|
"react-native-url-polyfill": "^3.0.0",
|
||||||
"react-native-vector-icons": "^10.3.0",
|
"react-native-vector-icons": "^10.3.0",
|
||||||
"react-native-video": "^6.19.0",
|
"react-native-video": "^6.19.0",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { localScraperService } from '../../services/pluginService';
|
||||||
import { VideoPlayerService } from '../../services/videoPlayerService';
|
import { VideoPlayerService } from '../../services/videoPlayerService';
|
||||||
import { streamCacheService } from '../../services/streamCacheService';
|
import { streamCacheService } from '../../services/streamCacheService';
|
||||||
import { tmdbService } from '../../services/tmdbService';
|
import { tmdbService } from '../../services/tmdbService';
|
||||||
|
import { torrentService } from '../../services/torrentService';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
import { TABLET_BREAKPOINT } from './constants';
|
import { TABLET_BREAKPOINT } from './constants';
|
||||||
import {
|
import {
|
||||||
|
|
@ -463,10 +464,26 @@ export const useStreamsScreen = () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block magnet links
|
// Let external players handle magnet links
|
||||||
if (typeof stream.url === 'string' && stream.url.startsWith('magnet:')) {
|
const isMagnet = typeof stream.url === 'string' && stream.url.startsWith('magnet:');
|
||||||
openAlert('Not supported', 'Torrent streaming is not supported yet.');
|
|
||||||
return;
|
// 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
|
// iOS external player
|
||||||
|
|
|
||||||
71
src/services/torrentService.ts
Normal file
71
src/services/torrentService.ts
Normal file
|
|
@ -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<string> => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -29,11 +29,13 @@ export const VideoPlayerService = {
|
||||||
options.releaseDate
|
options.releaseDate
|
||||||
].filter(Boolean).join(' - ');
|
].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', {
|
await IntentLauncher.startActivityAsync('android.intent.action.VIEW', {
|
||||||
data: url,
|
data: url,
|
||||||
flags: 1, // FLAG_ACTIVITY_NEW_TASK
|
flags: 1, // FLAG_ACTIVITY_NEW_TASK
|
||||||
type: 'video/*',
|
type: isMagnet ? 'application/x-bittorrent' : 'video/*',
|
||||||
extra: {
|
extra: {
|
||||||
'android.intent.extra.TITLE': fullTitle,
|
'android.intent.extra.TITLE': fullTitle,
|
||||||
'position': 0, // Start from beginning
|
'position': 0, // Start from beginning
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue