se-bastiaan/TorrentStream-Android

Stream while downloading

AlenToma opened this issue · 4 comments

Thanx for sharing this library with us man.

I did create a react-native plugin for this. And I am using these version of the library.

    implementation "com.github.TorrentStream:TorrentStream-Android:2.8.0"
    implementation "com.github.TorrentStream:TorrentStreamServer-Android:1.0.6"

The problem is that onServerReady gets trigered only when the whole torrent progress is 100%
the buffer only gets 100% when the progress is 100%.

What am I doing wrong here ?

This issue has nothing with react-native But I am going to post the module I created below.
If you could have a look an see if I did something wrong. will be really helpfull
Here is the Module I created.

package com.ghondar.torrentstreamer;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Arguments;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import java.util.Map;
import java.util.HashMap;
import com.frostwire.jlibtorrent.FileStorage;

import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import com.github.se_bastiaan.torrentstream.StreamStatus;
import com.github.se_bastiaan.torrentstream.Torrent;
import com.github.se_bastiaan.torrentstream.TorrentOptions;
import com.github.se_bastiaan.torrentstream.TorrentStream;
import com.github.se_bastiaan.torrentstream.listeners.TorrentListener;
import com.github.se_bastiaan.torrentstreamserver.TorrentServerListener;
import com.github.se_bastiaan.torrentstreamserver.TorrentStreamNotInitializedException;
import com.github.se_bastiaan.torrentstreamserver.TorrentStreamServer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.UnknownHostException;

public class TorrentStreamServerModule extends ReactContextBaseJavaModule implements TorrentServerListener {
    @Override
    public String getName() {
        return "TorrentStreamServer";
    }

    private final ReactApplicationContext reactContext;
    private TorrentStreamServer torrentStreamServer = null;
    private String ipAddress = "127.0.0.1";
    private Torrent _torrent;
    private String magnetUrl;
    private String url;
    private int index = -1;
    private boolean firstRun = false;
    private String _location;

    public TorrentStreamServerModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

    private void setup(String location, Boolean removeAfterStop) {

        if (location == null)
            location = "" + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        this._location = location;
        if (this.torrentStreamServer == null) {
            TorrentOptions torrentOptions = new TorrentOptions.Builder()
                    .saveLocation(location)
                    .removeFilesAfterStop(removeAfterStop)
                    .build();

            torrentStreamServer = TorrentStreamServer.getInstance();
            torrentStreamServer.setTorrentOptions(torrentOptions);
            torrentStreamServer.setServerHost(ipAddress);
            torrentStreamServer.setServerPort(8080);
            torrentStreamServer.startTorrentStream();
            torrentStreamServer.addListener(this);

        }
    }

    public void sendEvent(String eventName, @Nullable WritableMap params) {
        this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }

    @ReactMethod
    public void start(String magnetUrl, String location, Boolean removeAfterStop) {
        this.magnetUrl = magnetUrl;
        this.setup(location, removeAfterStop);
        try {
            this.torrentStreamServer.startStream(magnetUrl);
        } catch (IOException | TorrentStreamNotInitializedException e) {
            WritableMap info = Arguments.createMap();
            info.putString("msg", e.toString());
            this.sendEvent("error", info);
        }
    }

    @ReactMethod
    public void stop() {
        if (this.torrentStreamServer != null)
            this.torrentStreamServer.stopTorrentStream();
    }

    private WritableArray getFileInfos() {
        FileStorage fileStorage = this._torrent.getTorrentHandle().torrentFile().files();
        WritableArray infos = Arguments.createArray();
        for (int i = 0; i < fileStorage.numFiles(); i++) {
            WritableMap info = Arguments.createMap();
            info.putString("path", this._location + "/" + fileStorage.filePath(i));
            info.putString("fileName", fileStorage.fileName(i));
            info.putDouble("size", fileStorage.fileSize(i));
            infos.pushMap(info);
        }
        return infos;
    }

    @Override
    public void onStreamPrepared(Torrent torrent) {
        WritableMap params = Arguments.createMap();
        if (this.index == -1) {
            Long size = 0L;
            FileStorage fileStorage = this._torrent.getTorrentHandle().torrentFile().files();
            for (int i = 0; i < fileStorage.numFiles(); i++) {
                if (fileStorage.fileSize(i) > size) {
                    size = fileStorage.fileSize(i);
                    this.index = i;
                }
            }
            torrent.setSelectedFileIndex(this.index);
        }

        params.putString("magnetUrl", "" + this.magnetUrl);
        params.putString("data", "OnStreamPrepared");
        this.sendEvent("progress", params);
    }

    @Override
    public void onStreamStarted(Torrent torrent) {
        this._torrent = torrent;
        WritableMap params = Arguments.createMap();
        params.putString("magnetUrl", "" + this.magnetUrl);
        if (!this.firstRun)
            params.putArray("files", this.getFileInfos());
        params.putString("data", "onStreamStarted");
        this.firstRun = true;
        this.sendEvent("progress", params);
    }

    @Override
    public void onStreamError(Torrent torrent, Exception e) {
        WritableMap params = Arguments.createMap();
        params.putString("magnetUrl", "" + this.magnetUrl);
        params.putString("msg", e.getMessage());
        params.putString("data", "onStreamError");
        this.sendEvent("error", params);
    }

    @Override
    public void onStreamReady(Torrent torrent) {
        WritableMap params = Arguments.createMap();
        params.putString("magnetUrl", "" + this.magnetUrl);
        params.putString("url", torrent.getVideoFile().toString());
        params.putString("filename", torrent.getTorrentHandle().name());
        params.putString("url", url);
        params.putString("data", "onStreamReady");
        this.sendEvent("ready", params);
    }

    @Override
    public void onStreamProgress(Torrent torrent, StreamStatus status) {
        WritableMap params = Arguments.createMap();
        params.putString("magnetUrl", "" + this.magnetUrl);
        params.putString("buffer", "" + status.bufferProgress);
        params.putString("downloadSpeed", "" + status.downloadSpeed);
        params.putString("progress", "" + status.progress);
        params.putString("seeds", "" + status.seeds);
        try {
            params.putString("url", this.torrentStreamServer.getCurrentStreamUrl());
        } catch (Exception e) {

        }
        params.putString("data", "onStreamProgress");
        this.sendEvent("status", params);
    }

    @Override
    public void onStreamStopped() {
        WritableMap params = Arguments.createMap();
        params.putString("magnetUrl", "" + this.magnetUrl);
        params.putString("data", "OnStreamStoped");
        this.sendEvent("stop", params);
    }

    @Override
    public void onServerReady(String url) {
        WritableMap params = Arguments.createMap();
        this.url = url;
        params.putString("url", url);
        params.putString("data", "onServerReady");
        this.sendEvent("serverReady", params);
    }
}

The only thing I notice in your code is that you are selecting the largest file in onStreamPrepared, which the TorrentStream library already does by default.

That onReady is not called means that the pieces that were selected as the start/end of the file were not downloaded yet.

And since you have not disabled the auto download option I think that is where your problem may be. The download already started, then you change the selected file.

Could you try using the default behaviour by not selecting a new file in onPrepared?

I have removed the selectedIndex code(that you mentioned) and the problem still accord the buffer dose not reach 100%
I have tested it with this https://yts.mx/torrent/download/32B403D8FD0571C5B5D15D6D931AE38CCCC24BAA torrent
and when the progress is 48 the buffer is 45. As you can see that I have to wait for the whole file to be downloaded before I can stream it.

And sadly there is also the different in speed between utorrent and the current stream service. I would guess that it dose not use all the seeds. That is only a guess though.

Have a look below, this prove what I say

screenshot-2021-12-17_07 06 17 448

I think that this problem is related to #49 (comment)

I hope it works now, since I closed #49. If you still happen to be working on this, you're welcome to reopen this issue if it persists.