├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── sv244 │ └── torrentstream │ ├── StreamStatus.java │ ├── Torrent.java │ ├── TorrentOptions.java │ ├── TorrentStream.java │ ├── exceptions │ ├── DirectoryCreationException.java │ ├── NotInitializedException.java │ └── TorrentInfoException.java │ ├── listeners │ ├── DHTStatsAlertListener.java │ ├── TorrentAddedAlertListener.java │ └── TorrentListener.java │ └── utils │ ├── FileUtils.java │ └── ThreadUtils.java ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── eu │ │ └── sv244 │ │ └── torrentstreamer │ │ └── sample │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | .idea/ 15 | *.iml 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Keystore 31 | *.jks -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TorrentStream-Android 2 | ====== 3 | 4 | A torrent streamer library for Android based on [jlibtorrent](https://github.com/frostwire/frostwire-jlibtorrent). 5 | 6 | Used in [Popcorn Time for Android](https://github.com/popcorn-official/popcorn-android) and definitely not done yet. 7 | 8 | Using 9 | -------- 10 | 11 | See the sample. 12 | 13 | Available on Maven Central as 'com.github.sv244:torrentstream-android' 14 | 15 | License 16 | -------- 17 | 18 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 19 | 20 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. 23 | 24 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidTorrent/TorrentStream-Android/b2e49cd2ab8078d8686e6cd68adcc4ca16fb2d9a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Aug 08 21:50:44 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.3.0' 8 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | def groupId = 'com.github.sv244' 17 | def artifactId = 'torrentstream-android' 18 | def version = '1.0.2' 19 | def localReleaseDest = "${buildDir}/release/${version}" 20 | 21 | apply plugin: 'com.android.library' 22 | apply plugin: 'maven' 23 | apply plugin: 'signing' 24 | 25 | android { 26 | compileSdkVersion 23 27 | buildToolsVersion "23.0.0" 28 | 29 | defaultConfig { 30 | minSdkVersion 15 31 | targetSdkVersion 23 32 | versionCode 1 33 | versionName '1.0.2' 34 | } 35 | 36 | buildTypes { 37 | release { 38 | minifyEnabled false 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | 43 | lintOptions { 44 | abortOnError false 45 | } 46 | } 47 | 48 | dependencies { 49 | compile fileTree(dir: 'libs', include: ['*.jar']) 50 | compile 'com.frostwire:jlibtorrent:1.1.0.9' 51 | compile 'com.frostwire:jlibtorrent-android:1.1.0.9' 52 | } 53 | 54 | task androidJavadocs(type: Javadoc) { 55 | source = android.sourceSets.main.java.srcDirs 56 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 57 | } 58 | 59 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 60 | classifier = 'javadoc' 61 | from androidJavadocs.destinationDir 62 | } 63 | 64 | task androidSourcesJar(type: Jar) { 65 | classifier = 'sources' 66 | from android.sourceSets.main.java.srcDirs 67 | } 68 | 69 | artifacts { 70 | archives androidSourcesJar 71 | archives androidJavadocsJar 72 | } 73 | 74 | signing { 75 | sign configurations.archives 76 | } 77 | 78 | uploadArchives { 79 | repositories.mavenDeployer { 80 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 81 | pom.groupId = groupId 82 | pom.artifactId = artifactId 83 | pom.version = version 84 | pom.project { 85 | name 'TorrentStream-Android' 86 | packaging 'aar' 87 | description 'Android Torrent Streaming library' 88 | url 'https://github.com/sv244/TorrentStream-Android' 89 | 90 | scm { 91 | url 'https://github.com/sv244/TorrentStream-Android' 92 | connection 'scm:git@github.com:sv244/TorrentStream-Android.git' 93 | developerConnection 'scm:git@github.com:sv244/TorrentStream-Android.git' 94 | } 95 | 96 | licenses { 97 | license { 98 | name 'GNU General Public License, Version 3.0' 99 | url 'www.gnu.org/licenses/gpl-3.0.html' 100 | } 101 | } 102 | 103 | developers { 104 | developer { 105 | id 'sv244' 106 | name 'sv244' 107 | email 'sv244@it.pt' 108 | } 109 | } 110 | } 111 | 112 | repository(url: "file://${localReleaseDest}") 113 | } 114 | } 115 | 116 | task zipRelease(type: Zip) { 117 | from (localReleaseDest + '/com/github/sv244/torrentstream-android/' + version + '/') 118 | destinationDir buildDir 119 | archiveName "release-${version}.zip" 120 | } 121 | 122 | task generateRelease << { 123 | println "Release ${version} can be found at ${localReleaseDest}/" 124 | println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip" 125 | } 126 | 127 | generateRelease.dependsOn(uploadArchives) 128 | generateRelease.dependsOn(zipRelease) -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Sebastiaan/Development/Android/SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/StreamStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of TorrentStreamer-Android. 3 | * 4 | * TorrentStreamer-Android is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * TorrentStreamer-Android is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with TorrentStreamer-Android. If not, see . 16 | */ 17 | 18 | package com.github.sv244.torrentstream; 19 | 20 | public class StreamStatus { 21 | public final float progress; 22 | public final int bufferProgress; 23 | public final int seeds; 24 | public final float downloadSpeed; 25 | 26 | protected StreamStatus(float progress, int bufferProgress, int seeds, int downloadSpeed) { 27 | this.progress = progress; 28 | this.bufferProgress = bufferProgress; 29 | this.seeds = seeds; 30 | this.downloadSpeed = downloadSpeed; 31 | } 32 | } -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/Torrent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of TorrentStreamer-Android. 3 | * 4 | * TorrentStreamer-Android is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * TorrentStreamer-Android is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with TorrentStreamer-Android. If not, see . 16 | */ 17 | 18 | package com.github.sv244.torrentstream; 19 | 20 | import com.frostwire.jlibtorrent.AlertListener; 21 | import com.frostwire.jlibtorrent.FileStorage; 22 | import com.frostwire.jlibtorrent.Priority; 23 | import com.frostwire.jlibtorrent.TorrentHandle; 24 | import com.frostwire.jlibtorrent.TorrentInfo; 25 | import com.frostwire.jlibtorrent.TorrentStatus; 26 | import com.frostwire.jlibtorrent.alerts.Alert; 27 | import com.frostwire.jlibtorrent.alerts.AlertType; 28 | import com.frostwire.jlibtorrent.alerts.BlockFinishedAlert; 29 | import com.frostwire.jlibtorrent.alerts.PieceFinishedAlert; 30 | 31 | import java.io.File; 32 | import java.util.ArrayList; 33 | import java.util.Arrays; 34 | import java.util.Iterator; 35 | import java.util.List; 36 | 37 | import com.github.sv244.torrentstream.listeners.TorrentListener; 38 | 39 | public class Torrent implements AlertListener { 40 | 41 | private final static Integer MAX_PREPARE_COUNT = 20; 42 | private final static Integer MIN_PREPARE_COUNT = 2; 43 | private final static Integer DEFAULT_PREPARE_COUNT = 5; 44 | 45 | public enum State { UNKNOWN, RETRIEVING_META, STARTING, STREAMING } 46 | 47 | private Integer mPiecesToPrepare; 48 | private Integer mLastPieceIndex; 49 | private Integer mFirstPieceIndex; 50 | private Integer mSelectedFile = -1; 51 | private Long mPrepareSize; 52 | 53 | private Double mPrepareProgress = 0d; 54 | private Double mProgressStep = 0d; 55 | private List mPreparePieces; 56 | private Boolean[] mHasPieces; 57 | private Integer[] mProcessingPieces = new Integer[5]; 58 | 59 | private TorrentHandle mTorrentHandle; 60 | private TorrentListener mListener; 61 | 62 | private State mState = State.RETRIEVING_META; 63 | 64 | /** 65 | * The constructor for a new Torrent 66 | * 67 | * First the largest file in the download is selected as the file for playback 68 | * 69 | * After setting this priority, the first and last index of the pieces that make up this file are determined. 70 | * And last: amount of pieces that are needed for playback are calculated (needed for playback means: make up 10 megabyte of the file) 71 | * 72 | * @param torrentHandle jlibtorrent TorrentHandle 73 | */ 74 | public Torrent(TorrentHandle torrentHandle, TorrentListener listener, Long prepareSize) { 75 | mTorrentHandle = torrentHandle; 76 | mListener = listener; 77 | 78 | mPrepareSize = prepareSize; 79 | 80 | torrentHandle.setPriority(Priority.NORMAL.getSwig()); 81 | 82 | if(mSelectedFile == -1) 83 | setLargestFile(); 84 | 85 | if(mListener != null) 86 | mListener.onStreamPrepared(this); 87 | } 88 | 89 | /** 90 | * Reset piece priorities 91 | * First set all piece priorities to {@link Priority}.IGNORE and then set the file priority to the file selected for playback. 92 | */ 93 | private void resetPriorities() { 94 | Priority[] priorities = mTorrentHandle.getPiecePriorities(); 95 | for (int i = 0; i < priorities.length; i++) { 96 | if(i >= mFirstPieceIndex && i <= mLastPieceIndex) { 97 | mTorrentHandle.setPiecePriority(i, Priority.NORMAL); 98 | } else { 99 | mTorrentHandle.setPiecePriority(i, Priority.IGNORE); 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * Get LibTorrent torrent handle of this torrent 106 | * @return 107 | */ 108 | public TorrentHandle getTorrentHandle() { 109 | return mTorrentHandle; 110 | } 111 | 112 | public File getVideoFile() { 113 | return new File(mTorrentHandle.getSavePath() + "/" + mTorrentHandle.getTorrentInfo().getFiles().getFilePath(mSelectedFile)); 114 | } 115 | 116 | public File getSaveLocation() { 117 | return new File(mTorrentHandle.getSavePath() + "/" + mTorrentHandle.getName()); 118 | } 119 | 120 | public void resume() { 121 | mTorrentHandle.resume(); 122 | } 123 | 124 | public void pause() { 125 | mTorrentHandle.pause(); 126 | } 127 | 128 | public void setLargestFile() { 129 | setSelectedFile(-1); 130 | } 131 | 132 | /** 133 | * Set the file to download 134 | * If the given index is -1, then the largest file is chosen 135 | * @param selectedFileIndex Integer 136 | */ 137 | public void setSelectedFile(Integer selectedFileIndex) { 138 | TorrentInfo torrentInfo = mTorrentHandle.getTorrentInfo(); 139 | FileStorage fileStorage = torrentInfo.getFiles(); 140 | 141 | if(selectedFileIndex == -1) { 142 | long highestFileSize = 0; 143 | int selectedFile = -1; 144 | for (int i = 0; i < fileStorage.getNumFiles(); i++) { 145 | long fileSize = fileStorage.getFileSize(i); 146 | if (highestFileSize < fileSize) { 147 | highestFileSize = fileSize; 148 | mTorrentHandle.setFilePriority(selectedFile, Priority.IGNORE); 149 | selectedFile = i; 150 | mTorrentHandle.setFilePriority(i, Priority.NORMAL); 151 | } else { 152 | mTorrentHandle.setFilePriority(i, Priority.IGNORE); 153 | } 154 | } 155 | selectedFileIndex = selectedFile; 156 | } else { 157 | for (int i = 0; i < fileStorage.getNumFiles(); i++) { 158 | if(i == selectedFileIndex) { 159 | mTorrentHandle.setFilePriority(i, Priority.NORMAL); 160 | } else { 161 | mTorrentHandle.setFilePriority(i, Priority.IGNORE); 162 | } 163 | } 164 | } 165 | mSelectedFile = selectedFileIndex; 166 | 167 | Priority[] piecePriorities = mTorrentHandle.getPiecePriorities(); 168 | int firstPieceIndex = -1; 169 | int lastPieceIndex = -1; 170 | for (int i = 0; i < piecePriorities.length; i++) { 171 | if (piecePriorities[i] != Priority.IGNORE) { 172 | if (firstPieceIndex == -1) { 173 | firstPieceIndex = i; 174 | } 175 | piecePriorities[i] = Priority.IGNORE; 176 | } else { 177 | if (firstPieceIndex != -1 && lastPieceIndex == -1) { 178 | lastPieceIndex = i - 1; 179 | } 180 | } 181 | } 182 | 183 | if (lastPieceIndex == -1) { 184 | lastPieceIndex = piecePriorities.length - 1; 185 | } 186 | int pieceCount = lastPieceIndex - firstPieceIndex + 1; 187 | int pieceLength = mTorrentHandle.getTorrentInfo().getPieceLength(); 188 | int activePieceCount; 189 | if (pieceLength > 0) { 190 | activePieceCount = (int) (mPrepareSize / pieceLength); 191 | if (activePieceCount < MIN_PREPARE_COUNT) { 192 | activePieceCount = MIN_PREPARE_COUNT; 193 | } else if (activePieceCount > MAX_PREPARE_COUNT) { 194 | activePieceCount = MAX_PREPARE_COUNT; 195 | } 196 | } else { 197 | activePieceCount = DEFAULT_PREPARE_COUNT; 198 | } 199 | 200 | if (pieceCount < activePieceCount) { 201 | activePieceCount = pieceCount / 2; 202 | } 203 | 204 | mFirstPieceIndex = firstPieceIndex; 205 | mLastPieceIndex = lastPieceIndex; 206 | mPiecesToPrepare = activePieceCount; 207 | } 208 | 209 | public String[] getFileNames() { 210 | FileStorage fileStorage = mTorrentHandle.getTorrentInfo().getFiles(); 211 | String[] fileNames = new String[fileStorage.getNumFiles()]; 212 | for(int i = 0; i < fileStorage.getNumFiles(); i++) { 213 | fileNames[i] = fileStorage.getFileName(i); 214 | } 215 | return fileNames; 216 | } 217 | 218 | /** 219 | * Prepare torrent for playback. Prioritize the first `mPiecesToPrepare` pieces and the last `mPiecesToPrepare` pieces 220 | * from `mFirstPieceIndex` and `mLastPieceIndex`. Ignore all other pieces. 221 | */ 222 | public void startDownload() { 223 | if(mState == State.STREAMING) return; 224 | mState = State.STARTING; 225 | mTorrentHandle.setPriority(Priority.NORMAL.getSwig()); 226 | 227 | List indices = new ArrayList<>(); 228 | 229 | Priority[] priorities = mTorrentHandle.getPiecePriorities(); 230 | for (int i = 0; i < priorities.length; i++) { 231 | if(priorities[i] != Priority.IGNORE) { 232 | mTorrentHandle.setPiecePriority(i, Priority.NORMAL); 233 | } 234 | } 235 | 236 | for (int i = 0; i < mPiecesToPrepare; i++) { 237 | indices.add(mLastPieceIndex - i); 238 | mTorrentHandle.setPiecePriority(mLastPieceIndex - i, Priority.SEVEN); 239 | mTorrentHandle.setPieceDeadline(mLastPieceIndex - i, 1000); 240 | } 241 | 242 | for (int i = 0; i < mPiecesToPrepare; i++) { 243 | indices.add(mFirstPieceIndex + i); 244 | mTorrentHandle.setPiecePriority(mFirstPieceIndex + i, Priority.SEVEN); 245 | mTorrentHandle.setPieceDeadline(mFirstPieceIndex + i, 1000); 246 | } 247 | 248 | mPreparePieces = indices; 249 | 250 | mHasPieces = new Boolean[mLastPieceIndex - mFirstPieceIndex + 1]; 251 | Arrays.fill(mHasPieces, false); 252 | 253 | TorrentInfo torrentInfo = mTorrentHandle.getTorrentInfo(); 254 | TorrentStatus status = mTorrentHandle.getStatus(); 255 | 256 | double blockCount = indices.size() * torrentInfo.getPieceLength() / status.getBlockSize(); 257 | 258 | mProgressStep = 100 / blockCount; 259 | 260 | mTorrentHandle.resume(); 261 | 262 | mListener.onStreamStarted(this); 263 | } 264 | 265 | private void startSequentialMode() { 266 | resetPriorities(); 267 | 268 | if(mHasPieces == null) { 269 | mTorrentHandle.setSequentialDownload(true); 270 | } else { 271 | for (int i = mFirstPieceIndex + mPiecesToPrepare; i < mFirstPieceIndex + mPiecesToPrepare + 5; i++) { 272 | mTorrentHandle.setPiecePriority(i, Priority.SEVEN); 273 | mTorrentHandle.setPieceDeadline(i, 1000); 274 | } 275 | } 276 | } 277 | 278 | public State getState() { 279 | return mState; 280 | } 281 | 282 | public void pieceFinished(PieceFinishedAlert alert) { 283 | if(mState == State.STREAMING && mHasPieces != null) { 284 | mHasPieces[alert.getPieceIndex() - mFirstPieceIndex] = true; 285 | 286 | for(int i = alert.getPieceIndex() - mFirstPieceIndex; i < mHasPieces.length; i++) { 287 | if(!mHasPieces[i]) { 288 | mTorrentHandle.setPiecePriority(i + mFirstPieceIndex, Priority.SEVEN); 289 | mTorrentHandle.setPieceDeadline(i + mFirstPieceIndex, 1000); 290 | break; 291 | } 292 | } 293 | } else { 294 | Iterator piecesIterator = mPreparePieces.iterator(); 295 | while (piecesIterator.hasNext()) { 296 | int index = piecesIterator.next(); 297 | if (index == alert.getPieceIndex()) { 298 | piecesIterator.remove(); 299 | } 300 | } 301 | 302 | if(mHasPieces != null) 303 | mHasPieces[alert.getPieceIndex() - mFirstPieceIndex] = true; 304 | 305 | if (mPreparePieces.size() == 0) { 306 | startSequentialMode(); 307 | 308 | mPrepareProgress = 100d; 309 | sendStreamProgress(); 310 | mState = State.STREAMING; 311 | 312 | if (mListener != null) 313 | mListener.onStreamReady(this); 314 | } 315 | } 316 | } 317 | 318 | public void blockFinished(BlockFinishedAlert alert) { 319 | for (Integer index : mPreparePieces) { 320 | if (index == alert.getPieceIndex()) { 321 | mPrepareProgress += mProgressStep; 322 | break; 323 | } 324 | } 325 | 326 | sendStreamProgress(); 327 | } 328 | 329 | private void sendStreamProgress() { 330 | TorrentStatus status = mTorrentHandle.getStatus(); 331 | float progress = status.getProgress() * 100; 332 | int seeds = status.getNumSeeds(); 333 | int downloadSpeed = status.getDownloadPayloadRate(); 334 | 335 | if(mListener != null && mPrepareProgress >= 1) 336 | mListener.onStreamProgress(this, new StreamStatus(progress, mPrepareProgress.intValue(), seeds, downloadSpeed)); 337 | } 338 | 339 | @Override 340 | public int[] types() { 341 | return new int[] { AlertType.PIECE_FINISHED.getSwig(), AlertType.BLOCK_FINISHED.getSwig() }; 342 | } 343 | 344 | @Override 345 | public void alert(Alert alert) { 346 | switch (alert.getType()) { 347 | case PIECE_FINISHED: 348 | pieceFinished((PieceFinishedAlert) alert); 349 | break; 350 | case BLOCK_FINISHED: 351 | blockFinished((BlockFinishedAlert) alert); 352 | break; 353 | } 354 | } 355 | 356 | } -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/TorrentOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream; 21 | 22 | import java.io.File; 23 | 24 | public class TorrentOptions { 25 | 26 | protected String mSaveLocation = "/", mProxyHost, mProxyUsername, mProxyPassword, mPeerFingerprint; 27 | protected Integer mMaxDownloadSpeed = 0, mMaxUploadSpeed = 0, mMaxConnections = 200, mMaxDht = 88, mListeningPort = -1; 28 | protected Boolean mRemoveFiles = false, mAnonymousMode = true; 29 | protected Long mPrepareSize = 10 * 1024L * 1024L; 30 | 31 | public void setSaveLocation(String saveLocation) { 32 | mSaveLocation = saveLocation; 33 | } 34 | 35 | public void setSaveLocation(File saveLocation) { 36 | mSaveLocation = saveLocation.getAbsolutePath(); 37 | } 38 | 39 | public void setMaxUploadSpeed(Integer maxUploadSpeed) { 40 | mMaxUploadSpeed = maxUploadSpeed; 41 | } 42 | 43 | public void setMaxDownloadSpeed(Integer maxDownloadSpeed) { 44 | mMaxDownloadSpeed = maxDownloadSpeed; 45 | } 46 | 47 | public void setMaxConnections(Integer maxConnections) { 48 | mMaxConnections = maxConnections; 49 | } 50 | 51 | public void setMaxActiveDHT(Integer maxActiveDHT) { 52 | mMaxDht = maxActiveDHT; 53 | } 54 | 55 | public void setRemoveFilesAfterStop(Boolean b) { 56 | mRemoveFiles = b; 57 | } 58 | 59 | public void setPrepareSize(Long prepareSize) { 60 | mPrepareSize = prepareSize; 61 | } 62 | 63 | public void setListeningPort(Integer port) { 64 | mListeningPort = port; 65 | } 66 | 67 | public void setProxy(String host, String username, String password) { 68 | mProxyHost = host; 69 | mProxyUsername = username; 70 | mProxyPassword = password; 71 | } 72 | 73 | public void setPeerFingerprint(String peerId) { 74 | mPeerFingerprint = peerId; 75 | mAnonymousMode = false; 76 | } 77 | 78 | public void setAnonymousMode(Boolean enable) { 79 | mAnonymousMode = enable; 80 | if(mAnonymousMode) 81 | mPeerFingerprint = null; 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/TorrentStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of TorrentStreamer-Android. 3 | * 4 | * TorrentStreamer-Android is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * TorrentStreamer-Android is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with TorrentStreamer-Android. If not, see . 16 | */ 17 | 18 | package com.github.sv244.torrentstream; 19 | 20 | import android.net.Uri; 21 | import android.os.Handler; 22 | import android.os.HandlerThread; 23 | 24 | import com.frostwire.jlibtorrent.DHT; 25 | import com.frostwire.jlibtorrent.Downloader; 26 | import com.frostwire.jlibtorrent.Priority; 27 | import com.frostwire.jlibtorrent.Session; 28 | import com.frostwire.jlibtorrent.SettingsPack; 29 | import com.frostwire.jlibtorrent.TorrentHandle; 30 | import com.frostwire.jlibtorrent.TorrentInfo; 31 | import com.frostwire.jlibtorrent.alerts.TorrentAddedAlert; 32 | import com.frostwire.jlibtorrent.swig.settings_pack; 33 | import com.github.sv244.torrentstream.exceptions.DirectoryCreationException; 34 | import com.github.sv244.torrentstream.exceptions.NotInitializedException; 35 | import com.github.sv244.torrentstream.exceptions.TorrentInfoException; 36 | import com.github.sv244.torrentstream.listeners.DHTStatsAlertListener; 37 | import com.github.sv244.torrentstream.listeners.TorrentAddedAlertListener; 38 | import com.github.sv244.torrentstream.listeners.TorrentListener; 39 | import com.github.sv244.torrentstream.utils.FileUtils; 40 | import com.github.sv244.torrentstream.utils.ThreadUtils; 41 | 42 | import java.io.ByteArrayOutputStream; 43 | import java.io.File; 44 | import java.io.FileInputStream; 45 | import java.io.FileNotFoundException; 46 | import java.io.IOException; 47 | import java.io.InputStream; 48 | import java.net.HttpURLConnection; 49 | import java.net.MalformedURLException; 50 | import java.net.URL; 51 | import java.util.ArrayList; 52 | import java.util.List; 53 | 54 | public class TorrentStream { 55 | 56 | private static final String LIBTORRENT_THREAD_NAME = "TORRENTSTREAM_LIBTORRENT", STREAMING_THREAD_NAME = "TORRENTSTREAMER_STREAMING"; 57 | private static TorrentStream INSTANCE; 58 | 59 | private Session mTorrentSession; 60 | private DHT mDHT; 61 | private Boolean mInitialised = false, mIsStreaming = false, mIsCancelled = false; 62 | private TorrentOptions mTorrentOptions; 63 | 64 | private Torrent mCurrentTorrent; 65 | private String mCurrentTorrentUrl; 66 | private Integer mDhtNodes = 0; 67 | 68 | private List mListener = new ArrayList<>(); 69 | 70 | private HandlerThread mLibTorrentThread, mStreamingThread; 71 | private Handler mLibTorrentHandler, mStreamingHandler; 72 | 73 | private TorrentStream(TorrentOptions options) { 74 | mTorrentOptions = options; 75 | initialise(); 76 | } 77 | 78 | public static TorrentStream init(TorrentOptions options) { 79 | INSTANCE = new TorrentStream(options); 80 | return INSTANCE; 81 | } 82 | 83 | public static TorrentStream getInstance() throws NotInitializedException { 84 | if(INSTANCE == null) 85 | throw new NotInitializedException(); 86 | 87 | return INSTANCE; 88 | } 89 | 90 | private void initialise() { 91 | if (mLibTorrentThread != null && mTorrentSession != null) { 92 | resumeSession(); 93 | } else { 94 | if(mInitialised) { 95 | if (mLibTorrentThread != null) { 96 | mLibTorrentThread.interrupt(); 97 | } 98 | } 99 | 100 | mLibTorrentThread = new HandlerThread(LIBTORRENT_THREAD_NAME); 101 | mLibTorrentThread.start(); 102 | mLibTorrentHandler = new Handler(mLibTorrentThread.getLooper()); 103 | mLibTorrentHandler.post(new Runnable() { 104 | @Override 105 | public void run() { 106 | mTorrentSession = new Session(); 107 | setOptions(mTorrentOptions); 108 | 109 | mTorrentSession.addListener(mDhtStatsAlertListener); 110 | 111 | mDHT = new DHT(mTorrentSession); 112 | mDHT.start(); 113 | 114 | mInitialised = true; 115 | } 116 | }); 117 | } 118 | } 119 | 120 | /** 121 | * Resume TorrentSession 122 | */ 123 | public void resumeSession() { 124 | if (mLibTorrentThread != null && mTorrentSession != null) { 125 | mLibTorrentHandler.removeCallbacksAndMessages(null); 126 | 127 | //resume torrent session if needed 128 | if (mTorrentSession.isPaused()) { 129 | mLibTorrentHandler.post(new Runnable() { 130 | @Override 131 | public void run() { 132 | mTorrentSession.resume(); 133 | } 134 | }); 135 | } 136 | 137 | //start DHT if needed 138 | if (mDHT != null && !mDHT.isRunning()) { 139 | mLibTorrentHandler.post(new Runnable() { 140 | @Override 141 | public void run() { 142 | mDHT.start(); 143 | } 144 | }); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Pause TorrentSession 151 | */ 152 | public void pauseSession() { 153 | if(!mIsStreaming) 154 | mLibTorrentHandler.post(new Runnable() { 155 | @Override 156 | public void run() { 157 | mTorrentSession.pause(); 158 | } 159 | }); 160 | } 161 | 162 | /** 163 | * Get torrent metadata, either by downloading the .torrent or fetching the magnet 164 | * @param torrentUrl {@link String} URL to .torrent or magnet link 165 | * @return {@link TorrentInfo} 166 | */ 167 | private TorrentInfo getTorrentInfo(String torrentUrl) { 168 | if (torrentUrl.startsWith("magnet")) { 169 | Downloader d = new Downloader(mTorrentSession); 170 | 171 | byte[] data = d.fetchMagnet(torrentUrl, 30000); 172 | if(data != null) 173 | try { 174 | return TorrentInfo.bdecode(data); 175 | } catch (IllegalArgumentException e) { 176 | // Eat exception 177 | } 178 | 179 | } else if(torrentUrl.startsWith("http") || torrentUrl.startsWith("https")){ 180 | try { 181 | URL url = new URL(torrentUrl); 182 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 183 | 184 | connection.setRequestMethod("GET"); 185 | connection.setInstanceFollowRedirects(true); 186 | connection.connect(); 187 | 188 | InputStream inputStream = connection.getInputStream(); 189 | 190 | byte[] responseByteArray = new byte[0]; 191 | 192 | if(connection.getResponseCode() == 200) { 193 | responseByteArray = getBytesFromInputStream(inputStream); 194 | } 195 | 196 | inputStream.close(); 197 | connection.disconnect(); 198 | 199 | if(responseByteArray.length > 0) { 200 | return TorrentInfo.bdecode(responseByteArray); 201 | } 202 | } catch (MalformedURLException e) { 203 | // Eat exception 204 | } catch (IOException e) { 205 | // Eat exception 206 | } catch (IllegalArgumentException e) { 207 | // Eat exception 208 | } 209 | } else if(torrentUrl.startsWith("file")) { 210 | Uri path = Uri.parse(torrentUrl); 211 | File file = new File(path.getPath()); 212 | 213 | try { 214 | FileInputStream fileInputStream = new FileInputStream(file); 215 | byte[] responseByteArray = getBytesFromInputStream(fileInputStream); 216 | fileInputStream.close(); 217 | 218 | if(responseByteArray.length > 0) { 219 | return TorrentInfo.bdecode(responseByteArray); 220 | } 221 | } catch (FileNotFoundException e) { 222 | // Eat exception 223 | } catch (IOException e) { 224 | // Eat exception 225 | } catch (IllegalArgumentException e) { 226 | // Eat exception 227 | } 228 | } 229 | return null; 230 | } 231 | 232 | private byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { 233 | ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); 234 | 235 | int bufferSize = 1024; 236 | byte[] buffer = new byte[bufferSize]; 237 | 238 | int len = 0; 239 | while ((len = inputStream.read(buffer)) != -1) { 240 | byteBuffer.write(buffer, 0, len); 241 | } 242 | 243 | return byteBuffer.toByteArray(); 244 | } 245 | 246 | /** 247 | * Start stream download for specified torrent 248 | * @param torrentUrl {@link String} .torrent or magnet link 249 | */ 250 | public void startStream(final String torrentUrl) { 251 | if(!mInitialised) 252 | initialise(); 253 | 254 | if (mLibTorrentHandler == null || mIsStreaming) return; 255 | 256 | mIsCancelled = false; 257 | 258 | mStreamingThread = new HandlerThread(STREAMING_THREAD_NAME); 259 | mStreamingThread.start(); 260 | mStreamingHandler = new Handler(mStreamingThread.getLooper()); 261 | 262 | mStreamingHandler.post(new Runnable() { 263 | @Override 264 | public void run() { 265 | mIsStreaming = true; 266 | mCurrentTorrentUrl = torrentUrl; 267 | 268 | File saveDirectory = new File(mTorrentOptions.mSaveLocation); 269 | if (!saveDirectory.isDirectory()) { 270 | if (!saveDirectory.mkdirs()) { 271 | for (final TorrentListener listener : mListener) { 272 | ThreadUtils.runOnUiThread(new Runnable() { 273 | @Override 274 | public void run() { 275 | listener.onStreamError(null, new DirectoryCreationException()); 276 | } 277 | }); 278 | } 279 | mIsStreaming = false; 280 | return; 281 | } 282 | } 283 | 284 | mTorrentSession.removeListener(mTorrentAddedAlertListener); 285 | TorrentInfo torrentInfo = getTorrentInfo(torrentUrl); 286 | mTorrentSession.addListener(mTorrentAddedAlertListener); 287 | 288 | if (torrentInfo == null) { 289 | for (final TorrentListener listener : mListener) { 290 | ThreadUtils.runOnUiThread(new Runnable() { 291 | @Override 292 | public void run() { 293 | listener.onStreamError(null, new TorrentInfoException()); 294 | } 295 | }); 296 | } 297 | mIsStreaming = false; 298 | return; 299 | } 300 | 301 | Priority[] priorities = new Priority[torrentInfo.getNumPieces()]; 302 | for (int i = 0; i < priorities.length; i++) { 303 | priorities[i] = Priority.IGNORE; 304 | } 305 | 306 | if (!mCurrentTorrentUrl.equals(torrentUrl) || mIsCancelled) { 307 | return; 308 | } 309 | 310 | mTorrentSession.asyncAddTorrent(torrentInfo, saveDirectory, priorities, null); 311 | } 312 | }); 313 | } 314 | 315 | /** 316 | * Stop current torrent stream 317 | */ 318 | public void stopStream() { 319 | //remove all callbacks from handler 320 | if(mLibTorrentHandler != null) 321 | mLibTorrentHandler.removeCallbacksAndMessages(null); 322 | if(mStreamingHandler != null) 323 | mStreamingHandler.removeCallbacksAndMessages(null); 324 | 325 | mIsCancelled = true; 326 | mIsStreaming = false; 327 | if (mCurrentTorrent != null) { 328 | final File saveLocation = mCurrentTorrent.getSaveLocation(); 329 | 330 | mCurrentTorrent.pause(); 331 | mTorrentSession.removeListener(mCurrentTorrent); 332 | mTorrentSession.removeTorrent(mCurrentTorrent.getTorrentHandle()); 333 | mCurrentTorrent = null; 334 | 335 | if (mTorrentOptions.mRemoveFiles) { 336 | new Thread(new Runnable() { 337 | @Override 338 | public void run() { 339 | int tries = 0; 340 | while(!FileUtils.recursiveDelete(saveLocation) && tries < 5) { 341 | tries++; 342 | try { 343 | Thread.sleep(1000); // If deleted failed then something is still using the file, wait and then retry 344 | } catch (InterruptedException e) { 345 | // Eat exception 346 | } 347 | } 348 | } 349 | }).start(); 350 | } 351 | } 352 | 353 | if(mStreamingThread != null) 354 | mStreamingThread.interrupt(); 355 | 356 | for (final TorrentListener listener : mListener) { 357 | ThreadUtils.runOnUiThread(new Runnable() { 358 | @Override 359 | public void run() { 360 | listener.onStreamStopped(); 361 | } 362 | }); 363 | } 364 | } 365 | 366 | public TorrentOptions getOptions() { 367 | return mTorrentOptions; 368 | } 369 | 370 | public void setOptions(TorrentOptions options) { 371 | mTorrentOptions = options; 372 | 373 | SettingsPack settingsPack = new SettingsPack(); 374 | settingsPack.setAnonymousMode(mTorrentOptions.mAnonymousMode); 375 | 376 | settingsPack.setConnectionsLimit(mTorrentOptions.mMaxConnections); 377 | settingsPack.setDownloadRateLimit(mTorrentOptions.mMaxDownloadSpeed); 378 | settingsPack.setUploadRateLimit(mTorrentOptions.mMaxUploadSpeed); 379 | settingsPack.setInteger(settings_pack.int_types.active_dht_limit.swigValue(), mTorrentOptions.mMaxDht); 380 | 381 | if(mTorrentOptions.mListeningPort != -1) { 382 | String if_string = String.format("%s:%d", "0.0.0.0", mTorrentOptions.mListeningPort); 383 | settingsPack.setString(settings_pack.string_types.listen_interfaces.swigValue(), if_string); 384 | } 385 | 386 | if(mTorrentOptions.mProxyHost != null) 387 | settingsPack.setString(settings_pack.string_types.proxy_hostname.swigValue(), mTorrentOptions.mProxyHost); 388 | if(mTorrentOptions.mProxyUsername != null) 389 | settingsPack.setString(settings_pack.string_types.proxy_username.swigValue(), mTorrentOptions.mProxyUsername); 390 | if(mTorrentOptions.mProxyPassword != null) 391 | settingsPack.setString(settings_pack.string_types.proxy_password.swigValue(), mTorrentOptions.mProxyPassword); 392 | 393 | if(mTorrentOptions.mPeerFingerprint != null) 394 | settingsPack.setString(settings_pack.string_types.peer_fingerprint.swigValue(), mTorrentOptions.mPeerFingerprint); 395 | 396 | mTorrentSession.applySettings(settingsPack); 397 | } 398 | 399 | public boolean isStreaming() { 400 | return mIsStreaming; 401 | } 402 | 403 | public String getCurrentTorrentUrl() { 404 | return mCurrentTorrentUrl; 405 | } 406 | 407 | public Integer getTotalDhtNodes() { 408 | return mDhtNodes; 409 | } 410 | 411 | public void addListener(TorrentListener listener) { 412 | if(listener != null) 413 | mListener.add(listener); 414 | } 415 | 416 | public void removeListener(TorrentListener listener) { 417 | if(listener != null) 418 | mListener.remove(listener); 419 | } 420 | 421 | private DHTStatsAlertListener mDhtStatsAlertListener = new DHTStatsAlertListener() { 422 | @Override 423 | public void stats(int totalDhtNodes) { 424 | mDhtNodes = totalDhtNodes; 425 | } 426 | }; 427 | 428 | private TorrentAddedAlertListener mTorrentAddedAlertListener = new TorrentAddedAlertListener() { 429 | @Override 430 | public void torrentAdded(TorrentAddedAlert alert) { 431 | InternalTorrentListener listener = new InternalTorrentListener(); 432 | TorrentHandle th = mTorrentSession.findTorrent((alert).getHandle().getInfoHash()); 433 | mCurrentTorrent = new Torrent(th, listener, mTorrentOptions.mPrepareSize); 434 | mTorrentSession.addListener(mCurrentTorrent); 435 | } 436 | }; 437 | 438 | protected class InternalTorrentListener implements TorrentListener { 439 | 440 | public void onStreamStarted(final Torrent torrent) { 441 | for (final TorrentListener listener : mListener) { 442 | ThreadUtils.runOnUiThread(new Runnable() { 443 | @Override 444 | public void run() { 445 | listener.onStreamStarted(torrent); 446 | } 447 | }); 448 | } 449 | } 450 | 451 | public void onStreamError(final Torrent torrent, final Exception e) { 452 | for (final TorrentListener listener : mListener) { 453 | ThreadUtils.runOnUiThread(new Runnable() { 454 | @Override 455 | public void run() { 456 | listener.onStreamError(torrent, e); 457 | } 458 | }); 459 | } 460 | } 461 | 462 | public void onStreamReady(final Torrent torrent) { 463 | for (final TorrentListener listener : mListener) { 464 | ThreadUtils.runOnUiThread(new Runnable() { 465 | @Override 466 | public void run() { 467 | listener.onStreamReady(torrent); 468 | } 469 | }); 470 | } 471 | } 472 | 473 | public void onStreamProgress(final Torrent torrent, final StreamStatus status) { 474 | for (final TorrentListener listener : mListener) { 475 | ThreadUtils.runOnUiThread(new Runnable() { 476 | @Override 477 | public void run() { 478 | listener.onStreamProgress(torrent, status); 479 | } 480 | }); 481 | } 482 | } 483 | 484 | @Override 485 | public void onStreamStopped() { 486 | // Not used 487 | } 488 | 489 | @Override 490 | public void onStreamPrepared(final Torrent torrent) { 491 | for (final TorrentListener listener : mListener) { 492 | ThreadUtils.runOnUiThread(new Runnable() { 493 | @Override 494 | public void run() { 495 | listener.onStreamPrepared(torrent); 496 | } 497 | }); 498 | } 499 | } 500 | } 501 | 502 | } 503 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/exceptions/DirectoryCreationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.exceptions; 21 | 22 | public class DirectoryCreationException extends Exception { 23 | 24 | public DirectoryCreationException() { 25 | super("Could not create save directory"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/exceptions/NotInitializedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of TorrentStreamer-Android. 3 | * 4 | * TorrentStreamer-Android is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * TorrentStreamer-Android is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with TorrentStreamer-Android. If not, see . 16 | */ 17 | 18 | package com.github.sv244.torrentstream.exceptions; 19 | 20 | public class NotInitializedException extends Exception { 21 | 22 | public NotInitializedException() { 23 | super("TorrentStreamer is not initialized. Call init() first before getting an instance."); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/exceptions/TorrentInfoException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.exceptions; 21 | 22 | public class TorrentInfoException extends Exception { 23 | 24 | public TorrentInfoException() { 25 | super("No torrent info could be found"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/listeners/DHTStatsAlertListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.listeners; 21 | 22 | import com.frostwire.jlibtorrent.AlertListener; 23 | import com.frostwire.jlibtorrent.DHTRoutingBucket; 24 | import com.frostwire.jlibtorrent.alerts.Alert; 25 | import com.frostwire.jlibtorrent.alerts.AlertType; 26 | import com.frostwire.jlibtorrent.alerts.DhtStatsAlert; 27 | 28 | public abstract class DHTStatsAlertListener implements AlertListener { 29 | @Override 30 | public int[] types() { 31 | return new int[] { AlertType.DHT_STATS.getSwig() }; 32 | } 33 | 34 | public void alert(Alert alert) { 35 | if (alert instanceof DhtStatsAlert) { 36 | DhtStatsAlert dhtAlert = (DhtStatsAlert) alert; 37 | stats(countTotalDHTNodes(dhtAlert)); 38 | } 39 | } 40 | 41 | public abstract void stats(int totalDhtNodes); 42 | 43 | private int countTotalDHTNodes(DhtStatsAlert alert) { 44 | final DHTRoutingBucket[] routingTable = alert.getRoutingTable(); 45 | 46 | int totalNodes = 0; 47 | if (routingTable != null && routingTable.length > 0) { 48 | for (int i=0; i < routingTable.length; i++) { 49 | DHTRoutingBucket bucket = routingTable[i]; 50 | totalNodes += bucket.numNodes(); 51 | } 52 | } 53 | 54 | return totalNodes; 55 | } 56 | } -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/listeners/TorrentAddedAlertListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.listeners; 21 | 22 | import com.frostwire.jlibtorrent.AlertListener; 23 | import com.frostwire.jlibtorrent.alerts.Alert; 24 | import com.frostwire.jlibtorrent.alerts.AlertType; 25 | import com.frostwire.jlibtorrent.alerts.TorrentAddedAlert; 26 | 27 | public abstract class TorrentAddedAlertListener implements AlertListener { 28 | @Override 29 | public int[] types() { 30 | return new int[] { AlertType.TORRENT_ADDED.getSwig() }; 31 | } 32 | 33 | @Override 34 | public void alert(Alert alert) { 35 | switch (alert.getType()) { 36 | case TORRENT_ADDED: 37 | torrentAdded((TorrentAddedAlert) alert); 38 | break; 39 | } 40 | } 41 | 42 | public abstract void torrentAdded(TorrentAddedAlert alert); 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/listeners/TorrentListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of TorrentStreamer-Android. 3 | * 4 | * TorrentStreamer-Android is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * TorrentStreamer-Android is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with TorrentStreamer-Android. If not, see . 16 | */ 17 | 18 | package com.github.sv244.torrentstream.listeners; 19 | 20 | import com.github.sv244.torrentstream.StreamStatus; 21 | import com.github.sv244.torrentstream.Torrent; 22 | 23 | public interface TorrentListener { 24 | void onStreamPrepared(Torrent torrent); 25 | 26 | void onStreamStarted(Torrent torrent); 27 | 28 | void onStreamError(Torrent torrent, Exception e); 29 | 30 | void onStreamReady(Torrent torrent); 31 | 32 | void onStreamProgress(Torrent torrent, StreamStatus status); 33 | 34 | void onStreamStopped(); 35 | } 36 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.utils; 21 | 22 | import java.io.File; 23 | 24 | public class FileUtils { 25 | 26 | /** 27 | * Delete every item below the File location 28 | * 29 | * @param file Location 30 | */ 31 | public static boolean recursiveDelete(File file) { 32 | if (file.isDirectory()) { 33 | String[] children = file.list(); 34 | if (children == null) return false; 35 | for (String child : children) { 36 | recursiveDelete(new File(file, child)); 37 | } 38 | } 39 | 40 | return file.delete(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /library/src/main/java/com/github/sv244/torrentstream/utils/ThreadUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package com.github.sv244.torrentstream.utils; 21 | 22 | import android.os.Handler; 23 | import android.os.Looper; 24 | 25 | public class ThreadUtils { 26 | 27 | /** 28 | * Execute the given {@link Runnable} on the ui thread. 29 | * 30 | * @param runnable The runnable to execute. 31 | */ 32 | public static void runOnUiThread(Runnable runnable) { 33 | Thread uiThread = Looper.getMainLooper().getThread(); 34 | if (Thread.currentThread() != uiThread) new Handler(Looper.getMainLooper()).post(runnable); 35 | else runnable.run(); 36 | } 37 | } -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | apply plugin: 'com.android.application' 21 | 22 | android { 23 | compileSdkVersion 23 24 | buildToolsVersion "23.0.0" 25 | 26 | defaultConfig { 27 | applicationId "com.github.torrentstreamer.sample" 28 | minSdkVersion 15 29 | targetSdkVersion 23 30 | versionCode 1 31 | versionName "1.0" 32 | } 33 | buildTypes { 34 | release { 35 | minifyEnabled false 36 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 37 | } 38 | } 39 | lintOptions { 40 | abortOnError false 41 | } 42 | } 43 | 44 | dependencies { 45 | compile fileTree(dir: 'libs', include: ['*.jar']) 46 | compile 'com.android.support:appcompat-v7:23.0.1' 47 | compile project(':library') 48 | } 49 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Sebastiaan/Development/Android/SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /sample/src/main/java/eu/sv244/torrentstreamer/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * * This file is part of TorrentStreamer-Android. 4 | * * 5 | * * TorrentStreamer-Android is free software: you can redistribute it and/or modify 6 | * * it under the terms of the GNU General Public License as published by 7 | * * the Free Software Foundation, either version 3 of the License, or 8 | * * (at your option) any later version. 9 | * * 10 | * * TorrentStreamer-Android is distributed in the hope that it will be useful, 11 | * * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * * GNU General Public License for more details. 14 | * * 15 | * * You should have received a copy of the GNU General Public License 16 | * * along with TorrentStreamer-Android. If not, see . 17 | * 18 | */ 19 | 20 | package eu.sv244.torrentstreamer.sample; 21 | 22 | import android.content.Intent; 23 | import android.net.Uri; 24 | import android.os.Bundle; 25 | import android.os.Environment; 26 | import android.support.v7.app.AppCompatActivity; 27 | import android.util.Log; 28 | import android.view.View; 29 | import android.widget.Button; 30 | import android.widget.ProgressBar; 31 | 32 | import com.github.sv244.torrentstream.StreamStatus; 33 | import com.github.sv244.torrentstream.Torrent; 34 | import com.github.sv244.torrentstream.TorrentOptions; 35 | import com.github.sv244.torrentstream.TorrentStream; 36 | import com.github.sv244.torrentstream.listeners.TorrentListener; 37 | 38 | import java.io.UnsupportedEncodingException; 39 | import java.net.URLDecoder; 40 | 41 | public class MainActivity extends AppCompatActivity implements TorrentListener { 42 | 43 | private Button mButton; 44 | private ProgressBar mProgressBar; 45 | private TorrentStream mTorrentStream; 46 | 47 | private String mStreamUrl = "magnet:?xt=urn:btih:D60795899F8488E7E489BA642DEFBCE1B23C9DA0&dn=Kingsman%3A+The+Secret+Service+%282014%29+%5B720p%5D&tr=http%3A%2F%2Ftracker.yify-torrents.com%2Fannounce&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.org%3A80&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Fp4p.arenabg.ch%3A1337&tr=udp%3A%2F%2Fp4p.arenabg.com%3A1337"; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | setContentView(R.layout.activity_main); 53 | 54 | String action = getIntent().getAction(); 55 | Uri data = getIntent().getData(); 56 | if (action != null && action.equals(Intent.ACTION_VIEW) && data != null) { 57 | try { 58 | mStreamUrl = URLDecoder.decode(data.toString(), "utf-8"); 59 | } catch (UnsupportedEncodingException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | TorrentOptions torrentOptions = new TorrentOptions(); 65 | torrentOptions.setSaveLocation(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)); 66 | torrentOptions.setRemoveFilesAfterStop(true); 67 | 68 | mTorrentStream = TorrentStream.init(torrentOptions); 69 | mTorrentStream.addListener(this); 70 | 71 | mButton = (Button) findViewById(R.id.button); 72 | mButton.setOnClickListener(mOnClickListener); 73 | mProgressBar = (ProgressBar) findViewById(R.id.progress); 74 | 75 | mProgressBar.setMax(100); 76 | } 77 | 78 | View.OnClickListener mOnClickListener = new View.OnClickListener() { 79 | @Override 80 | public void onClick(View v) { 81 | mProgressBar.setProgress(0); 82 | if(mTorrentStream.isStreaming()) { 83 | mTorrentStream.stopStream(); 84 | mButton.setText("Start stream"); 85 | return; 86 | } 87 | //mTorrentStream.startStream("https://yts.to/torrent/download/D60795899F8488E7E489BA642DEFBCE1B23C9DA0.torrent"); 88 | mTorrentStream.startStream(mStreamUrl); // Start Torrent Stream download to phone 89 | mButton.setText("Stop stream"); 90 | } 91 | }; 92 | 93 | @Override 94 | public void onStreamPrepared(Torrent torrent) { 95 | Log.d("Torrent", "OnStreamPrepared"); 96 | torrent.startDownload(); 97 | } 98 | 99 | @Override 100 | public void onStreamStarted(Torrent torrent) { 101 | Log.d("Torrent", "onStreamStarted"); 102 | } 103 | 104 | @Override 105 | public void onStreamError(Torrent torrent, Exception e) { 106 | Log.d("Torrent", "onStreamError"); 107 | mButton.setText("Start stream"); 108 | } 109 | 110 | @Override 111 | public void onStreamReady(Torrent torrent) { 112 | mProgressBar.setProgress(100); 113 | Log.d("Torrent", "onStreamReady: " + torrent.getVideoFile()); 114 | 115 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(torrent.getVideoFile().toString())); 116 | intent.setDataAndType(Uri.parse(torrent.getVideoFile().toString()), "video/mp4"); 117 | startActivity(intent); 118 | } 119 | 120 | @Override 121 | public void onStreamProgress(Torrent torrent, StreamStatus status) { 122 | if(status.bufferProgress <= 100 && mProgressBar.getProgress() < 100 && mProgressBar.getProgress() != status.bufferProgress) { 123 | Log.d("Torrent", "Progress: " + status.bufferProgress); 124 | mProgressBar.setProgress(status.bufferProgress); 125 | } 126 | } 127 | 128 | @Override 129 | public void onStreamStopped() { 130 | Log.d("Torrent", "onStreamStopped"); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | 28 |