Skip to content

Latest commit

 

History

History
507 lines (397 loc) · 11.2 KB

File metadata and controls

507 lines (397 loc) · 11.2 KB

FilmPlus Nuvio Provider - Complete Documentation

Table of Contents

  1. Overview
  2. Architecture
  3. Provider API
  4. Stream Objects
  5. Building Providers
  6. Advanced Features
  7. Troubleshooting
  8. API Reference

Overview

FilmPlus Nuvio Provider is a collection of streaming provider integrations for the Nuvio app. It aggregates multiple streaming sources discovered in the FilmPlus mobile application.

Key Features

  • Multiple Sources: Supports 8+ streaming platforms
  • Async Operations: Full async/await support with error handling
  • Retry Logic: Automatic retry with exponential backoff
  • Metadata Integration: TMDB API for rich content information
  • Format Support: M3U8, MP4, MKV, and more

Architecture

Directory Structure

filmplus-nuvio-provider/
├── src/
│   └── filmplus/
│       ├── index.js          # Main provider logic
│       ├── http.js           # HTTP utilities
│       ├── extractor.js      # Stream extraction
│       └── config.js         # Configuration
├── providers/                # Compiled output
├── manifest.json             # Provider registry
├── build.js                  # Build system
├── package.json              # Dependencies
└── README.md                 # Quick start guide

Flow Diagram

User Request
    ↓
getStreams(tmdbId, mediaType, season, episode)
    ↓
getTmdbInfo() ← Get metadata
    ↓
searchOnSource() ← Query each platform
    ↓
extractStreamsFromLink() ← Parse HTML/API
    ↓
Return Stream Objects
    ↓
Nuvio Player

Provider API

Main Function: getStreams()

/**
 * @async
 * @param {string} tmdbId - TMDB database identifier
 * @param {string} mediaType - 'movie' or 'tv'
 * @param {number} season - Season number (TV only)
 * @param {number} episode - Episode number (TV only)
 * @returns {Promise<Array>} Array of stream objects
 */
async function getStreams(tmdbId, mediaType, season, episode)

Function Signature

The function MUST:

  1. Accept four parameters as shown above
  2. Return a Promise resolving to an array
  3. Return an empty array on error (not throw)
  4. Handle both movies and TV shows
  5. Include error logging for debugging

Example Implementation

async function getStreams(tmdbId, mediaType, season, episode) {
  try {
    // Get content metadata
    const content = await getTmdbInfo(tmdbId, mediaType);
    
    if (!content) {
      console.log('[Provider] Could not fetch metadata');
      return [];
    }

    // Build search query
    const query = buildQuery(content, season, episode);
    
    // Search available sources
    const results = await searchSources(query);
    
    // Extract stream URLs
    const streams = [];
    for (const result of results) {
      const pageStreams = await extractStreams(result.url);
      streams.push(...pageStreams);
    }
    
    return streams;
  } catch (error) {
    console.error('[Provider] Error:', error.message);
    return [];
  }
}

Stream Objects

Required Fields

{
  name: "ProviderName",        // string - Provider identifier
  title: "1080p Stream",       // string - Display title
  url: "https://...",          // string - Playback URL
  quality: "1080p"             // string - Quality label
}

Optional Fields

{
  size: "2.5 GB",              // string - File size estimate
  format: "mp4",               // string - File format
  headers: {                   // object - HTTP headers
    "Referer": "https://...",
    "User-Agent": "Mozilla/..."
  },
  subtitles: [{                // array - Subtitle URLs
    language: "en",
    url: "https://..."
  }],
  duration: 9000,              // number - Duration in seconds
  type: "direct"               // string - "direct", "hls", "dash"
}

Quality Values

Use standard quality labels:

  • 4K or 2160p - Ultra HD
  • 1080p - Full HD
  • 720p - HD
  • 480p - Standard
  • 360p - Mobile
  • Auto - Adaptive bitrate

Example Stream Objects

// Direct MP4 stream
{
  name: "FilmPlus",
  title: "1080p Direct Link",
  url: "https://example.com/movie.mp4",
  quality: "1080p",
  format: "mp4",
  headers: {
    "Referer": "https://example.com"
  }
}

// HLS adaptive stream
{
  name: "FilmPlus",
  title: "Adaptive Quality",
  url: "https://example.com/playlist.m3u8",
  quality: "Auto",
  type: "hls",
  headers: {
    "User-Agent": "Mozilla/5.0..."
  }
}

// Embedded player
{
  name: "FilmPlus",
  title: "Player Stream",
  url: "https://embed.example.com/video?id=123",
  quality: "720p",
  headers: {
    "Referer": "https://source.example.com",
    "Authorization": "Bearer token123"
  }
}

Building Providers

Single-File Provider

Simplest approach for basic providers:

// providers/simple-provider.js
const axios = require('axios');

async function getStreams(tmdbId, mediaType, season, episode) {
  // Implementation
  return [];
}

module.exports = { getStreams };

Build:

node build.js --transpile simple-provider.js

Multi-File Provider (Recommended)

Better for complex providers with multiple utilities:

mkdir -p src/my-provider

src/my-provider/index.js

const { fetchText } = require('./http.js');
const { extractStreams } = require('./extractor.js');

async function getStreams(tmdbId, mediaType, season, episode) {
  const html = await fetchText(`https://api.example.com/search?q=${tmdbId}`);
  return extractStreams(html);
}

module.exports = { getStreams };

src/my-provider/http.js

const axios = require('axios');

async function fetchText(url) {
  const response = await axios.get(url);
  return response.data;
}

module.exports = { fetchText };

src/my-provider/extractor.js

const cheerio = require('cheerio-without-node-native');

function extractStreams(html) {
  const $ = cheerio.load(html);
  const streams = [];
  
  $('a[href*=".m3u8"]').each((i, el) => {
    streams.push({
      name: "MyProvider",
      title: `Stream ${i + 1}`,
      url: $(el).attr('href'),
      quality: "Auto"
    });
  });
  
  return streams;
}

module.exports = { extractStreams };

Build:

npm run build

Advanced Features

Using HTTP Utilities

const { fetchText, fetchJSON, postJSON } = require('./http');

// Fetch HTML
const html = await fetchText('https://example.com/search?q=movie', {
  headers: { 'Authorization': 'Bearer token' },
  retries: 3,
  delay: 1000
});

// Fetch JSON
const data = await fetchJSON('https://api.example.com/data');

// POST request
const result = await postJSON('https://api.example.com/search', {
  query: 'movie title'
});

Using Extractor Utilities

const { 
  extractStreams, 
  extractSearchResults, 
  detectQuality 
} = require('./extractor');

// Extract streams from page
const streams = extractStreams(html, 'ProviderName', 'https://referer.url');

// Extract search results
const results = extractSearchResults(html, {
  titleSelector: 'a.title',
  limit: 10
});

// Detect quality from URL
const quality = detectQuality('https://example.com/1080p/movie.mp4');
// Returns: "1080p"

Retry Logic with Backoff

async function fetchWithRetry(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fetch(url);
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      
      const delay = 1000 * Math.pow(2, attempt);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

Caching Results

const cache = new Map();

async function getStreamsWithCache(tmdbId, mediaType) {
  const cacheKey = `${tmdbId}-${mediaType}`;
  
  if (cache.has(cacheKey)) {
    return cache.get(cacheKey);
  }
  
  const streams = await getStreams(tmdbId, mediaType);
  cache.set(cacheKey, streams);
  
  // Clear cache after 1 hour
  setTimeout(() => cache.delete(cacheKey), 3600000);
  
  return streams;
}

Troubleshooting

Common Issues

No streams found

// Debugging checklist:

// 1. Check TMDB metadata retrieval
console.log('[Provider] TMDB data:', content);

// 2. Verify search results
console.log('[Provider] Search results:', results);

// 3. Check stream extraction
console.log('[Provider] Extracted streams:', streams);

// 4. Verify stream URL format
streams.forEach(s => {
  if (!s.url.startsWith('http')) {
    console.warn('[Provider] Invalid URL:', s.url);
  }
});

Connection errors

// Solution: Add proper retry logic with delays

async function fetchWithRetry(url, options = {}) {
  const { retries = 3, delay = 1000 } = options;
  
  for (let i = 0; i < retries; i++) {
    try {
      return await fetch(url, {
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
        }
      });
    } catch (error) {
      console.log(`Attempt ${i + 1} failed: ${error.message}`);
      if (i < retries - 1) {
        await new Promise(r => setTimeout(r, delay * Math.pow(2, i)));
      }
    }
  }
  return null;
}

Mixed content warnings

Ensure all URLs use HTTPS:

const url = url.replace(/^http:/, 'https:');

Debug Mode

Enable detailed logging:

const DEBUG = true;

function log(message) {
  if (DEBUG) console.log(`[Provider] ${message}`);
}

async function getStreams(tmdbId, mediaType, season, episode) {
  log(`Searching: TMDB ${tmdbId}, ${mediaType}`);
  
  try {
    const results = await search();
    log(`Found ${results.length} results`);
    // ...
  } catch (error) {
    log(`Error: ${error.message}`);
  }
}

API Reference

Module: http.js

fetchText(url, options)

  • url (string) - URL to fetch
  • options (object) - Optional config
    • headers (object) - Custom headers
    • retries (number) - Retry count (default: 3)
    • delay (number) - Retry delay in ms (default: 1000)
    • timeout (number) - Request timeout in ms (default: 10000)
  • Returns: Promise

fetchJSON(url, options)

  • url (string) - JSON endpoint URL
  • options (object) - Same as fetchText
  • Returns: Promise

    postJSON(url, data, options)

    • url (string) - POST endpoint
    • data (object) - JSON payload
    • options (object) - Config (no retries for POST)
    • Returns: Promise

      Module: extractor.js

      extractStreams(html, source, referer)

      • html (string) - Page HTML
      • source (string) - Provider name (default: "FilmPlus")
      • referer (string) - Referer header value
      • Returns: Array of stream objects

      extractSearchResults(html, options)

      • html (string) - Search page HTML
      • options (object):
        • titleSelector (string) - CSS selector for results
        • linkSelector (string) - Link selector
        • limit (number) - Max results (default: 10)
      • Returns: Array of result objects

      detectQuality(text)

      • text (string) - URL or text to analyze
      • Returns: string - Detected quality ("1080p", "720p", etc.)

      isVideoUrl(url)

      • url (string) - URL to check
      • Returns: boolean - True if video file

      For more information, see README.md or open an issue on GitHub.