Android app now is working, still some problems with series sound but movies are fine. and video works.

This commit is contained in:
2026-01-01 13:40:36 +01:00
parent 4c900ca935
commit 5cecb0cd48
12 changed files with 1545 additions and 179 deletions

510
README.md
View File

@@ -1,204 +1,444 @@
# beStream
<p align="center">
<img src="public/icon.svg" alt="beStream Logo" width="120" height="120">
</p>
A cross-platform movie streaming application with a Netflix-style UI, built with React and capable of running on Android, iOS, Windows, Linux, and macOS.
<h1 align="center">beStream</h1>
## Features
<p align="center">
<strong>A cross-platform streaming application with a Netflix-style interface</strong>
</p>
- 🎬 **Netflix-style UI** - Beautiful, responsive interface with hero banners, horizontal scrolling rows, and smooth animations
- 🔍 **Browse & Search** - Filter movies by genre, quality, rating, and more with infinite scroll
- 📥 **Real Torrent Streaming** - Stream movies via backend server with WebTorrent + FFmpeg transcoding
- 🎥 **HLS Playback** - Adaptive streaming with HLS.js for smooth playback
- 💾 **Offline Downloads** - Download movies for offline viewing
- 📝 **Watchlist** - Save movies to watch later
- 📊 **Watch History** - Track viewing progress and resume playback
- 🌐 **Multi-Platform** - Single codebase for all platforms
<p align="center">
<a href="#features">Features</a> •
<a href="#platforms">Platforms</a> •
<a href="#installation">Installation</a> •
<a href="#development">Development</a> •
<a href="#building">Building</a> •
<a href="#documentation">Documentation</a>
</p>
## Architecture
<p align="center">
<img src="https://img.shields.io/badge/version-2.4.0-blue.svg" alt="Version">
<img src="https://img.shields.io/badge/react-18.3-61dafb.svg" alt="React">
<img src="https://img.shields.io/badge/electron-33.2-47848f.svg" alt="Electron">
<img src="https://img.shields.io/badge/capacitor-7.4-119eff.svg" alt="Capacitor">
<img src="https://img.shields.io/badge/license-private-red.svg" alt="License">
</p>
```
┌─────────────────┐ ┌─────────────────────────────────────┐
│ React App │────▶│ Node.js Backend │
│ (Frontend) │ │ │
│ Port: 5173 │◀────│ WebTorrent ──▶ FFmpeg ──▶ HLS │
└─────────────────┘ │ Port: 3001 │
└─────────────────────────────────────┘
```
---
## Tech Stack
## ✨ Features
| Component | Technology |
|-----------|------------|
| UI Framework | React 18 + TypeScript |
| Build Tool | Vite |
| Styling | TailwindCSS + Framer Motion |
| State Management | Zustand |
| Mobile | Capacitor (iOS/Android) |
| Desktop | Electron (Windows/Linux/macOS) |
| Torrent | WebTorrent |
| Storage | IndexedDB (Dexie.js) |
- 🎬 **Stream Movies & TV Shows** - Real-time streaming via WebTorrent
- 🎨 **Netflix-Style UI** - Beautiful, responsive dark theme interface
- 📱 **Cross-Platform** - Windows, macOS, Linux, and Android support
- 🔍 **Smart Search** - Search across multiple content providers
- 📺 **TV Show Support** - Browse seasons, episodes with auto-play
- 📥 **Download Manager** - Queue and manage downloads
- 📚 **Watch History** - Track your progress across devices
-**Watchlist** - Save content for later
- 🎯 **Quality Selection** - Choose from 480p to 4K
- 🔗 **Integrations** - Connect with Radarr, Sonarr, and Lidarr
- 🌐 **Subtitle Support** - Load external subtitles (SRT)
## Getting Started
---
## 🖥️ Platforms
| Platform | Status | Technology |
|----------|--------|------------|
| Windows | ✅ Supported | Electron |
| macOS | ✅ Supported | Electron |
| Linux | ✅ Supported | Electron |
| Android | ✅ Supported | Capacitor + NodeJS |
| iOS | 🚧 Planned | Capacitor |
| Web | 🔧 Dev Only | Vite |
---
## 📦 Installation
### Prerequisites
- Node.js 18+
- npm or yarn
- For iOS: Xcode 14+ (macOS only)
- For Android: Android Studio with SDK 24+
- For Desktop: No additional requirements
- **Node.js** 18.x or higher
- **npm** 9.x or higher
- **Git**
### Prerequisites
For Android builds:
- **Android Studio** with SDK 33+
- **Java JDK** 17+
- Node.js 18+
- **FFmpeg** - Required for video transcoding
- Windows: `winget install FFmpeg` or download from https://ffmpeg.org
- macOS: `brew install ffmpeg`
- Linux: `sudo apt install ffmpeg`
### Installation
### Quick Start
```bash
# Clone the repository
git clone <repository-url>
git clone https://github.com/yourusername/bestream.git
cd bestream
# Install frontend dependencies
# Install dependencies
npm install
# Install server dependencies
npm run server:install
```
### Development
```bash
# Start both frontend and streaming server
# Start development server
npm run dev:all
# Or start them separately:
npm run dev # Frontend only (http://localhost:5173)
npm run server # Backend only (http://localhost:3001)
# Start Electron development
npm run electron:dev
```
### How Streaming Works
The app will be available at `http://localhost:5173` with the backend server at `http://localhost:3001`.
1. Click "Play" on any movie
2. Backend downloads torrent via WebTorrent
3. FFmpeg transcodes to HLS format in real-time
4. Frontend plays HLS stream with adaptive quality
5. Watch immediately while download continues in background
---
### Building
## 🛠️ Development
### Available Scripts
| Command | Description |
|---------|-------------|
| `npm run dev` | Start Vite dev server (frontend only) |
| `npm run dev:all` | Start frontend + backend server |
| `npm run server` | Start backend server only |
| `npm run build` | Build frontend for production |
| `npm run lint` | Run ESLint |
| `npm run electron:dev` | Start Electron in dev mode |
### Project Structure
```
beStream/
├── src/ # React frontend
│ ├── components/ # UI components
│ │ ├── layout/ # Header, Sidebar, Layout
│ │ ├── movie/ # Hero, MovieRow, MovieCard
│ │ ├── tv/ # TV show components
│ │ ├── player/ # Video player components
│ │ └── ui/ # Button, Modal, Skeleton, etc.
│ ├── pages/ # Route pages
│ ├── hooks/ # Custom React hooks
│ ├── services/ # API and business logic
│ │ ├── api/ # YTS, EZTV, TMDB clients
│ │ ├── streaming/ # Streaming service
│ │ └── server/ # Server management
│ ├── stores/ # Zustand state stores
│ ├── types/ # TypeScript definitions
│ └── utils/ # Utility functions
├── server/ # Node.js backend
│ └── src/
│ ├── routes/ # Express routes
│ │ ├── stream.js # Streaming endpoints
│ │ └── proxy.js # API proxy endpoints
│ └── services/ # Backend services
│ ├── torrentManager.js # WebTorrent client
│ └── transcoder.js # FFmpeg transcoding
├── electron/ # Electron main process
│ ├── main.ts # Main process entry
│ └── preload.ts # Preload scripts
├── android/ # Android native project
├── scripts/ # Build scripts
└── docs/ # Documentation
```
### Tech Stack
**Frontend:**
- React 18 with TypeScript
- Vite for bundling
- Tailwind CSS for styling
- Zustand for state management
- Framer Motion for animations
- React Router for navigation
- HLS.js for adaptive streaming
**Backend:**
- Express.js web server
- WebTorrent for P2P streaming
- FFmpeg for HLS transcoding (desktop only)
- WebSocket for real-time updates
**Desktop:**
- Electron 33
- Custom frameless window
- System tray integration
**Mobile:**
- Capacitor 7
- capacitor-nodejs plugin
- Embedded Node.js runtime
---
## 🏗️ Building
### Desktop (Electron)
```bash
# Build web version
npm run build
# Build for Android
npm run build:android
# Build for iOS
npm run build:ios
# Build for Windows
# Windows
npm run build:win
# Build for macOS
# macOS
npm run build:mac
# Build for Linux
# Linux
npm run build:linux
```
## Project Structure
**Output:** `release/` directory
```
bestream/
├── src/
│ ├── components/ # React components
│ │ ├── ui/ # Base UI components
│ │ ├── movie/ # Movie-related components
│ │ ├── player/ # Video player components
│ │ └── layout/ # Layout components
│ ├── pages/ # Route pages
│ ├── services/ # API and business logic
│ │ ├── api/ # YTS API client
│ │ ├── torrent/ # WebTorrent service
│ │ ├── subtitles/ # OpenSubtitles service
│ │ └── storage/ # IndexedDB service
│ ├── stores/ # Zustand state stores
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Helper utilities
│ └── types/ # TypeScript definitions
├── electron/ # Electron main process
├── public/ # Static assets
└── docs/ # Documentation
Built files:
- Windows: `beStream Setup x.x.x.exe`
- macOS: `beStream-x.x.x.dmg`
- Linux: `beStream-x.x.x.AppImage`
### Android
```bash
# Debug APK
npm run build:android:apk
# Release APK (signed)
npm run build:android:release
```
## API Reference
**Output:** `android/app/build/outputs/apk/`
The app uses the YTS API for movie data:
### Build Process (Android)
- **List Movies**: `GET /list_movies.json`
- **Movie Details**: `GET /movie_details.json`
- **Movie Suggestions**: `GET /movie_suggestions.json`
- **Parental Guides**: `GET /movie_parental_guides.json`
The Android build process involves several steps:
See the [YTS API Documentation](https://yts.mx/api) for more details.
1. **Build Frontend** (`npm run build`)
- TypeScript compilation
- Vite production build
- Output: `dist/`
## Platform-Specific Notes
2. **Bundle Server** (`npm run copy-server-to-android`)
- Bundle with esbuild into single file
- Apply platform polyfills
- Output: `dist/nodejs-v3/index.js`
### Mobile (iOS/Android)
3. **Sync Capacitor** (`npx cap sync android`)
- Copy web assets to Android
- Update native plugins
- Torrent streaming uses WebRTC-based WebTorrent
- Downloads are stored in app-specific storage
- Supports picture-in-picture playback
4. **Build APK** (`gradlew assembleDebug`)
- Compile Android project
- Package native + web resources
### Desktop (Windows/Linux/macOS)
---
- Full WebTorrent support with DHT
- Downloads can be saved to any location
- System tray integration
- Native menu bar
## 📚 Documentation
## Configuration
Detailed documentation is available in the `docs/` directory:
| Document | Description |
|----------|-------------|
| [DOCUMENTATION.md](docs/DOCUMENTATION.md) | Full technical documentation |
| [GETTING_API_KEYS.md](docs/GETTING_API_KEYS.md) | Setup external API keys |
| [OPENING_PORTS.md](docs/OPENING_PORTS.md) | Network configuration guide |
---
## 🔧 Configuration
### Environment Variables
Create a `.env` file in the root directory:
```env
# Optional: OpenSubtitles API key for subtitle support
VITE_OPENSUBTITLES_API_KEY=your-api-key
# API Configuration (optional)
VITE_API_URL=http://localhost:3001
# EZTV Authentication (optional)
VITE_EZTV_USERNAME=your_username
VITE_EZTV_PASSWORD=your_password
```
### Capacitor Configuration
### App Settings
Edit `capacitor.config.ts` to customize mobile settings.
Settings are stored locally and persist across sessions:
### Electron Configuration
| Setting | Default | Description |
|---------|---------|-------------|
| Preferred Quality | 1080p | Default streaming quality |
| Language | en | Preferred content language |
| Auto Play | true | Auto-play next episode |
| Theme | dark | UI theme (dark/light) |
| Max Downloads | 3 | Concurrent download limit |
Edit `electron/main.ts` to customize desktop settings.
### Integration Settings
## Legal Notice
Connect to media managers for enhanced features:
This application is provided for educational purposes only. It does not host any content and simply provides an interface to the YTS API. Users are responsible for complying with local laws regarding content streaming and downloading.
- **Radarr** - Movie collection management
- **Sonarr** - TV show collection management
- **Lidarr** - Music collection management
## License
Configure in Settings → Integrations.
MIT License - See LICENSE file for details.
---
## Contributing
## 🔌 API Endpoints
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
### Backend Server (Port 3001)
## Support
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/health` | GET | Server health check |
| `/api/stream/start` | POST | Start streaming session |
| `/api/stream/:id/status` | GET | Get session status |
| `/api/stream/:id/video` | GET | Stream video |
| `/api/stream/:id/hls` | POST | Start HLS transcoding |
| `/api/eztv/*` | GET | EZTV API proxy |
| `/api/yts/*` | GET | YTS API proxy |
| `/api/tmdb/*` | GET | TMDB API proxy |
For issues and feature requests, please use the GitHub issue tracker.
### WebSocket (Port 3001/ws)
Real-time streaming progress updates:
```javascript
// Connect
const ws = new WebSocket('ws://localhost:3001/ws');
// Subscribe to session
ws.send(JSON.stringify({ type: 'subscribe', sessionId: 'xxx' }));
// Receive updates
ws.onmessage = (e) => {
const { type, progress, downloadSpeed, peers } = JSON.parse(e.data);
};
```
---
## 📱 Android Notes
### Requirements
- Android 7.0 (API 24) or higher
- ~100MB storage space
- Internet connection
### Limitations
Due to platform constraints, the Android version has some limitations:
| Feature | Desktop | Android |
|---------|---------|---------|
| HLS Transcoding | ✅ | ❌ |
| uTP Protocol | ✅ | ❌ |
| File Downloads | ✅ | Limited |
| FFmpeg Processing | ✅ | ❌ |
### Debugging
```bash
# View app logs
adb logcat | grep com.bestream.app
# View Node.js server logs
adb logcat | grep -E "(NodeJS|beStream Server)"
# Install debug build
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
# Uninstall and reinstall
adb uninstall com.bestream.app
adb install android/app/build/outputs/apk/debug/app-debug.apk
```
---
## 🐛 Troubleshooting
### Common Issues
**Server not starting:**
```bash
# Check if port is in use
netstat -an | grep 3001
# Kill process on port
npx kill-port 3001
```
**Android build fails:**
```bash
# Clean and rebuild
cd android
./gradlew clean
cd ..
npm run build:android:apk
```
**Video not playing:**
- Check server health: `curl http://localhost:3001/health`
- Verify torrent has seeders
- Try different quality option
- Check browser console for errors
**CORS errors in development:**
- Ensure Vite proxy is configured
- Check that backend is running on port 3001
---
## 🤝 Contributing
We welcome contributions! Please follow these steps:
1. **Fork** the repository
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
3. **Commit** changes: `git commit -m 'Add amazing feature'`
4. **Push** to branch: `git push origin feature/amazing-feature`
5. **Open** a Pull Request
### Guidelines
- Write TypeScript for all new code
- Follow existing code style (ESLint + Prettier)
- Use functional components with hooks
- Add tests for new features
- Update documentation as needed
---
## ⚠️ Disclaimer
This application is intended for **educational and personal use only**. Users are solely responsible for ensuring they have the legal right to stream or download any content. The developers:
- Do not host any content
- Do not provide torrents or links to copyrighted material
- Are not responsible for user actions
- Encourage compliance with local laws and regulations
---
## 📄 License
This project is **private and proprietary**. Unauthorized copying, modification, distribution, or use of this software is strictly prohibited without explicit permission.
---
## 🙏 Acknowledgments
Special thanks to these amazing projects:
- [WebTorrent](https://webtorrent.io) - Streaming torrent library
- [Electron](https://electronjs.org) - Desktop application framework
- [Capacitor](https://capacitorjs.com) - Cross-platform native runtime
- [React](https://reactjs.org) - UI library
- [Tailwind CSS](https://tailwindcss.com) - Utility-first CSS
- [YTS](https://yts.mx) - Movie data provider
- [EZTV](https://eztv.re) - TV show data provider
- [TMDB](https://themoviedb.org) - Movie and TV metadata
---
<p align="center">
<sub>Built with ❤️ by the beStream Team</sub>
</p>
<p align="center">
<sub>Version 2.4.0 • Last Updated: January 2026</sub>
</p>

758
docs/DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,758 @@
# beStream Technical Documentation
## Table of Contents
1. [Overview](#overview)
2. [Architecture](#architecture)
3. [Project Structure](#project-structure)
4. [Frontend Application](#frontend-application)
5. [Backend Server](#backend-server)
6. [Platform Support](#platform-support)
7. [API Reference](#api-reference)
8. [State Management](#state-management)
9. [Services](#services)
10. [Build System](#build-system)
11. [Configuration](#configuration)
12. [Troubleshooting](#troubleshooting)
---
## Overview
beStream is a cross-platform movie and TV show streaming application with a Netflix-style UI. It supports:
- **Desktop**: Windows, macOS, Linux (via Electron)
- **Mobile**: Android (via Capacitor with embedded Node.js)
- **Web**: Browser-based development mode
The application streams content via WebTorrent, providing real-time torrent-to-stream functionality with optional HLS transcoding for broader device compatibility.
### Key Features
- 🎬 Stream movies and TV shows in real-time
- 📺 Netflix-style responsive UI
- 🔍 Search and browse content from YTS and EZTV
- 📱 Cross-platform support (Desktop & Android)
- ⬇️ Download management with progress tracking
- 📚 Watch history and watchlist
- 🎯 Quality selection (480p, 720p, 1080p, 2160p)
- 🔄 Integration with Radarr/Sonarr/Lidarr
---
## Architecture
### High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Pages │ │Components│ │ Stores │ │ Services │ │
│ │ │ │ │ │ (Zustand)│ │ (API, Streaming) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│ HTTP/WebSocket
┌─────────────────────────────────────────────────────────────────┐
│ Backend Server (Node.js) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Express │ │ WebTorrent │ │ FFmpeg Transcoder │ │
│ │ Routes │ │ Manager │ │ (HLS streaming) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### Platform-Specific Architecture
#### Electron (Desktop)
```
┌─────────────────────────────────────────┐
│ Electron App │
│ ┌─────────────────────────────────┐ │
│ │ Main Process │ │
│ │ - Window management │ │
│ │ - IPC handling │ │
│ │ - Server process spawn │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Renderer Process │ │
│ │ - React Frontend │ │
│ │ - Preload scripts │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Server Process (fork) │ │
│ │ - Express + WebTorrent │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
#### Capacitor (Android)
```
┌─────────────────────────────────────────┐
│ Android Application │
│ ┌─────────────────────────────────┐ │
│ │ WebView │ │
│ │ - React Frontend │ │
│ │ - Capacitor Bridge │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ capacitor-nodejs plugin │ │
│ │ - Embedded Node.js runtime │ │
│ │ - Express + WebTorrent bundle │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
---
## Project Structure
```
beStream/
├── src/ # Frontend React application
│ ├── components/ # Reusable UI components
│ │ ├── calendar/ # Calendar-related components
│ │ ├── integration/ # *arr integration components
│ │ ├── layout/ # Layout components (Header, Sidebar)
│ │ ├── movie/ # Movie display components
│ │ ├── music/ # Music-related components
│ │ ├── player/ # Video player components
│ │ ├── tv/ # TV show components
│ │ └── ui/ # Generic UI components (Button, Modal, etc.)
│ ├── pages/ # Page components (routes)
│ ├── hooks/ # Custom React hooks
│ ├── services/ # API and business logic services
│ │ ├── api/ # External API clients (YTS, EZTV, TMDB)
│ │ ├── integration/ # *arr service integrations
│ │ ├── server/ # Server management
│ │ ├── storage/ # Local storage services
│ │ ├── streaming/ # Streaming service client
│ │ ├── subtitles/ # Subtitle handling
│ │ └── torrent/ # Torrent utilities
│ ├── stores/ # Zustand state stores
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ └── constants/ # Application constants
├── server/ # Backend Node.js server
│ ├── src/
│ │ ├── routes/ # Express route handlers
│ │ ├── services/ # Backend services (torrent, transcoder)
│ │ ├── utils/ # Server utilities
│ │ └── polyfills/ # Platform polyfills
│ ├── bundle.js # esbuild bundler script
│ └── package.json # Server dependencies
├── electron/ # Electron main process
│ ├── main.ts # Main process entry
│ ├── preload.ts # Preload script
│ └── tsconfig.json # Electron TypeScript config
├── android/ # Android native project (Capacitor)
├── scripts/ # Build and utility scripts
├── docs/ # Documentation
├── public/ # Static assets
├── dist/ # Built frontend
├── dist-electron/ # Built Electron files
└── release/ # Release builds
```
---
## Frontend Application
### Technology Stack
- **React 18** - UI framework
- **TypeScript** - Type safety
- **Vite** - Build tool and dev server
- **Tailwind CSS** - Utility-first styling
- **Zustand** - State management
- **React Router** - Client-side routing
- **Framer Motion** - Animations
- **Axios** - HTTP client
- **HLS.js** - HLS video playback
### Pages
| Page | Route | Description |
|------|-------|-------------|
| Home | `/` | Hero section with trending content |
| Browse | `/browse`, `/browse/:genre` | Browse movies by genre |
| Search | `/search` | Search movies and TV shows |
| Movie Details | `/movie/:id` | Movie information and stream options |
| Player | `/player/:id` | Video player with streaming |
| TV Shows | `/tv` | Browse TV shows |
| TV Show Details | `/tv/:id` | Season/episode browser |
| TV Player | `/tv/:showId/:seasonId/:episodeId` | TV episode player |
| Watchlist | `/watchlist` | Saved items |
| Downloads | `/downloads` | Download manager |
| History | `/history` | Watch history |
| Queue | `/queue` | Download queue |
| Settings | `/settings` | App configuration |
### Component Architecture
#### Layout Components
- **Layout** - Main app layout with header and sidebar
- **Header** - Top navigation with search
- **Sidebar** - Navigation menu
#### Movie Components
- **Hero** - Featured content carousel
- **MovieRow** - Horizontal scrollable movie list
- **MovieCard** - Individual movie thumbnail
- **MovieGrid** - Grid layout for movies
#### Player Components
- **StreamingPlayer** - Main video player wrapper
- **VideoPlayer** - Core video element with controls
- **NextEpisodeOverlay** - Auto-play next episode UI
### Hooks
| Hook | Purpose |
|------|---------|
| `useMovies` | Fetch and cache movie data |
| `useSeries` | Fetch TV show data |
| `useCalendar` | Episode calendar data |
| `useIntegration` | *arr service integration |
| `useAsyncData` | Generic async data fetching |
| `useMusic` | Music service data |
---
## Backend Server
### Technology Stack
- **Express.js** - Web framework
- **WebTorrent** - Torrent client
- **FFmpeg** (optional) - Video transcoding
- **ws** - WebSocket server
- **mime-types** - MIME type detection
### Server Entry Point
The server initializes in `server/src/index.js`:
1. Creates Express app with security headers
2. Sets up CORS for cross-origin requests
3. Registers API routes
4. Creates WebSocket server for real-time updates
5. Binds to `localhost:3001` (or `0.0.0.0:3001` on Android)
### Routes
#### Stream Routes (`/api/stream`)
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/start` | Start a new streaming session |
| GET | `/:sessionId/status` | Get session status |
| GET | `/:sessionId/video` | Stream video (supports range requests) |
| POST | `/:sessionId/hls` | Start HLS transcoding |
| GET | `/:sessionId/hls/playlist.m3u8` | Get HLS playlist |
| GET | `/:sessionId/hls/:segment` | Get HLS segment |
| DELETE | `/:sessionId` | End streaming session |
#### Proxy Routes (`/api`)
| Endpoint | Target | Purpose |
|----------|--------|---------|
| `/api/eztv/*` | eztvx.to | TV show torrents |
| `/api/yts/*` | yts.mx | Movie torrents |
| `/api/tmdb/*` | themoviedb.org | Movie/TV metadata |
### Services
#### TorrentManager
Manages WebTorrent client and streaming sessions:
```javascript
class TorrentManager {
// Start a new torrent session
async startSession(sessionId, torrentHash, movieName, onProgress)
// Create readable stream for video file
createReadStream(sessionId, options)
// Get session status (progress, speed, peers)
getSessionStatus(sessionId)
// Clean up session
destroySession(sessionId)
}
```
Key features:
- Session reuse for duplicate torrents
- Range request support for seeking
- Progress tracking via callbacks
- Platform-aware cache directories
#### Transcoder
Handles FFmpeg-based HLS transcoding (desktop only):
```javascript
class Transcoder {
// Get video file information
getVideoInfo(inputPath)
// Start HLS transcoding from file
startHlsTranscode(sessionId, inputPath, onProgress, quality)
// Start HLS transcoding from stream
startHlsStreamTranscode(sessionId, inputStream, onProgress, quality)
// Clean up transcoding session
cleanup(sessionId)
}
```
Note: FFmpeg transcoding is disabled on Android due to binary unavailability.
---
## Platform Support
### Electron (Desktop)
The Electron build provides:
- Native window with frameless design (macOS) or custom frame (Windows/Linux)
- System tray integration
- File system access for downloads
- Embedded server process
#### Main Process (`electron/main.ts`)
- Window creation and management
- Server process lifecycle
- IPC handlers for native features
- Menu and tray setup
#### Preload Script (`electron/preload.ts`)
Exposes safe APIs to renderer:
- `window.electron.openExternal(url)` - Open URLs in browser
- `window.electron.getDownloadsPath()` - Get downloads directory
- `window.electron.showItemInFolder(path)` - Show file in explorer
### Capacitor (Android)
The Android build uses:
- Capacitor WebView for UI
- `capacitor-nodejs` plugin for embedded Node.js
- Bundled server (single `index.js` file)
#### Build Process
1. Build frontend with Vite
2. Bundle server with esbuild
3. Copy bundle to Android assets
4. Sync Capacitor
5. Build APK with Gradle
#### Platform Considerations
- **No FFmpeg**: Transcoding disabled, direct video streaming only
- **No uTP**: WebTorrent uTP disabled (native module incompatible)
- **Cache Directory**: Uses `/data/data/com.bestream.app/files/` instead of `/tmp`
- **Network**: Server binds to `0.0.0.0` for WebView access
---
## API Reference
### External APIs
#### YTS API
Movie data and torrents from YTS.
```typescript
// List movies with filters
ytsApi.listMovies({
limit: 20,
page: 1,
quality: '1080p',
genre: 'action',
sort_by: 'rating',
order_by: 'desc',
query_term: 'search term'
})
// Get movie details
ytsApi.getMovieDetails({ movie_id: 12345 })
// Get suggestions
ytsApi.getMovieSuggestions(12345)
```
#### EZTV API
TV show torrents from EZTV.
```typescript
// Get torrents for a show (by IMDB ID)
tvDiscoveryApi.getTorrents('tt1234567')
// Search episode torrents
tvDiscoveryApi.searchEpisodeTorrents(showTitle, season, episode, imdbId)
```
#### TMDB API
TV show metadata from The Movie Database.
```typescript
// Get trending shows
tvDiscoveryApi.getTrending()
// Get show details
tvDiscoveryApi.getShowDetails(showId)
// Get season details
tvDiscoveryApi.getSeasonDetails(showId, seasonNumber)
```
### Internal APIs
#### Streaming Service
```typescript
// Start a stream
streamingService.startStream(hash, name, quality)
// Connect to WebSocket updates
streamingService.connect(sessionId)
// Subscribe to progress updates
streamingService.subscribe(sessionId, callback)
// Get video URL
streamingService.getVideoUrl(sessionId)
// Check server health
streamingService.checkHealth()
```
---
## State Management
The app uses Zustand for state management with persistence.
### Stores
#### Settings Store
```typescript
interface SettingsState {
settings: {
preferredQuality: '480p' | '720p' | '1080p' | '2160p';
preferredLanguage: string;
autoPlay: boolean;
notifications: boolean;
downloadPath: string;
maxConcurrentDownloads: number;
theme: 'dark' | 'light';
};
updateSettings(settings: Partial<AppSettings>): void;
resetSettings(): void;
}
```
#### History Store
```typescript
interface HistoryState {
history: HistoryItem[];
addToHistory(item: HistoryItem): void;
updateProgress(id: number, progress: number): void;
getProgress(id: number): HistoryItem | undefined;
removeFromHistory(id: number): void;
clearHistory(): void;
}
```
#### Watchlist Store
```typescript
interface WatchlistState {
watchlist: WatchlistItem[];
addToWatchlist(item: WatchlistItem): void;
removeFromWatchlist(id: number): void;
isInWatchlist(id: number): boolean;
}
```
#### Download Store
```typescript
interface DownloadState {
downloads: Download[];
addDownload(download: Download): void;
updateDownload(id: string, updates: Partial<Download>): void;
removeDownload(id: string): void;
}
```
#### Integration Store
```typescript
interface IntegrationState {
radarrUrl: string;
radarrApiKey: string;
sonarrUrl: string;
sonarrApiKey: string;
lidarrUrl: string;
lidarrApiKey: string;
// ... setters
}
```
---
## Services
### API Services (`src/services/api/`)
- **yts.ts** - YTS API client for movies
- **tvDiscovery.ts** - TMDB + EZTV for TV shows
- **radarr.ts** - Radarr integration
- **sonarr.ts** - Sonarr integration
- **lidarr.ts** - Lidarr integration
### Streaming Services (`src/services/streaming/`)
- **streamingService.ts** - Main streaming client
- HTTP requests to backend
- WebSocket connection management
- Session lifecycle
### Server Services (`src/services/server/`)
- **serverManager.ts** - Server lifecycle management
- Start/stop server (Capacitor)
- Health checks
- Automatic restarts
### Storage Services (`src/services/storage/`)
- Local database (Dexie/IndexedDB)
- Download management
- Offline data caching
---
## Build System
### Development
```bash
# Frontend only
npm run dev
# Frontend + Server
npm run dev:all
# Electron development
npm run electron:dev
```
### Production Builds
```bash
# Web build
npm run build
# Electron builds
npm run build:win # Windows
npm run build:mac # macOS
npm run build:linux # Linux
# Android build
npm run build:android:apk # Debug APK
npm run build:android:release # Release APK
```
### Build Pipeline (Android)
1. **Frontend Build** (`npm run build`)
- TypeScript compilation
- Vite production build
- Output: `dist/`
2. **Server Install** (`npm run server:install`)
- Install server dependencies
3. **Server Bundle** (`npm run copy-server-to-android`)
- Bundle with esbuild
- Single file output
- Platform-aware polyfills
- Output: `dist/nodejs-v3/index.js`
4. **Capacitor Sync** (`npx cap sync android`)
- Copy web assets
- Update native project
5. **Gradle Build** (`gradlew assembleDebug`)
- Compile Android project
- Output: `android/app/build/outputs/apk/`
---
## Configuration
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `VITE_API_URL` | Backend server URL | `http://localhost:3001` |
| `PORT` | Server port | `3001` |
| `NODE_ENV` | Environment mode | `development` |
| `VITE_EZTV_USERNAME` | EZTV authentication | - |
| `VITE_EZTV_PASSWORD` | EZTV authentication | - |
### Capacitor Config (`capacitor.config.ts`)
```typescript
{
appId: 'com.bestream.app',
appName: 'beStream',
webDir: 'dist',
plugins: {
CapacitorNodeJS: {
nodeDir: 'nodejs-v3',
startMode: 'manual',
},
},
android: {
allowMixedContent: true,
cleartext: true,
},
}
```
### Vite Config (`vite.config.ts`)
- React plugin
- Path aliases (`@/``src/`)
- Development proxy for APIs
- Rollup externals for Capacitor
### Tailwind Config (`tailwind.config.js`)
Custom theme with Netflix-inspired colors:
- `netflix-black`: `#141414`
- `netflix-red`: `#E50914`
- Custom spacing and breakpoints
---
## Troubleshooting
### Common Issues
#### Server Not Starting (Android)
**Symptoms**: App shows "Server not running" error
**Solutions**:
1. Check Logcat for Node.js errors
2. Verify `nodejs-v3/index.js` exists in APK
3. Ensure `builtin_modules` placeholder exists
4. Check for native module errors (node-datachannel, uTP)
#### Video Not Playing
**Symptoms**: Black screen or loading forever
**Solutions**:
1. Check server health: `http://localhost:3001/health`
2. Verify torrent has seeders
3. Check browser console for errors
4. Try different quality/torrent
#### CORS Errors
**Symptoms**: Network errors in browser console
**Solutions**:
1. Ensure server CORS is configured
2. Check proxy configuration in Vite
3. Verify API URLs are correct
#### Build Failures (Android)
**Symptoms**: Gradle build errors
**Solutions**:
1. Clean build: `cd android && gradlew clean`
2. Sync Capacitor: `npx cap sync android`
3. Check Java/Gradle versions
4. Verify Android SDK installation
### Debug Tools
#### Logcat (Android)
```bash
# All app logs
adb logcat | grep com.bestream.app
# Node.js logs only
adb logcat | grep -E "(NodeJS|beStream)"
```
#### Server Health Check
```bash
curl http://localhost:3001/health
```
#### WebSocket Testing
```javascript
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', sessionId: 'test' }));
ws.onmessage = (e) => console.log(JSON.parse(e.data));
```
---
## Contributing
### Code Style
- TypeScript for all new code
- ESLint + Prettier for formatting
- Functional components with hooks
- Named exports preferred
### Testing
- Unit tests for utilities
- Integration tests for services
- E2E tests for critical flows
### Pull Request Process
1. Create feature branch
2. Make changes with tests
3. Update documentation
4. Submit PR with description
5. Pass CI checks
6. Get review approval
---
## License
This project is private and proprietary. See LICENSE file for details.

View File

@@ -56,8 +56,9 @@ function copyServerToAndroid() {
writeFileSync(join(distNodejsDir, 'package.json'), JSON.stringify(pkg, null, 2));
}
// 6. Handle builtin_modules (Capacitor requirement)
const pluginBuiltinModules = join(rootDir, 'node_modules', 'capacitor-nodejs', 'android', 'src', 'main', 'assets', 'builtin_modules');
// 6. Handle builtin_modules (Capacitor NodeJS requirement)
// The plugin expects a builtin_modules folder in assets, but the source only has .gitkeep
// which Android's AssetManager ignores. We must create a real file.
const androidBuiltinModules = join(rootDir, 'android', 'app', 'src', 'main', 'assets', 'builtin_modules');
if (!existsSync(dirname(androidBuiltinModules))) {
@@ -68,25 +69,14 @@ function copyServerToAndroid() {
rmSync(androidBuiltinModules, { recursive: true, force: true });
}
if (existsSync(pluginBuiltinModules)) {
console.log('Copying builtin_modules...');
// Simple recursive copy for builtin_modules
const copyRecursive = (src, dest) => {
const stats = statSync(src);
if (stats.isDirectory()) {
mkdirSync(dest, { recursive: true });
readdirSync(src).forEach(child => {
copyRecursive(join(src, child), join(dest, child));
});
} else {
copyFileSync(src, dest);
}
};
copyRecursive(pluginBuiltinModules, androidBuiltinModules);
} else {
mkdirSync(androidBuiltinModules, { recursive: true });
writeFileSync(join(androidBuiltinModules, 'README.txt'), 'Placeholder for builtin_modules');
}
// Always create the directory with a proper placeholder file
// Android's AssetManager ignores .gitkeep and empty directories
console.log('Creating builtin_modules placeholder...');
mkdirSync(androidBuiltinModules, { recursive: true });
writeFileSync(
join(androidBuiltinModules, 'README.txt'),
'Placeholder for capacitor-nodejs builtin_modules directory.\nThis file ensures Android AssetManager recognizes this folder.'
);
console.log('✓ Server bundled and copied successfully to android/assets/nodejs-v3');
console.log(' NOTE: node_modules were explicitely SKIPPED because we are using a bundle.');

View File

@@ -23,6 +23,10 @@ try {
'fsevents', // macOS only
'electron', // Not available in this environment
],
alias: {
// Provide stub for native WebRTC module on Android (native .node binaries don't work)
'node-datachannel': resolve(__dirname, 'src/polyfills/node-datachannel-stub.js'),
},
loader: {
'.node': 'file', // Handle .node files if any
},
@@ -37,12 +41,12 @@ try {
format: 'esm',
banner: {
js: `
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const require = createRequire(import.meta.url);
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import { createRequire as _createRequire } from 'module';
import { fileURLToPath as _fileURLToPath } from 'url';
import { dirname as _dirname } from 'path';
const require = _createRequire(import.meta.url);
const __filename = _fileURLToPath(import.meta.url);
const __dirname = _dirname(__filename);
`,
},
});

View File

@@ -1,12 +1,22 @@
// Early startup log for debugging
console.log('[beStream Server] Starting initialization...');
console.log('[beStream Server] Platform:', process.platform);
console.log('[beStream Server] Node version:', process.version);
console.log('[beStream Server] CWD:', process.cwd());
import express from 'express';
import cors from 'cors';
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
import { streamRouter } from './routes/stream.js';
import { proxyRouter } from './routes/proxy.js';
import { torrentManager } from './services/torrentManager.js';
console.log('[beStream Server] Imports loaded successfully');
const app = express();
const PORT = process.env.PORT || 3001;
console.log('[beStream Server] Will listen on port:', PORT);
// Security headers middleware
app.use((req, res, next) => {
@@ -43,6 +53,7 @@ app.get('/health', (req, res) => {
// API Routes
app.use('/api/stream', streamRouter);
app.use('/api', proxyRouter); // Proxy routes for EZTV, YTS, TMDB
// Create HTTP server
const server = createServer(app);
@@ -94,7 +105,10 @@ process.on('SIGTERM', async () => {
// On other platforms, localhost is fine
const HOST = process.platform === 'android' ? '0.0.0.0' : 'localhost';
console.log('[beStream Server] Attempting to bind to', HOST + ':' + PORT);
server.listen(PORT, HOST, () => {
console.log('[beStream Server] SUCCESS! Server is now listening');
console.log(`
╔═══════════════════════════════════════════════════════╗
║ ║
@@ -108,3 +122,8 @@ server.listen(PORT, HOST, () => {
`);
});
server.on('error', (err) => {
console.error('[beStream Server] ERROR binding to port:', err.message);
console.error('[beStream Server] Full error:', err);
});

View File

@@ -0,0 +1,58 @@
/**
* Stub for node-datachannel native module
* This module cannot work on Android because it requires compiled native binaries.
* WebTorrent uses this for WebRTC peer connections, but on mobile we can fall back
* to websocket-based trackers which don't require WebRTC.
*/
class PeerConnection {
constructor() {
console.warn('[node-datachannel-stub] PeerConnection is not available on this platform');
}
createDataChannel() {
throw new Error('WebRTC DataChannel is not available on this platform');
}
setLocalDescription() {}
setRemoteDescription() {}
addRemoteCandidate() {}
close() {}
onLocalDescription() {}
onLocalCandidate() {}
onStateChange() {}
onGatheringStateChange() {}
onDataChannel() {}
}
class DataChannel {
constructor() {
console.warn('[node-datachannel-stub] DataChannel is not available on this platform');
}
send() {}
close() {}
onOpen() {}
onClosed() {}
onError() {}
onMessage() {}
}
// Export stubs
export { PeerConnection, DataChannel };
export const RtcpReceivingSession = class {};
export const Track = class {};
export const Video = class {};
export const Audio = class {};
// Default export for CommonJS compatibility
export default {
PeerConnection,
DataChannel,
RtcpReceivingSession,
Track,
Video,
Audio,
};

145
server/src/routes/proxy.js Normal file
View File

@@ -0,0 +1,145 @@
import express from 'express';
import https from 'https';
import http from 'http';
const router = express.Router();
// Helper function to make HTTP/HTTPS requests
function makeRequest(targetUrl, options = {}) {
return new Promise((resolve, reject) => {
const url = new URL(targetUrl);
const isHttps = url.protocol === 'https:';
const client = isHttps ? https : http;
const requestOptions = {
hostname: url.hostname,
port: url.port || (isHttps ? 443 : 80),
path: url.pathname + url.search,
method: options.method || 'GET',
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.9',
...options.headers,
},
timeout: 30000,
};
// Disable certificate verification for problematic sites
if (isHttps) {
requestOptions.rejectUnauthorized = false;
}
const req = client.request(requestOptions, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
resolve({
statusCode: res.statusCode,
headers: res.headers,
body: data,
});
});
});
req.on('error', reject);
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timeout'));
});
if (options.body) {
req.write(options.body);
}
req.end();
});
}
/**
* EZTV API Proxy
* Proxies requests to https://eztvx.to/api
*/
router.get('/eztv/*', async (req, res) => {
try {
// Build the target URL
const path = req.params[0] || '';
const queryString = new URLSearchParams(req.query).toString();
const targetUrl = `https://eztvx.to/api/${path}${queryString ? '?' + queryString : ''}`;
console.log(`[Proxy] EZTV request: ${targetUrl}`);
const response = await makeRequest(targetUrl);
// Set CORS headers
res.set('Access-Control-Allow-Origin', '*');
res.set('Content-Type', 'application/json');
res.status(response.statusCode).send(response.body);
} catch (error) {
console.error('[Proxy] EZTV error:', error.message);
res.status(500).json({
error: 'Proxy error',
message: error.message,
torrents: [],
torrents_count: 0
});
}
});
/**
* YTS API Proxy
* Proxies requests to https://yts.mx/api/v2
*/
router.get('/yts/*', async (req, res) => {
try {
const path = req.params[0] || '';
const queryString = new URLSearchParams(req.query).toString();
const targetUrl = `https://yts.mx/api/v2/${path}${queryString ? '?' + queryString : ''}`;
console.log(`[Proxy] YTS request: ${targetUrl}`);
const response = await makeRequest(targetUrl);
res.set('Access-Control-Allow-Origin', '*');
res.set('Content-Type', 'application/json');
res.status(response.statusCode).send(response.body);
} catch (error) {
console.error('[Proxy] YTS error:', error.message);
res.status(500).json({
error: 'Proxy error',
message: error.message,
data: { movies: [] }
});
}
});
/**
* TMDB API Proxy
* Proxies requests to https://api.themoviedb.org/3
*/
router.get('/tmdb/*', async (req, res) => {
try {
const path = req.params[0] || '';
const queryString = new URLSearchParams(req.query).toString();
const targetUrl = `https://api.themoviedb.org/3/${path}${queryString ? '?' + queryString : ''}`;
console.log(`[Proxy] TMDB request: ${targetUrl}`);
const response = await makeRequest(targetUrl);
res.set('Access-Control-Allow-Origin', '*');
res.set('Content-Type', 'application/json');
res.status(response.statusCode).send(response.body);
} catch (error) {
console.error('[Proxy] TMDB error:', error.message);
res.status(500).json({
error: 'Proxy error',
message: error.message
});
}
});
export { router as proxyRouter };

View File

@@ -132,9 +132,12 @@ streamRouter.get('/:sessionId/video', async (req, res) => {
const stream = torrentManager.createReadStream(sessionId, { start, end });
// Handle stream errors gracefully
// Handle stream errors gracefully - suppress common client disconnect errors
stream.on('error', (err) => {
console.error('Stream error:', err.message);
// Only log unexpected errors, not normal client disconnects
if (!err.message?.includes('closed prematurely') && !err.message?.includes('ECONNRESET')) {
console.error('Stream error:', err.message);
}
if (!res.headersSent) {
res.status(500).end();
}
@@ -156,9 +159,12 @@ streamRouter.get('/:sessionId/video', async (req, res) => {
const stream = torrentManager.createReadStream(sessionId);
// Handle stream errors gracefully
// Handle stream errors gracefully - suppress common client disconnect errors
stream.on('error', (err) => {
console.error('Stream error:', err.message);
// Only log unexpected errors, not normal client disconnects
if (!err.message?.includes('closed prematurely') && !err.message?.includes('ECONNRESET')) {
console.error('Stream error:', err.message);
}
if (!res.headersSent) {
res.status(500).end();
}

View File

@@ -1,7 +1,12 @@
import WebTorrent from 'webtorrent';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { getCacheDir, isAndroid } from '../utils/platformPaths.js';
// Get cache directory using platform-aware utility
function getCacheDirectory() {
return getCacheDir('bestream-cache');
}
// Trackers for magnet links
const TRACKERS = [
@@ -17,11 +22,20 @@ const TRACKERS = [
class TorrentManager {
constructor() {
this.client = new WebTorrent({
// Configure WebTorrent options
const webTorrentOptions = {
maxConns: 100,
});
};
// Disable uTP on Android (native module not supported)
if (isAndroid()) {
webTorrentOptions.utp = false;
console.log(' Running on Android - uTP disabled');
}
this.client = new WebTorrent(webTorrentOptions);
this.sessions = new Map();
this.downloadPath = path.join(os.tmpdir(), 'bestream-cache');
this.downloadPath = getCacheDirectory();
// Ensure download directory exists
if (!fs.existsSync(this.downloadPath)) {

View File

@@ -1,12 +1,21 @@
import ffmpeg from 'fluent-ffmpeg';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { getCacheDir, isAndroid } from '../utils/platformPaths.js';
class Transcoder {
constructor() {
this.hlsPath = path.join(os.tmpdir(), 'bestream-hls');
this.hlsPath = getCacheDir('bestream-hls');
this.activeTranscodes = new Map();
this.ffmpegAvailable = false;
// Skip FFmpeg setup on Android - it's not available
if (isAndroid()) {
// FFmpeg is not bundled with the Android app
// Direct video streaming will be used instead
console.log(`📺 HLS output directory: ${this.hlsPath}`);
return;
}
// Ensure HLS directory exists
if (!fs.existsSync(this.hlsPath)) {
@@ -38,16 +47,18 @@ class Transcoder {
}
console.log(`📺 FFmpeg configured at: ${ffmpegPath}`);
ffmpegFound = true;
this.ffmpegAvailable = true;
break;
}
}
// If no FFmpeg found in common locations, warn user
// If no FFmpeg found in common locations, it's optional
if (!ffmpegFound) {
console.warn('⚠️ FFmpeg not found in common locations. Make sure FFmpeg is installed and in your PATH.');
console.warn(' Install FFmpeg: winget install Gyan.FFmpeg');
console.warn(' Or download from: https://ffmpeg.org/download.html');
console.log('📺 FFmpeg not found - HLS transcoding disabled (direct streaming will be used)');
}
} else if (process.platform !== 'android') {
// On Linux/Mac, assume FFmpeg might be in PATH
this.ffmpegAvailable = true;
}
console.log(`📺 HLS output directory: ${this.hlsPath}`);
@@ -58,6 +69,10 @@ class Transcoder {
*/
getVideoInfo(inputPath) {
return new Promise((resolve, reject) => {
if (!this.ffmpegAvailable) {
reject(new Error('FFmpeg not available'));
return;
}
ffmpeg.ffprobe(inputPath, (err, metadata) => {
if (err) {
reject(err);
@@ -91,6 +106,11 @@ class Transcoder {
* Start HLS transcoding
*/
async startHlsTranscode(sessionId, inputPath, onProgress, quality = null) {
// Check if FFmpeg is available
if (!this.ffmpegAvailable) {
throw new Error('FFmpeg not available - use direct streaming instead');
}
const outputDir = path.join(this.hlsPath, sessionId);
const playlistPath = path.join(outputDir, 'playlist.m3u8');
@@ -190,6 +210,11 @@ class Transcoder {
* This allows transcoding to start even when the file isn't fully on disk
*/
async startHlsStreamTranscode(sessionId, inputStream, onProgress, quality = null) {
// Check if FFmpeg is available
if (!this.ffmpegAvailable) {
throw new Error('FFmpeg not available - use direct streaming instead');
}
const outputDir = path.join(this.hlsPath, sessionId);
const playlistPath = path.join(outputDir, 'playlist.m3u8');

View File

@@ -0,0 +1,99 @@
/**
* Platform-aware path utilities for Android compatibility
* On Android (capacitor-nodejs), os.tmpdir() returns '/tmp' which is not writable.
* This module provides utilities to get writable directories on all platforms.
*/
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';
// Cache the detected values
let _isAndroid = null;
let _dataDir = null;
/**
* Detect if running on Android (capacitor-nodejs environment)
*/
export function isAndroid() {
if (_isAndroid !== null) return _isAndroid;
const tmpDir = os.tmpdir();
const cwd = process.cwd();
// Multiple detection methods for Android:
// 1. process.platform is 'android' on capacitor-nodejs
// 2. tmpdir is '/tmp' (not writable on Android)
// 3. cwd contains Android app paths
_isAndroid =
process.platform === 'android' ||
tmpDir === '/tmp' ||
cwd.includes('/data/data/') ||
cwd.includes('/data/user/');
return _isAndroid;
}
/**
* Get the base data directory for the app.
* On Android: /data/data/com.bestream.app/files/
* On Desktop: System temp directory
*/
export function getDataDirectory() {
if (_dataDir !== null) return _dataDir;
if (isAndroid()) {
try {
// Derive from import.meta.url which gives us the bundle location
// Bundle runs from: file:///data/data/com.bestream.app/files/nodejs/public/index.js
// We want: /data/data/com.bestream.app/files/
const currentFileUrl = import.meta.url;
const currentFilePath = fileURLToPath(currentFileUrl);
// Go up to find the 'files' directory
let dir = path.dirname(currentFilePath);
while (dir && dir !== '/' && !dir.endsWith('/files')) {
const parent = path.dirname(dir);
if (parent === dir) break; // Reached root
dir = parent;
}
_dataDir = dir;
console.log(`[platformPaths] Android data directory: ${_dataDir}`);
} catch (e) {
// Fallback to hardcoded path
_dataDir = '/data/data/com.bestream.app/files';
console.log(`[platformPaths] Using fallback Android path: ${_dataDir}`);
}
} else {
_dataDir = os.tmpdir();
console.log(`[platformPaths] Desktop temp directory: ${_dataDir}`);
}
return _dataDir;
}
/**
* Get a writable cache directory for a specific purpose.
* Creates the directory if it doesn't exist.
* @param {string} name - The subdirectory name (e.g., 'bestream-cache', 'bestream-hls')
* @returns {string} The full path to the cache directory
*/
export function getCacheDir(name) {
const baseDir = getDataDirectory();
return path.join(baseDir, name);
}
/**
* Get a temporary file path that works on all platforms
* @param {string} filename - The filename
* @returns {string} Full path to the temp file
*/
export function getTempFilePath(filename) {
const baseDir = getDataDirectory();
return path.join(baseDir, 'temp', filename);
}
// Log platform info on first import
const platform = isAndroid() ? 'Android' : process.platform;
console.log(`[platformPaths] Detected platform: ${platform}`);

View File

@@ -1,4 +1,5 @@
import axios from 'axios';
import { isCapacitor, getApiUrl } from '../../utils/platform';
// TMDB API for TV show metadata and discovery
// Using a proxy to avoid CORS issues
@@ -7,7 +8,14 @@ const TMDB_BASE_URL = 'https://api.themoviedb.org/3'; // Temporarily bypass prox
const TMDB_IMAGE_BASE = 'https://image.tmdb.org/t/p/';
// EZTV API for torrents
const EZTV_BASE_URL = '/api/eztv'; // Proxied through Vite
// On Capacitor (Android), use the Node.js server proxy
// On web/dev, use Vite's proxy
const getEztvBaseUrl = () => {
if (isCapacitor()) {
return `${getApiUrl()}/api/eztv`;
}
return '/api/eztv'; // Proxied through Vite in dev
};
// EZTV authentication (optional - for accessing more content)
// Set these in your environment variables
@@ -331,7 +339,7 @@ export const tvDiscoveryApi = {
console.log(`[EZTV] Fetching torrents for IMDB ID: ${numericId} (original: ${imdbId}) ${isAuthenticated ? '(authenticated)' : '(anonymous)'}`);
// Use the working eztvx.to API
const response = await axios.get(`${EZTV_BASE_URL}/get-torrents`, {
const response = await axios.get(`${getEztvBaseUrl()}/get-torrents`, {
params: {
imdb_id: numericId,
limit: 100,
@@ -408,7 +416,7 @@ export const tvDiscoveryApi = {
console.log(`[Episode Search] Searching for S${seasonStr}E${episodeStr} with IMDB: ${numericId}`);
try {
const response = await axios.get(`${EZTV_BASE_URL}/get-torrents`, {
const response = await axios.get(`${getEztvBaseUrl()}/get-torrents`, {
params: {
imdb_id: numericId,
limit: 100, // Get plenty of results