webtorrent / webtorrent

⚡️ Streaming torrent client for the web

Home Page:https://webtorrent.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

file.streamTo video src inconsistently returning 404

dodo721 opened this issue · comments

What version of this package are you using?
2.0.37

What operating system, Node.js, and npm version?
OS: Ubuntu 21.10
Node.js: v14.21.2
npm: 6.14.17

What happened?
When using file.streamTo to attach a URL src to a video element, the video element returns a 404 on trying to fetch the video on most attempts, though very occasionally succeeding. If I open the URL generated in a new tab, it loads fine. Timing appears to make no difference: tests from generating the URL as the torrent generates, to waiting for a successful AJAX call first, to just a manual 20 second timeout all fail with the same inconsistency. My AJAX calls also still return status 200, even while the video element reports 404.

My code:

import React, { useEffect, useState, useRef } from 'react';
import { Button } from 'reactstrap';
import WebTorrent from 'webtorrent/dist/webtorrent.min.js';
import $ from 'jquery';

const MainDiv = () => {

    const [torrent, setTorrent] = useState(null);
    const [torrentProgress, setTorrentProgress] = useState("0%");
    const [updateInterval, setUpdateInterval] = useState(null);
    const [streamUrl, setStreamUrl] = useState(null);
    const [videoStatus, setVideoStatus] = useState("Unloaded");
    const videoRef = useRef();

    useEffect(() => {
        let client = new WebTorrent();

        client.on('error', err => {
            console.log('[+] Webtorrent error: ' + err.message);
        });
        // Sintel
        const magnetURI = "magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent";
        window.torrentClient = client;

        const download = () => {
            client.add(magnetURI, (torrent) => {
                const interval = setInterval(() => {
                    // console.log('[+] Progress: ' + (torrent.progress * 100).toFixed(1) + '%')
                    setTorrentProgress((torrent.progress * 100).toFixed(1) + '%');
                    $.get(streamUrl).done((res, status, xhr) => {
                        setVideoStatus(xhr.status);
                        //console.log(xhr.status);
                    }).fail((res, status, xhr) => {
                        setVideoStatus(xhr.status);
                    });
                }, 500);
                torrent.on('done', () => {
                    console.log('Progress: 100%');
                    clearInterval(interval);
                });
                
                setUpdateInterval(interval);
                setTorrent(torrent);
                // TODO Figure out a better way to render these files 
                torrent.files.map((file, i) => {
                    console.log("FILE " + i, file);
                    if (file.name.endsWith(".mp4")) {
                        file.streamTo(videoRef.current);
                        const stream = videoRef.current.src;
                        //console.log("WOW I HAVE A STREAM URL???", streamUrl)
                        setStreamUrl(stream);
                    }
                })
            });
        }

        navigator.serviceWorker.register('/sw.min.js').then(reg => {
            const worker = reg.active || reg.waiting || reg.installing
            function checkState (worker) {
                return worker.state === 'activated' && client.createServer({ controller: reg }) && download();
            }
            if (!checkState(worker)) {
                worker.addEventListener('statechange', ({ target }) => checkState(target))
            }
        });
    }, []);

    const cancelTorrent = () => {
        torrent.destroy({destroyStore:true});
        setTorrent(null);
        setTorrentProgress("0%");
        clearInterval(updateInterval);
        setUpdateInterval(null);
        //unregister();
    }

    return <div className='main-page'>
        <h1>{torrent?.name}</h1>
        <p><b>Torrent Info Hash: </b>{torrent?.infoHash}</p>
        <p><b>Torrent Progress: </b>{torrentProgress}</p>
        <p>{videoStatus}</p>
        <video id="loader" ref={videoRef}/>
        <video id="player" src={videoStatus===200 ? streamUrl : null}/>
        {torrent && <Button color="danger" onClick={cancelTorrent}>Clear torrent</Button>}
    </div>;

};

export default MainDiv;

Additional info:

I am using NGINX to host my server on a local URL (local.milfos.com), with the following config:

# copy this to nginx sites-available, also modify etc/hosts

map $sent_http_content_type $expires {
    default                         off;
    text/html                       epoch;
    text/css                        epoch;
    application/javascript          epoch;
    ~image/                         epoch;
}

server {
	listen   80; ## listen for ipv4; this line is default and implied

	listen               443;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
	ssl_prefer_server_ciphers off;
	ssl_certificate      <REDACTED>;
	ssl_certificate_key  <REDACTED>;

	add_header X-Frame-Options SAMEORIGIN always;

	root /home/oem/MiLFOS/web;
	index index.html;

	server_name local.milfos.com;

    expires $expires;

	location / {
		try_files $uri $uri/ index @backend;
		add_header 'Access-Control-Allow-Origin' "$http_origin";
		add_header 'Access-Control-Allow-Credentials' 'true';
		add_header 'Cache-Control' 'no-cache';
	}

	location @backend {
		proxy_pass		http://localhost:3000;
		proxy_set_header	X-Real-IP $remote_addr;
		proxy_set_header	X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header	Host $http_host;
	}
}

I am using the provided sw.min.js service worker, stored in the root web directory.

What did you expect to happen?
The video element to load the generated URL src successfully.

Are you willing to submit a pull request to fix this bug?
I do not have enough knowledge of the issue to do so

commented

if you're using nextjs to my knowledge, they patch the fetch function, check globalThis.fetch, which breaks service worker requests [I think]

Hey, thanks for getting back to me so quick.
I'm not using nextjs - just webpack/babel compiling to a web directory, with nginx pointing to it.
I've also tried opening the page in a fresh profile with no extensions, making sure sw.min.js is the only service worker running, still to no avail.
I have found that clearing the cache gets me one successful run before it stops working again.
It seems to work until I click "Clear torrent" which destroys the torrent. After that it returns 404 on the video element again.
I've noticed these errors which may be relevant:

Screenshot from 2023-06-11 19-09-02

Screenshot from 2023-06-11 18-26-01

My package.json, for completion:

{
  "name": "milfos",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "compile": "webpack --config $PWD/webpack.config.js --progress --color && ./gitstamp.sh",
    "compile-watch": "node ci.js",
    "compile-watch-fast": "NO_PROD=true webpack --progress --color --watch",
    "server": "nodemon server/index.js"
  },
  "author": "dodo721",
  "license": "ISC",
  "dependencies": {
    "@babel/runtime": "^7.16.5",
    "express": "^4.18.2",
    "jquery": "^3.7.0",
    "lodash": "^4.17.21",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "reactstrap": "^8.10.1",
    "torrent-search-api": "^2.1.4",
    "webtorrent": "^2.0.37"
  },
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/eslint-parser": "^7.18.9",
    "@babel/plugin-proposal-class-properties": "^7.16.0",
    "@babel/plugin-proposal-object-rest-spread": "^7.16.0",
    "@babel/plugin-proposal-private-methods": "^7.18.6",
    "@babel/plugin-transform-react-jsx": "^7.16.0",
    "@babel/plugin-transform-runtime": "^7.16.5",
    "@babel/polyfill": "^7.11.5",
    "@babel/preset-env": "^7.16.4",
    "@babel/preset-react": "^7.16.0",
    "@types/react": "^17.0.11",
    "@types/react-dom": "^17.0.7",
    "@types/reactstrap": "^8.5.1",
    "@typescript-eslint/eslint-plugin": "^5.59.1",
    "@typescript-eslint/parser": "^5.59.1",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^27.0.2",
    "babel-loader": "^8.2.3",
    "babel-plugin-const-enum": "^1.2.0",
    "core-js": "^3.19.3",
    "css-loader": "^6.5.1",
    "eslint": "^8.39.0",
    "eslint-plugin-eslint-comments": "^3.2.0",
    "eslint-plugin-import": "^2.25.3",
    "eslint-plugin-jsdoc": "^39.6.6",
    "eslint-plugin-jsx-a11y": "^6.5.1",
    "eslint-plugin-react": "^7.32.1",
    "eslint-plugin-react-hooks": "^4.3.0",
    "jsdom": "^21.0.0",
    "less": "^4.1.2",
    "less-loader": "^10.2.0",
    "mini-css-extract-plugin": "^2.4.5",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  },
  "description": "Home media software with torrent scraping"
}
commented

simply run fetch in console, nothing else, then check if it returns native code or some code path
image

Screenshot from 2023-06-12 17-50-34

Looks like it's native code

commented

I cannot reproduce this in the examples webtorrent provides, so this has to be some issue on your end/config, seems like the service worker is completely ignored and goes directly to nginx

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?