From d88fe691cf8e0e5e736d49be43290cafc6312477 Mon Sep 17 00:00:00 2001 From: wb9688 Date: Sat, 5 Aug 2017 10:03:56 +0200 Subject: [PATCH 1/2] Use Gradle --- .gitignore | 14 ++ .travis.yml | 1 + build.gradle | 14 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54712 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ settings.gradle | 1 + .../schabi/newpipe/extractor/Downloader.java | 0 .../schabi/newpipe/extractor/Extractor.java | 0 .../org/schabi/newpipe/extractor/Info.java | 0 .../schabi/newpipe/extractor/InfoItem.java | 0 .../newpipe/extractor/InfoItemCollector.java | 0 .../newpipe/extractor/ListExtractor.java | 0 .../schabi/newpipe/extractor/MediaFormat.java | 0 .../org/schabi/newpipe/extractor/NewPipe.java | 0 .../schabi/newpipe/extractor/ServiceList.java | 0 .../newpipe/extractor/StreamingService.java | 0 .../extractor/SuggestionExtractor.java | 0 .../newpipe/extractor/UrlIdHandler.java | 0 .../extractor/channel}/ChannelExtractor.java | 0 .../extractor/channel}/ChannelInfo.java | 0 .../extractor/channel}/ChannelInfoItem.java | 0 .../channel}/ChannelInfoItemCollector.java | 0 .../channel}/ChannelInfoItemExtractor.java | 0 .../ContentNotAvailableException.java | 0 .../exceptions}/ExtractionException.java | 0 .../exceptions}/FoundAdException.java | 0 .../exceptions}/ParsingException.java | 0 .../exceptions}/ReCaptchaException.java | 0 .../playlist}/PlaylistExtractor.java | 0 .../extractor/playlist}/PlaylistInfo.java | 0 .../extractor/playlist}/PlaylistInfoItem.java | 0 .../playlist}/PlaylistInfoItemCollector.java | 0 .../playlist}/PlaylistInfoItemExtractor.java | 0 .../search}/InfoItemSearchCollector.java | 0 .../extractor/search}/SearchEngine.java | 0 .../extractor/search}/SearchResult.java | 0 .../SoundcloudChannelExtractor.java | 0 .../SoundcloudChannelInfoItemExtractor.java | 0 .../SoundcloudChannelUrlIdHandler.java | 0 .../soundcloud/SoundcloudParsingHelper.java | 0 .../SoundcloudPlaylistExtractor.java | 0 .../SoundcloudPlaylistUrlIdHandler.java | 0 .../soundcloud/SoundcloudSearchEngine.java | 0 .../soundcloud/SoundcloudService.java | 0 .../soundcloud/SoundcloudStreamExtractor.java | 0 .../SoundcloudStreamInfoItemExtractor.java | 0 .../SoundcloudStreamUrlIdHandler.java | 0 .../SoundcloudSuggestionExtractor.java | 0 .../extractor/services}/youtube/ItagItem.java | 0 .../youtube/YoutubeChannelExtractor.java | 0 .../YoutubeChannelInfoItemExtractor.java | 0 .../youtube/YoutubeChannelUrlIdHandler.java | 0 .../youtube/YoutubeParsingHelper.java | 0 .../youtube/YoutubePlaylistExtractor.java | 0 .../youtube/YoutubePlaylistUrlIdHandler.java | 0 .../youtube/YoutubeSearchEngine.java | 0 .../services}/youtube/YoutubeService.java | 0 .../youtube/YoutubeStreamExtractor.java | 0 .../YoutubeStreamInfoItemExtractor.java | 0 .../youtube/YoutubeStreamUrlIdHandler.java | 0 .../youtube/YoutubeSuggestionExtractor.java | 0 .../extractor/stream}/AudioStream.java | 0 .../newpipe/extractor/stream}/Stream.java | 0 .../extractor/stream}/StreamExtractor.java | 0 .../newpipe/extractor/stream}/StreamInfo.java | 0 .../extractor/stream}/StreamInfoItem.java | 0 .../stream}/StreamInfoItemCollector.java | 0 .../stream}/StreamInfoItemExtractor.java | 0 .../newpipe/extractor/stream}/StreamType.java | 0 .../extractor/stream}/VideoStream.java | 0 .../extractor/utils}/DashMpdParser.java | 0 .../newpipe/extractor/utils}/Parser.java | 0 .../newpipe/extractor/utils}/Utils.java | 0 .../java/org/schabi/newpipe/Downloader.java | 146 +++++++++++++++ .../youtube/YoutubeChannelExtractorTest.java | 107 +++++++++++ .../youtube/YoutubeSearchEngineAllTest.java | 67 +++++++ .../YoutubeSearchEngineChannelTest.java | 73 ++++++++ .../YoutubeSearchEngineStreamTest.java | 73 ++++++++ .../youtube/YoutubeSearchResultTest.java | 52 ++++++ .../YoutubeStreamExtractorDefaultTest.java | 141 ++++++++++++++ .../YoutubeStreamExtractorGemaTest.java | 55 ++++++ .../YoutubeStreamExtractorRestrictedTest.java | 108 +++++++++++ .../YoutubeStreamUrlIdHandlerTest.java | 119 ++++++++++++ 85 files changed, 1233 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle rename Downloader.java => src/main/java/org/schabi/newpipe/extractor/Downloader.java (100%) rename Extractor.java => src/main/java/org/schabi/newpipe/extractor/Extractor.java (100%) rename Info.java => src/main/java/org/schabi/newpipe/extractor/Info.java (100%) rename InfoItem.java => src/main/java/org/schabi/newpipe/extractor/InfoItem.java (100%) rename InfoItemCollector.java => src/main/java/org/schabi/newpipe/extractor/InfoItemCollector.java (100%) rename ListExtractor.java => src/main/java/org/schabi/newpipe/extractor/ListExtractor.java (100%) rename MediaFormat.java => src/main/java/org/schabi/newpipe/extractor/MediaFormat.java (100%) rename NewPipe.java => src/main/java/org/schabi/newpipe/extractor/NewPipe.java (100%) rename ServiceList.java => src/main/java/org/schabi/newpipe/extractor/ServiceList.java (100%) rename StreamingService.java => src/main/java/org/schabi/newpipe/extractor/StreamingService.java (100%) rename SuggestionExtractor.java => src/main/java/org/schabi/newpipe/extractor/SuggestionExtractor.java (100%) rename UrlIdHandler.java => src/main/java/org/schabi/newpipe/extractor/UrlIdHandler.java (100%) rename {channel => src/main/java/org/schabi/newpipe/extractor/channel}/ChannelExtractor.java (100%) rename {channel => src/main/java/org/schabi/newpipe/extractor/channel}/ChannelInfo.java (100%) rename {channel => src/main/java/org/schabi/newpipe/extractor/channel}/ChannelInfoItem.java (100%) rename {channel => src/main/java/org/schabi/newpipe/extractor/channel}/ChannelInfoItemCollector.java (100%) rename {channel => src/main/java/org/schabi/newpipe/extractor/channel}/ChannelInfoItemExtractor.java (100%) rename {exceptions => src/main/java/org/schabi/newpipe/extractor/exceptions}/ContentNotAvailableException.java (100%) rename {exceptions => src/main/java/org/schabi/newpipe/extractor/exceptions}/ExtractionException.java (100%) rename {exceptions => src/main/java/org/schabi/newpipe/extractor/exceptions}/FoundAdException.java (100%) rename {exceptions => src/main/java/org/schabi/newpipe/extractor/exceptions}/ParsingException.java (100%) rename {exceptions => src/main/java/org/schabi/newpipe/extractor/exceptions}/ReCaptchaException.java (100%) rename {playlist => src/main/java/org/schabi/newpipe/extractor/playlist}/PlaylistExtractor.java (100%) rename {playlist => src/main/java/org/schabi/newpipe/extractor/playlist}/PlaylistInfo.java (100%) rename {playlist => src/main/java/org/schabi/newpipe/extractor/playlist}/PlaylistInfoItem.java (100%) rename {playlist => src/main/java/org/schabi/newpipe/extractor/playlist}/PlaylistInfoItemCollector.java (100%) rename {playlist => src/main/java/org/schabi/newpipe/extractor/playlist}/PlaylistInfoItemExtractor.java (100%) rename {search => src/main/java/org/schabi/newpipe/extractor/search}/InfoItemSearchCollector.java (100%) rename {search => src/main/java/org/schabi/newpipe/extractor/search}/SearchEngine.java (100%) rename {search => src/main/java/org/schabi/newpipe/extractor/search}/SearchResult.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudChannelExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudChannelInfoItemExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudChannelUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudParsingHelper.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudPlaylistExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudPlaylistUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudSearchEngine.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudService.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudStreamExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudStreamInfoItemExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudStreamUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/soundcloud/SoundcloudSuggestionExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/ItagItem.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeChannelExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeChannelInfoItemExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeChannelUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeParsingHelper.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubePlaylistExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubePlaylistUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeSearchEngine.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeService.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeStreamExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeStreamInfoItemExtractor.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeStreamUrlIdHandler.java (100%) rename {services => src/main/java/org/schabi/newpipe/extractor/services}/youtube/YoutubeSuggestionExtractor.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/AudioStream.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/Stream.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamExtractor.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamInfo.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamInfoItem.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamInfoItemCollector.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamInfoItemExtractor.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/StreamType.java (100%) rename {stream => src/main/java/org/schabi/newpipe/extractor/stream}/VideoStream.java (100%) rename {utils => src/main/java/org/schabi/newpipe/extractor/utils}/DashMpdParser.java (100%) rename {utils => src/main/java/org/schabi/newpipe/extractor/utils}/Parser.java (100%) rename {utils => src/main/java/org/schabi/newpipe/extractor/utils}/Utils.java (100%) create mode 100644 src/test/java/org/schabi/newpipe/Downloader.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java create mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a1fc39c07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..dff5f3a5d --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..0bd985d15 --- /dev/null +++ b/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'java-library' + +repositories { + jcenter() +} + +dependencies { + implementation 'org.json:json:20160807' + implementation 'org.jsoup:jsoup:1.9.2' + implementation 'org.mozilla:rhino:1.7.7.1' + + testImplementation 'junit:junit:4.12' +} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2bb7222bc039f98cae71fbd366ba096ecce314f0 GIT binary patch literal 54712 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

49F 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 pugjmECO^C`Kg%uv?7#V7-b#Z3) \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..9a1b299d4 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'NewPipeExtractor' diff --git a/Downloader.java b/src/main/java/org/schabi/newpipe/extractor/Downloader.java similarity index 100% rename from Downloader.java rename to src/main/java/org/schabi/newpipe/extractor/Downloader.java diff --git a/Extractor.java b/src/main/java/org/schabi/newpipe/extractor/Extractor.java similarity index 100% rename from Extractor.java rename to src/main/java/org/schabi/newpipe/extractor/Extractor.java diff --git a/Info.java b/src/main/java/org/schabi/newpipe/extractor/Info.java similarity index 100% rename from Info.java rename to src/main/java/org/schabi/newpipe/extractor/Info.java diff --git a/InfoItem.java b/src/main/java/org/schabi/newpipe/extractor/InfoItem.java similarity index 100% rename from InfoItem.java rename to src/main/java/org/schabi/newpipe/extractor/InfoItem.java diff --git a/InfoItemCollector.java b/src/main/java/org/schabi/newpipe/extractor/InfoItemCollector.java similarity index 100% rename from InfoItemCollector.java rename to src/main/java/org/schabi/newpipe/extractor/InfoItemCollector.java diff --git a/ListExtractor.java b/src/main/java/org/schabi/newpipe/extractor/ListExtractor.java similarity index 100% rename from ListExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/ListExtractor.java diff --git a/MediaFormat.java b/src/main/java/org/schabi/newpipe/extractor/MediaFormat.java similarity index 100% rename from MediaFormat.java rename to src/main/java/org/schabi/newpipe/extractor/MediaFormat.java diff --git a/NewPipe.java b/src/main/java/org/schabi/newpipe/extractor/NewPipe.java similarity index 100% rename from NewPipe.java rename to src/main/java/org/schabi/newpipe/extractor/NewPipe.java diff --git a/ServiceList.java b/src/main/java/org/schabi/newpipe/extractor/ServiceList.java similarity index 100% rename from ServiceList.java rename to src/main/java/org/schabi/newpipe/extractor/ServiceList.java diff --git a/StreamingService.java b/src/main/java/org/schabi/newpipe/extractor/StreamingService.java similarity index 100% rename from StreamingService.java rename to src/main/java/org/schabi/newpipe/extractor/StreamingService.java diff --git a/SuggestionExtractor.java b/src/main/java/org/schabi/newpipe/extractor/SuggestionExtractor.java similarity index 100% rename from SuggestionExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/SuggestionExtractor.java diff --git a/UrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/UrlIdHandler.java similarity index 100% rename from UrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/UrlIdHandler.java diff --git a/channel/ChannelExtractor.java b/src/main/java/org/schabi/newpipe/extractor/channel/ChannelExtractor.java similarity index 100% rename from channel/ChannelExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/channel/ChannelExtractor.java diff --git a/channel/ChannelInfo.java b/src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfo.java similarity index 100% rename from channel/ChannelInfo.java rename to src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfo.java diff --git a/channel/ChannelInfoItem.java b/src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItem.java similarity index 100% rename from channel/ChannelInfoItem.java rename to src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItem.java diff --git a/channel/ChannelInfoItemCollector.java b/src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItemCollector.java similarity index 100% rename from channel/ChannelInfoItemCollector.java rename to src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItemCollector.java diff --git a/channel/ChannelInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItemExtractor.java similarity index 100% rename from channel/ChannelInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/channel/ChannelInfoItemExtractor.java diff --git a/exceptions/ContentNotAvailableException.java b/src/main/java/org/schabi/newpipe/extractor/exceptions/ContentNotAvailableException.java similarity index 100% rename from exceptions/ContentNotAvailableException.java rename to src/main/java/org/schabi/newpipe/extractor/exceptions/ContentNotAvailableException.java diff --git a/exceptions/ExtractionException.java b/src/main/java/org/schabi/newpipe/extractor/exceptions/ExtractionException.java similarity index 100% rename from exceptions/ExtractionException.java rename to src/main/java/org/schabi/newpipe/extractor/exceptions/ExtractionException.java diff --git a/exceptions/FoundAdException.java b/src/main/java/org/schabi/newpipe/extractor/exceptions/FoundAdException.java similarity index 100% rename from exceptions/FoundAdException.java rename to src/main/java/org/schabi/newpipe/extractor/exceptions/FoundAdException.java diff --git a/exceptions/ParsingException.java b/src/main/java/org/schabi/newpipe/extractor/exceptions/ParsingException.java similarity index 100% rename from exceptions/ParsingException.java rename to src/main/java/org/schabi/newpipe/extractor/exceptions/ParsingException.java diff --git a/exceptions/ReCaptchaException.java b/src/main/java/org/schabi/newpipe/extractor/exceptions/ReCaptchaException.java similarity index 100% rename from exceptions/ReCaptchaException.java rename to src/main/java/org/schabi/newpipe/extractor/exceptions/ReCaptchaException.java diff --git a/playlist/PlaylistExtractor.java b/src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistExtractor.java similarity index 100% rename from playlist/PlaylistExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistExtractor.java diff --git a/playlist/PlaylistInfo.java b/src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfo.java similarity index 100% rename from playlist/PlaylistInfo.java rename to src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfo.java diff --git a/playlist/PlaylistInfoItem.java b/src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItem.java similarity index 100% rename from playlist/PlaylistInfoItem.java rename to src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItem.java diff --git a/playlist/PlaylistInfoItemCollector.java b/src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItemCollector.java similarity index 100% rename from playlist/PlaylistInfoItemCollector.java rename to src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItemCollector.java diff --git a/playlist/PlaylistInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItemExtractor.java similarity index 100% rename from playlist/PlaylistInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/playlist/PlaylistInfoItemExtractor.java diff --git a/search/InfoItemSearchCollector.java b/src/main/java/org/schabi/newpipe/extractor/search/InfoItemSearchCollector.java similarity index 100% rename from search/InfoItemSearchCollector.java rename to src/main/java/org/schabi/newpipe/extractor/search/InfoItemSearchCollector.java diff --git a/search/SearchEngine.java b/src/main/java/org/schabi/newpipe/extractor/search/SearchEngine.java similarity index 100% rename from search/SearchEngine.java rename to src/main/java/org/schabi/newpipe/extractor/search/SearchEngine.java diff --git a/search/SearchResult.java b/src/main/java/org/schabi/newpipe/extractor/search/SearchResult.java similarity index 100% rename from search/SearchResult.java rename to src/main/java/org/schabi/newpipe/extractor/search/SearchResult.java diff --git a/services/soundcloud/SoundcloudChannelExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudChannelExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelExtractor.java diff --git a/services/soundcloud/SoundcloudChannelInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelInfoItemExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudChannelInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelInfoItemExtractor.java diff --git a/services/soundcloud/SoundcloudChannelUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelUrlIdHandler.java similarity index 100% rename from services/soundcloud/SoundcloudChannelUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudChannelUrlIdHandler.java diff --git a/services/soundcloud/SoundcloudParsingHelper.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java similarity index 100% rename from services/soundcloud/SoundcloudParsingHelper.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java diff --git a/services/soundcloud/SoundcloudPlaylistExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudPlaylistExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudPlaylistExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudPlaylistExtractor.java diff --git a/services/soundcloud/SoundcloudPlaylistUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudPlaylistUrlIdHandler.java similarity index 100% rename from services/soundcloud/SoundcloudPlaylistUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudPlaylistUrlIdHandler.java diff --git a/services/soundcloud/SoundcloudSearchEngine.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudSearchEngine.java similarity index 100% rename from services/soundcloud/SoundcloudSearchEngine.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudSearchEngine.java diff --git a/services/soundcloud/SoundcloudService.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudService.java similarity index 100% rename from services/soundcloud/SoundcloudService.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudService.java diff --git a/services/soundcloud/SoundcloudStreamExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudStreamExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamExtractor.java diff --git a/services/soundcloud/SoundcloudStreamInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamInfoItemExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudStreamInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamInfoItemExtractor.java diff --git a/services/soundcloud/SoundcloudStreamUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamUrlIdHandler.java similarity index 100% rename from services/soundcloud/SoundcloudStreamUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudStreamUrlIdHandler.java diff --git a/services/soundcloud/SoundcloudSuggestionExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudSuggestionExtractor.java similarity index 100% rename from services/soundcloud/SoundcloudSuggestionExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudSuggestionExtractor.java diff --git a/services/youtube/ItagItem.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java similarity index 100% rename from services/youtube/ItagItem.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java diff --git a/services/youtube/YoutubeChannelExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java similarity index 100% rename from services/youtube/YoutubeChannelExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java diff --git a/services/youtube/YoutubeChannelInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelInfoItemExtractor.java similarity index 100% rename from services/youtube/YoutubeChannelInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelInfoItemExtractor.java diff --git a/services/youtube/YoutubeChannelUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java similarity index 100% rename from services/youtube/YoutubeChannelUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java diff --git a/services/youtube/YoutubeParsingHelper.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java similarity index 100% rename from services/youtube/YoutubeParsingHelper.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeParsingHelper.java diff --git a/services/youtube/YoutubePlaylistExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractor.java similarity index 100% rename from services/youtube/YoutubePlaylistExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistExtractor.java diff --git a/services/youtube/YoutubePlaylistUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistUrlIdHandler.java similarity index 100% rename from services/youtube/YoutubePlaylistUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubePlaylistUrlIdHandler.java diff --git a/services/youtube/YoutubeSearchEngine.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngine.java similarity index 100% rename from services/youtube/YoutubeSearchEngine.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngine.java diff --git a/services/youtube/YoutubeService.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeService.java similarity index 100% rename from services/youtube/YoutubeService.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeService.java diff --git a/services/youtube/YoutubeStreamExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java similarity index 100% rename from services/youtube/YoutubeStreamExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java diff --git a/services/youtube/YoutubeStreamInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamInfoItemExtractor.java similarity index 100% rename from services/youtube/YoutubeStreamInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamInfoItemExtractor.java diff --git a/services/youtube/YoutubeStreamUrlIdHandler.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamUrlIdHandler.java similarity index 100% rename from services/youtube/YoutubeStreamUrlIdHandler.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamUrlIdHandler.java diff --git a/services/youtube/YoutubeSuggestionExtractor.java b/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSuggestionExtractor.java similarity index 100% rename from services/youtube/YoutubeSuggestionExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSuggestionExtractor.java diff --git a/stream/AudioStream.java b/src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java similarity index 100% rename from stream/AudioStream.java rename to src/main/java/org/schabi/newpipe/extractor/stream/AudioStream.java diff --git a/stream/Stream.java b/src/main/java/org/schabi/newpipe/extractor/stream/Stream.java similarity index 100% rename from stream/Stream.java rename to src/main/java/org/schabi/newpipe/extractor/stream/Stream.java diff --git a/stream/StreamExtractor.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java similarity index 100% rename from stream/StreamExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java diff --git a/stream/StreamInfo.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java similarity index 100% rename from stream/StreamInfo.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamInfo.java diff --git a/stream/StreamInfoItem.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java similarity index 100% rename from stream/StreamInfoItem.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItem.java diff --git a/stream/StreamInfoItemCollector.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemCollector.java similarity index 100% rename from stream/StreamInfoItemCollector.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemCollector.java diff --git a/stream/StreamInfoItemExtractor.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java similarity index 100% rename from stream/StreamInfoItemExtractor.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamInfoItemExtractor.java diff --git a/stream/StreamType.java b/src/main/java/org/schabi/newpipe/extractor/stream/StreamType.java similarity index 100% rename from stream/StreamType.java rename to src/main/java/org/schabi/newpipe/extractor/stream/StreamType.java diff --git a/stream/VideoStream.java b/src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java similarity index 100% rename from stream/VideoStream.java rename to src/main/java/org/schabi/newpipe/extractor/stream/VideoStream.java diff --git a/utils/DashMpdParser.java b/src/main/java/org/schabi/newpipe/extractor/utils/DashMpdParser.java similarity index 100% rename from utils/DashMpdParser.java rename to src/main/java/org/schabi/newpipe/extractor/utils/DashMpdParser.java diff --git a/utils/Parser.java b/src/main/java/org/schabi/newpipe/extractor/utils/Parser.java similarity index 100% rename from utils/Parser.java rename to src/main/java/org/schabi/newpipe/extractor/utils/Parser.java diff --git a/utils/Utils.java b/src/main/java/org/schabi/newpipe/extractor/utils/Utils.java similarity index 100% rename from utils/Utils.java rename to src/main/java/org/schabi/newpipe/extractor/utils/Utils.java diff --git a/src/test/java/org/schabi/newpipe/Downloader.java b/src/test/java/org/schabi/newpipe/Downloader.java new file mode 100644 index 000000000..870c17005 --- /dev/null +++ b/src/test/java/org/schabi/newpipe/Downloader.java @@ -0,0 +1,146 @@ +package org.schabi.newpipe; + +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; + + +/** + * Created by Christian Schabesberger on 28.01.16. + * + * Copyright (C) Christian Schabesberger 2016 + * Downloader.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class Downloader implements org.schabi.newpipe.extractor.Downloader { + + private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"; + private static String mCookies = ""; + + private static Downloader instance = null; + + private Downloader() {} + + public static Downloader getInstance() { + if(instance == null) { + synchronized (Downloader.class) { + if (instance == null) { + instance = new Downloader(); + } + } + } + return instance; + } + + public static synchronized void setCookies(String cookies) { + Downloader.mCookies = cookies; + } + + public static synchronized String getCookies() { + return Downloader.mCookies; + } + + /**Download the text file at the supplied URL as in download(String), + * but set the HTTP header field "Accept-Language" to the supplied string. + * @param siteUrl the URL of the text file to return the contents of + * @param language the language (usually a 2-character code) to set as the preferred language + * @return the contents of the specified text file*/ + public String download(String siteUrl, String language) throws IOException, ReCaptchaException { + Map requestProperties = new HashMap<>(); + requestProperties.put("Accept-Language", language); + return download(siteUrl, requestProperties); + } + + + /**Download the text file at the supplied URL as in download(String), + * but set the HTTP header field "Accept-Language" to the supplied string. + * @param siteUrl the URL of the text file to return the contents of + * @param customProperties set request header properties + * @return the contents of the specified text file + * @throws IOException*/ + public String download(String siteUrl, Map customProperties) throws IOException, ReCaptchaException { + URL url = new URL(siteUrl); + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + Iterator it = customProperties.entrySet().iterator(); + while(it.hasNext()) { + Map.Entry pair = (Map.Entry)it.next(); + con.setRequestProperty((String)pair.getKey(), (String)pair.getValue()); + } + return dl(con); + } + + /**Common functionality between download(String url) and download(String url, String language)*/ + private static String dl(HttpsURLConnection con) throws IOException, ReCaptchaException { + StringBuilder response = new StringBuilder(); + BufferedReader in = null; + + try { + con.setRequestMethod("GET"); + con.setRequestProperty("User-Agent", USER_AGENT); + + if (getCookies().length() > 0) { + con.setRequestProperty("Cookie", getCookies()); + } + + in = new BufferedReader( + new InputStreamReader(con.getInputStream())); + String inputLine; + + while((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + } catch(UnknownHostException uhe) {//thrown when there's no internet connection + throw new IOException("unknown host or no network", uhe); + //Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show(); + } catch(Exception e) { + /* + * HTTP 429 == Too Many Request + * Receive from Youtube.com = ReCaptcha challenge request + * See : https://github.com/rg3/youtube-dl/issues/5138 + */ + if (con.getResponseCode() == 429) { + throw new ReCaptchaException("reCaptcha Challenge requested"); + } + throw new IOException(e); + } finally { + if(in != null) { + in.close(); + } + } + + return response.toString(); + } + + /**Download (via HTTP) the text file located at the supplied URL, and return its contents. + * Primarily intended for downloading web pages. + * @param siteUrl the URL of the text file to download + * @return the contents of the specified text file*/ + public String download(String siteUrl) throws IOException, ReCaptchaException { + URL url = new URL(siteUrl); + HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); + //HttpsURLConnection con = NetCipher.getHttpsURLConnection(url); + return dl(con); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java new file mode 100644 index 000000000..5af3096e1 --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java @@ -0,0 +1,107 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.channel.ChannelExtractor; + +/** + * Created by Christian Schabesberger on 12.09.16. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeSearchEngineStreamTest.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link ChannelExtractor} + */ + +public class YoutubeChannelExtractorTest { + + ChannelExtractor extractor; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + extractor = NewPipe.getService("Youtube") + .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw"); + } + + @Test + public void testGetDownloader() throws Exception { + assertNotNull(NewPipe.getDownloader()); + } + + @Test + public void testGetChannelName() throws Exception { + assertEquals(extractor.getChannelName(), "Gronkh"); + } + + @Test + public void testGetAvatarUrl() throws Exception { + assertTrue(extractor.getAvatarUrl(), extractor.getAvatarUrl().contains("yt3")); + } + + @Test + public void testGetBannerurl() throws Exception { + assertTrue(extractor.getBannerUrl(), extractor.getBannerUrl().contains("yt3")); + } + + @Test + public void testGetFeedUrl() throws Exception { + assertTrue(extractor.getFeedUrl(), extractor.getFeedUrl().contains("feed")); + } + + @Test + public void testGetStreams() throws Exception { + assertTrue("no streams are received", !extractor.getStreams().getItemList().isEmpty()); + } + + @Test + public void testGetStreamsErrors() throws Exception { + assertTrue("errors during stream list extraction", extractor.getStreams().getErrors().isEmpty()); + } + + @Test + public void testHasNextPage() throws Exception { + // this particular example (link) has a next page !!! + assertTrue("no next page link found", extractor.hasMoreStreams()); + } + + @Test + public void testGetSubscriberCount() throws Exception { + assertTrue("wrong subscriber count", extractor.getSubscriberCount() >= 0); + } + + @Test + public void testGetNextPage() throws Exception { + extractor = NewPipe.getService("Youtube") + .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw"); + assertTrue("next page didn't have content", !extractor.getStreams().getItemList().isEmpty()); + } + + @Test + public void testGetNextNextPageUrl() throws Exception { + extractor = NewPipe.getService("Youtube") + .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw"); + assertTrue("next page didn't have content", extractor.hasMoreStreams()); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java new file mode 100644 index 000000000..4c051fa1c --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java @@ -0,0 +1,67 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import java.util.EnumSet; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.search.SearchEngine; +import org.schabi.newpipe.extractor.search.SearchResult; + + +/** + * Created by Christian Schabesberger on 29.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeSearchEngineStreamTest.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link SearchEngine} + */ +public class YoutubeSearchEngineAllTest { + private SearchResult result; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); + + result = engine.search("asdf", 0, "de", + EnumSet.of(SearchEngine.Filter.CHANNEL, + SearchEngine.Filter.STREAM)).getSearchResult(); + } + + @Test + public void testResultList() { + assertFalse(result.resultList.isEmpty()); + } + + @Test + public void testResultErrors() { + assertTrue(result.errors == null || result.errors.isEmpty()); + } + + @Test + public void testSuggestion() { + //todo write a real test + assertTrue(result.suggestion != null); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java new file mode 100644 index 000000000..68df60312 --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java @@ -0,0 +1,73 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import java.util.EnumSet; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.search.SearchEngine; +import org.schabi.newpipe.extractor.search.SearchResult; + + +/** + * Created by Christian Schabesberger on 29.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeSearchEngineStreamTest.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link SearchEngine} + */ +public class YoutubeSearchEngineChannelTest { + private SearchResult result; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); + + result = engine.search("gronkh", 0, "de", + EnumSet.of(SearchEngine.Filter.CHANNEL)).getSearchResult(); + } + + @Test + public void testResultList() { + assertFalse(result.resultList.isEmpty()); + } + + @Test + public void testChannelItemType() { + assertEquals(result.resultList.get(0).info_type, InfoItem.InfoType.CHANNEL); + } + + @Test + public void testResultErrors() { + assertTrue(result.errors == null || result.errors.isEmpty()); + } + + @Test + public void testSuggestion() { + //todo write a real test + assertTrue(result.suggestion != null); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java new file mode 100644 index 000000000..f3f2732db --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java @@ -0,0 +1,73 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import java.util.EnumSet; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.search.SearchEngine; +import org.schabi.newpipe.extractor.search.SearchResult; + + +/** + * Created by Christian Schabesberger on 29.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeSearchEngineStreamTest.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link SearchEngine} + */ +public class YoutubeSearchEngineStreamTest { + private SearchResult result; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); + + result = engine.search("this is something boring", 0, "de", + EnumSet.of(SearchEngine.Filter.STREAM)).getSearchResult(); + } + + @Test + public void testResultList() { + assertFalse(result.resultList.isEmpty()); + } + + @Test + public void testChannelItemType() { + assertEquals(result.resultList.get(0).info_type, InfoItem.InfoType.STREAM); + } + + @Test + public void testResultErrors() { + assertTrue(result.errors == null || result.errors.isEmpty()); + } + + @Test + public void testSuggestion() { + //todo write a real test + assertTrue(result.suggestion != null); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java new file mode 100644 index 000000000..1b2572917 --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java @@ -0,0 +1,52 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertFalse; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.SuggestionExtractor; +import org.schabi.newpipe.extractor.services.youtube.YoutubeSuggestionExtractor; + +/** + * Created by Christian Schabesberger on 18.11.16. + * + * Copyright (C) Christian Schabesberger 2016 + * YoutubeSearchResultTest.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link SuggestionExtractor} + */ +public class YoutubeSearchResultTest { + List suggestionReply; + + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + SuggestionExtractor engine = new YoutubeSuggestionExtractor(0); + suggestionReply = engine.suggestionList("hello", "de"); + } + + @Test + public void testIfSuggestions() { + assertFalse(suggestionReply.isEmpty()); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java new file mode 100644 index 000000000..9b5a3204a --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java @@ -0,0 +1,141 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; +import org.schabi.newpipe.extractor.stream.StreamExtractor; +import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.extractor.stream.VideoStream; + +/** + * Created by Christian Schabesberger on 30.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeVideoExtractorDefault.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +/** + * Test for {@link StreamExtractor} + */ +public class YoutubeStreamExtractorDefaultTest { + public static final String HTTPS = "https://"; + private StreamExtractor extractor; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + extractor = NewPipe.getService("Youtube") + .getStreamExtractorInstance("https://www.youtube.com/watch?v=YQHsXMglC9A"); + } + + @Test + public void testGetInvalidTimeStamp() throws ParsingException { + assertTrue(Integer.toString(extractor.getTimeStamp()), + extractor.getTimeStamp() <= 0); + } + + @Test + public void testGetValidTimeStamp() throws ExtractionException, IOException { + StreamExtractor extractor = + NewPipe.getService("Youtube") + .getStreamExtractorInstance("https://youtu.be/FmG385_uUys?t=174"); + assertTrue(Integer.toString(extractor.getTimeStamp()), + extractor.getTimeStamp() == 174); + } + + @Test + public void testGetTitle() throws ParsingException { + assertTrue(!extractor.getTitle().isEmpty()); + } + + @Test + public void testGetDescription() throws ParsingException { + assertTrue(extractor.getDescription() != null); + } + + @Test + public void testGetUploader() throws ParsingException { + assertTrue(!extractor.getUploader().isEmpty()); + } + + @Test + public void testGetLength() throws ParsingException { + assertTrue(extractor.getLength() > 0); + } + + @Test + public void testGetViewCount() throws ParsingException { + assertTrue(Long.toString(extractor.getViewCount()), + extractor.getViewCount() > /* specific to that video */ 1224000074); + } + + @Test + public void testGetUploadDate() throws ParsingException { + assertTrue(extractor.getUploadDate().length() > 0); + } + + @Test + public void testGetChannelUrl() throws ParsingException { + assertTrue(extractor.getChannelUrl().length() > 0); + } + + @Test + public void testGetThumbnailUrl() throws ParsingException { + assertTrue(extractor.getThumbnailUrl(), + extractor.getThumbnailUrl().contains(HTTPS)); + } + + @Test + public void testGetUploaderThumbnailUrl() throws ParsingException { + assertTrue(extractor.getUploaderThumbnailUrl(), + extractor.getUploaderThumbnailUrl().contains(HTTPS)); + } + + @Test + public void testGetAudioStreams() throws ParsingException, ReCaptchaException, IOException { + assertTrue(!extractor.getAudioStreams().isEmpty()); + } + + @Test + public void testGetVideoStreams() throws ParsingException { + for(VideoStream s : extractor.getVideoStreams()) { + assertTrue(s.url, + s.url.contains(HTTPS)); + assertTrue(s.resolution.length() > 0); + assertTrue(Integer.toString(s.format), + 0 <= s.format && s.format <= 4); + } + } + + @Test + public void testStreamType() throws ParsingException { + assertTrue(extractor.getStreamType() == StreamType.VIDEO_STREAM); + } + + @Test + public void testGetDashMpd() throws ParsingException { + assertTrue(extractor.getDashMpdUrl(), + extractor.getDashMpdUrl() != null || !extractor.getDashMpdUrl().isEmpty()); + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java new file mode 100644 index 000000000..dc4bb09f9 --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java @@ -0,0 +1,55 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; + +/** + * Created by Christian Schabesberger on 30.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * YoutubeVideoExtractorGema.java is part of NewPipe. + * + * NewPipe 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. + * + * NewPipe 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. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + + +// This class only works in Germany. +/** + * Test for {@link YoutubeStreamExtractor} + */ +public class YoutubeStreamExtractorGemaTest { + + // Deaktivate this Test Case bevore uploading it githup, otherwise CI will fail. + private static final boolean testActive = false; + + @Test + public void testGemaError() throws IOException, ExtractionException { + if(testActive) { + try { + NewPipe.init(Downloader.getInstance()); + NewPipe.getService("Youtube") + .getStreamExtractorInstance("https://www.youtube.com/watch?v=3O1_3zBUKM8"); + } catch(YoutubeStreamExtractor.GemaException ge) { + assertTrue(true); + } + } + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java new file mode 100644 index 000000000..d1874609e --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java @@ -0,0 +1,108 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; +import org.schabi.newpipe.extractor.stream.StreamExtractor; +import org.schabi.newpipe.extractor.stream.VideoStream; + +/** + * Test for {@link YoutubeStreamUrlIdHandler} + */ +public class YoutubeStreamExtractorRestrictedTest { + public static final String HTTPS = "https://"; + private StreamExtractor extractor; + + @Before + public void setUp() throws Exception { + NewPipe.init(Downloader.getInstance()); + extractor = NewPipe.getService("Youtube") + .getStreamExtractorInstance("https://www.youtube.com/watch?v=i6JTvzrpBy0"); + } + + @Test + public void testGetInvalidTimeStamp() throws ParsingException { + assertTrue(Integer.toString(extractor.getTimeStamp()), + extractor.getTimeStamp() <= 0); + } + + @Test + public void testGetValidTimeStamp() throws ExtractionException, IOException { + StreamExtractor extractor= NewPipe.getService("Youtube") + .getStreamExtractorInstance("https://youtu.be/FmG385_uUys?t=174"); + assertTrue(Integer.toString(extractor.getTimeStamp()), + extractor.getTimeStamp() == 174); + } + + @Test + public void testGetAgeLimit() throws ParsingException { + assertTrue(extractor.getAgeLimit() == 18); + } + + @Test + public void testGetTitle() throws ParsingException { + assertTrue(!extractor.getTitle().isEmpty()); + } + + @Test + public void testGetDescription() throws ParsingException { + assertTrue(extractor.getDescription() != null); + } + + @Test + public void testGetUploader() throws ParsingException { + assertTrue(!extractor.getUploader().isEmpty()); + } + + @Test + public void testGetLength() throws ParsingException { + assertTrue(extractor.getLength() > 0); + } + + @Test + public void testGetViews() throws ParsingException { + assertTrue(extractor.getLength() > 0); + } + + @Test + public void testGetUploadDate() throws ParsingException { + assertTrue(extractor.getUploadDate().length() > 0); + } + + @Test + public void testGetThumbnailUrl() throws ParsingException { + assertTrue(extractor.getThumbnailUrl(), + extractor.getThumbnailUrl().contains(HTTPS)); + } + + @Test + public void testGetUploaderThumbnailUrl() throws ParsingException { + assertTrue(extractor.getUploaderThumbnailUrl(), + extractor.getUploaderThumbnailUrl().contains(HTTPS)); + } + + @Test + public void testGetAudioStreams() throws ParsingException { + // audiostream not always necessary + //assertTrue(!extractor.getAudioStreams().isEmpty()); + } + + @Test + public void testGetVideoStreams() throws ParsingException { + for(VideoStream s : extractor.getVideoStreams()) { + assertTrue(s.url, + s.url.contains(HTTPS)); + assertTrue(s.resolution.length() > 0); + assertTrue(Integer.toString(s.format), + 0 <= s.format && s.format <= 4); + } + } +} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java new file mode 100644 index 000000000..e441af91f --- /dev/null +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java @@ -0,0 +1,119 @@ +package org.schabi.newpipe.extractor.services.youtube.youtube; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.exceptions.FoundAdException; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; + +/** + * Test for {@link YoutubeStreamUrlIdHandler} + */ +public class YoutubeStreamUrlIdHandlerTest { + private static String AD_URL = "https://googleads.g.doubleclick.net/aclk?sa=l&ai=C-2IPgeVTWPf4GcOStgfOnIOADf78n61GvKmmobYDrgIQASDj-5MDKAJg9ZXOgeAEoAGgy_T-A8gBAakC2gkpmquIsT6oAwGqBJMBT9BgD5kVgbN0dX602bFFaDw9vsxq-We-S8VkrXVBi6W_e7brZ36GCz1WO3EPEeklYuJjXLUowwCOKsd-8xr1UlS_tusuFJv9iX35xoBHKTRvs8-0aDbfEIm6in37QDfFuZjqgEMB8-tg0Jn_Pf1RU5OzbuU40B4Gy25NUTnOxhDKthOhKBUSZEksCEerUV8GMu10iAXCxquwApIFBggDEAEYAaAGGsgGlIjthrUDgAfItIsBqAemvhvYBwHSCAUIgGEQAbgT6AE&num=1&sig=AOD64_1DybDd4qAm5O7o9UAbTNRdqXXHFQ&ctype=21&video_id=dMO_IXYPZew&client=ca-pub-6219811747049371&adurl=http://www.youtube.com/watch%3Fv%3DdMO_IXYPZew"; + private YoutubeStreamUrlIdHandler urlIdHandler; + + @Before + public void setUp() throws Exception { + urlIdHandler = YoutubeStreamUrlIdHandler.getInstance(); + NewPipe.init(Downloader.getInstance()); + } + + @Test(expected = NullPointerException.class) + public void getIdWithNullAsUrl() throws ParsingException { + urlIdHandler.getId(null); + } + + @Test(expected = FoundAdException.class) + public void getIdForAd() throws ParsingException { + urlIdHandler.getId(AD_URL); + } + + @Test + public void getIdForInvalidUrls() throws ParsingException { + List invalidUrls = new ArrayList<>(50); + invalidUrls.add("https://www.youtube.com/watch?v=jZViOEv90d"); + invalidUrls.add("https://www.youtube.com/watchjZViOEv90d"); + invalidUrls.add("https://www.youtube.com/"); + for(String invalidUrl: invalidUrls) { + Throwable exception = null; + try { + urlIdHandler.getId(invalidUrl); + } catch (ParsingException e) { + exception = e; + } + if(exception == null) { + fail("Expected ParsingException for url: " + invalidUrl); + } + } + } + @Test + public void getId() throws Exception { + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/watch?v=jZViOEv90dI")); + assertEquals("W-fFHeTX70Q", urlIdHandler.getId("https://www.youtube.com/watch?v=W-fFHeTX70Q")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/watch?v=jZViOEv90dI?t=100")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://WWW.YouTube.com/watch?v=jZViOEv90dI?t=100")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("HTTPS://www.youtube.com/watch?v=jZViOEv90dI?t=100")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://youtu.be/jZViOEv90dI?t=9s")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("HTTPS://Youtu.be/jZViOEv90dI?t=9s")); + assertEquals("uEJuoEs1UxY", urlIdHandler.getId("http://www.youtube.com/watch_popup?v=uEJuoEs1UxY")); + assertEquals("uEJuoEs1UxY", urlIdHandler.getId("http://www.Youtube.com/watch_popup?v=uEJuoEs1UxY")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/embed/jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube-nocookie.com/embed/jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube.com/watch?v=jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://youtube.com/watch?v=jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://youtu.be/jZViOEv90dI?t=9s")); + assertEquals("7_WWz2DSnT8", urlIdHandler.getId("https://youtu.be/7_WWz2DSnT8")); + assertEquals("oy6NvWeVruY", urlIdHandler.getId("https://m.youtube.com/watch?v=oy6NvWeVruY")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube.com/embed/jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.Youtube.com/embed/jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube-nocookie.com/embed/jZViOEv90dI")); + assertEquals("EhxJLojIE_o", urlIdHandler.getId("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI")); + assertEquals("jZViOEv90dI", urlIdHandler.getId("vnd.youtube:jZViOEv90dI")); + + // Shared links + String sharedId = "7JIArTByb3E"; + String realId = "Q7JsK50NGaA"; + assertEquals(realId, urlIdHandler.getId("vnd.youtube://www.YouTube.com/shared?ci=" + sharedId + "&feature=twitter-deep-link")); + assertEquals(realId, urlIdHandler.getId("vnd.youtube://www.youtube.com/shared?ci=" + sharedId )); + assertEquals(realId, urlIdHandler.getId("https://www.youtube.com/shared?ci=7JIArTByb3E")); + + } + + + @Test + public void testAcceptUrl() { + assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch?v=jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch?v=jZViOEv90dI?t=100")); + assertTrue(urlIdHandler.acceptUrl("https://WWW.YouTube.com/watch?v=jZViOEv90dI?t=100")); + assertTrue(urlIdHandler.acceptUrl("HTTPS://www.youtube.com/watch?v=jZViOEv90dI?t=100")); + assertTrue(urlIdHandler.acceptUrl("https://youtu.be/jZViOEv90dI?t=9s")); + //assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch/jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/embed/jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("https://www.youtube-nocookie.com/embed/jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/watch?v=jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("http://youtu.be/jZViOEv90dI?t=9s")); + assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/embed/jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("http://www.youtube-nocookie.com/embed/jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare")); + assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI")); + assertTrue(urlIdHandler.acceptUrl("vnd.youtube:jZViOEv90dI")); + + assertTrue(urlIdHandler.acceptUrl("vnd.youtube:jZViOEv90dI")); + + String sharedId = "8A940MXKFmQ"; + assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/shared?ci=" + sharedId + "&feature=twitter-deep-link")); + assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/shared?ci=" + sharedId )); + assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/shared?ci=" + sharedId)); + } +} \ No newline at end of file From 171c00a891985020d787c73894731116d57e66ab Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Sat, 5 Aug 2017 11:15:36 -0300 Subject: [PATCH 2/2] Try to fix tests - Fix double youtube package --- .gitignore | 2 + .../YoutubeChannelExtractorTest.java | 2 +- .../YoutubeSearchEngineAllTest.java | 17 +-- .../YoutubeSearchEngineChannelTest.java | 19 +-- .../YoutubeSearchEngineStreamTest.java | 19 +-- .../YoutubeStreamExtractorDefaultTest.java | 2 +- .../youtube/YoutubeSearchResultTest.java | 52 -------- .../YoutubeStreamExtractorGemaTest.java | 55 -------- .../YoutubeStreamExtractorRestrictedTest.java | 108 ---------------- .../YoutubeStreamUrlIdHandlerTest.java | 119 ------------------ 10 files changed, 36 insertions(+), 359 deletions(-) rename src/test/java/org/schabi/newpipe/extractor/services/youtube/{youtube => }/YoutubeChannelExtractorTest.java (98%) rename src/test/java/org/schabi/newpipe/extractor/services/youtube/{youtube => }/YoutubeSearchEngineAllTest.java (85%) rename src/test/java/org/schabi/newpipe/extractor/services/youtube/{youtube => }/YoutubeSearchEngineChannelTest.java (86%) rename src/test/java/org/schabi/newpipe/extractor/services/youtube/{youtube => }/YoutubeSearchEngineStreamTest.java (86%) rename src/test/java/org/schabi/newpipe/extractor/services/youtube/{youtube => }/YoutubeStreamExtractorDefaultTest.java (98%) delete mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java delete mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java delete mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java delete mode 100644 src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java diff --git a/.gitignore b/.gitignore index a1fc39c07..fb9614631 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .gradle /build/ +.idea +local.properties # Ignore Gradle GUI config gradle-app.setting diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java similarity index 98% rename from src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java rename to src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java index 5af3096e1..0338bb768 100644 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeChannelExtractorTest.java +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractorTest.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; +package org.schabi.newpipe.extractor.services.youtube; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineAllTest.java similarity index 85% rename from src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java rename to src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineAllTest.java index 4c051fa1c..ca01d2295 100644 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineAllTest.java +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineAllTest.java @@ -1,9 +1,4 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import java.util.EnumSet; +package org.schabi.newpipe.extractor.services.youtube; import org.junit.Before; import org.junit.Test; @@ -12,6 +7,11 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.search.SearchEngine; import org.schabi.newpipe.extractor.search.SearchResult; +import java.util.EnumSet; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + /** * Created by Christian Schabesberger on 29.12.15. @@ -44,7 +44,9 @@ public class YoutubeSearchEngineAllTest { NewPipe.init(Downloader.getInstance()); SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); - result = engine.search("asdf", 0, "de", + // Youtube will suggest "asdf" instead of "asdgff" + // keep in mind that the suggestions can change by country (the parameter "de") + result = engine.search("asdgff", 0, "de", EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM)).getSearchResult(); } @@ -56,6 +58,7 @@ public class YoutubeSearchEngineAllTest { @Test public void testResultErrors() { + if (!result.errors.isEmpty()) for (Throwable error : result.errors) error.printStackTrace(); assertTrue(result.errors == null || result.errors.isEmpty()); } diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineChannelTest.java similarity index 86% rename from src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java rename to src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineChannelTest.java index 68df60312..df0d0bb03 100644 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineChannelTest.java +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineChannelTest.java @@ -1,10 +1,4 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import java.util.EnumSet; +package org.schabi.newpipe.extractor.services.youtube; import org.junit.Before; import org.junit.Test; @@ -14,6 +8,12 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.search.SearchEngine; import org.schabi.newpipe.extractor.search.SearchResult; +import java.util.EnumSet; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + /** * Created by Christian Schabesberger on 29.12.15. @@ -46,7 +46,9 @@ public class YoutubeSearchEngineChannelTest { NewPipe.init(Downloader.getInstance()); SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); - result = engine.search("gronkh", 0, "de", + // Youtube will suggest "gronkh" instead of "grrunkh" + // keep in mind that the suggestions can change by country (the parameter "de") + result = engine.search("grrunkh", 0, "de", EnumSet.of(SearchEngine.Filter.CHANNEL)).getSearchResult(); } @@ -62,6 +64,7 @@ public class YoutubeSearchEngineChannelTest { @Test public void testResultErrors() { + if (!result.errors.isEmpty()) for (Throwable error : result.errors) error.printStackTrace(); assertTrue(result.errors == null || result.errors.isEmpty()); } diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineStreamTest.java similarity index 86% rename from src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java rename to src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineStreamTest.java index f3f2732db..74334a3e5 100644 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchEngineStreamTest.java +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeSearchEngineStreamTest.java @@ -1,10 +1,4 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import java.util.EnumSet; +package org.schabi.newpipe.extractor.services.youtube; import org.junit.Before; import org.junit.Test; @@ -14,6 +8,12 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.search.SearchEngine; import org.schabi.newpipe.extractor.search.SearchResult; +import java.util.EnumSet; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + /** * Created by Christian Schabesberger on 29.12.15. @@ -46,7 +46,9 @@ public class YoutubeSearchEngineStreamTest { NewPipe.init(Downloader.getInstance()); SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); - result = engine.search("this is something boring", 0, "de", + // Youtube will suggest "results" instead of "rsults", + // keep in mind that the suggestions can change by country (the parameter "de") + result = engine.search("rsults", 0, "de", EnumSet.of(SearchEngine.Filter.STREAM)).getSearchResult(); } @@ -62,6 +64,7 @@ public class YoutubeSearchEngineStreamTest { @Test public void testResultErrors() { + if (!result.errors.isEmpty()) for (Throwable error : result.errors) error.printStackTrace(); assertTrue(result.errors == null || result.errors.isEmpty()); } diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java similarity index 98% rename from src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java rename to src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java index 9b5a3204a..8e2c7be78 100644 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorDefaultTest.java +++ b/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; +package org.schabi.newpipe.extractor.services.youtube; import static junit.framework.Assert.assertTrue; diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java deleted file mode 100644 index 1b2572917..000000000 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeSearchResultTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertFalse; - -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.schabi.newpipe.Downloader; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.SuggestionExtractor; -import org.schabi.newpipe.extractor.services.youtube.YoutubeSuggestionExtractor; - -/** - * Created by Christian Schabesberger on 18.11.16. - * - * Copyright (C) Christian Schabesberger 2016 - * YoutubeSearchResultTest.java is part of NewPipe. - * - * NewPipe 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. - * - * NewPipe 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. - * - * You should have received a copy of the GNU General Public License - * along with NewPipe. If not, see . - */ - -/** - * Test for {@link SuggestionExtractor} - */ -public class YoutubeSearchResultTest { - List suggestionReply; - - - @Before - public void setUp() throws Exception { - NewPipe.init(Downloader.getInstance()); - SuggestionExtractor engine = new YoutubeSuggestionExtractor(0); - suggestionReply = engine.suggestionList("hello", "de"); - } - - @Test - public void testIfSuggestions() { - assertFalse(suggestionReply.isEmpty()); - } -} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java deleted file mode 100644 index dc4bb09f9..000000000 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorGemaTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertTrue; - -import java.io.IOException; - -import org.junit.Test; -import org.schabi.newpipe.Downloader; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; - -/** - * Created by Christian Schabesberger on 30.12.15. - * - * Copyright (C) Christian Schabesberger 2015 - * YoutubeVideoExtractorGema.java is part of NewPipe. - * - * NewPipe 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. - * - * NewPipe 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. - * - * You should have received a copy of the GNU General Public License - * along with NewPipe. If not, see . - */ - - -// This class only works in Germany. -/** - * Test for {@link YoutubeStreamExtractor} - */ -public class YoutubeStreamExtractorGemaTest { - - // Deaktivate this Test Case bevore uploading it githup, otherwise CI will fail. - private static final boolean testActive = false; - - @Test - public void testGemaError() throws IOException, ExtractionException { - if(testActive) { - try { - NewPipe.init(Downloader.getInstance()); - NewPipe.getService("Youtube") - .getStreamExtractorInstance("https://www.youtube.com/watch?v=3O1_3zBUKM8"); - } catch(YoutubeStreamExtractor.GemaException ge) { - assertTrue(true); - } - } - } -} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java deleted file mode 100644 index d1874609e..000000000 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamExtractorRestrictedTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertTrue; - -import java.io.IOException; - -import org.junit.Before; -import org.junit.Test; -import org.schabi.newpipe.Downloader; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.exceptions.ParsingException; -import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; -import org.schabi.newpipe.extractor.stream.StreamExtractor; -import org.schabi.newpipe.extractor.stream.VideoStream; - -/** - * Test for {@link YoutubeStreamUrlIdHandler} - */ -public class YoutubeStreamExtractorRestrictedTest { - public static final String HTTPS = "https://"; - private StreamExtractor extractor; - - @Before - public void setUp() throws Exception { - NewPipe.init(Downloader.getInstance()); - extractor = NewPipe.getService("Youtube") - .getStreamExtractorInstance("https://www.youtube.com/watch?v=i6JTvzrpBy0"); - } - - @Test - public void testGetInvalidTimeStamp() throws ParsingException { - assertTrue(Integer.toString(extractor.getTimeStamp()), - extractor.getTimeStamp() <= 0); - } - - @Test - public void testGetValidTimeStamp() throws ExtractionException, IOException { - StreamExtractor extractor= NewPipe.getService("Youtube") - .getStreamExtractorInstance("https://youtu.be/FmG385_uUys?t=174"); - assertTrue(Integer.toString(extractor.getTimeStamp()), - extractor.getTimeStamp() == 174); - } - - @Test - public void testGetAgeLimit() throws ParsingException { - assertTrue(extractor.getAgeLimit() == 18); - } - - @Test - public void testGetTitle() throws ParsingException { - assertTrue(!extractor.getTitle().isEmpty()); - } - - @Test - public void testGetDescription() throws ParsingException { - assertTrue(extractor.getDescription() != null); - } - - @Test - public void testGetUploader() throws ParsingException { - assertTrue(!extractor.getUploader().isEmpty()); - } - - @Test - public void testGetLength() throws ParsingException { - assertTrue(extractor.getLength() > 0); - } - - @Test - public void testGetViews() throws ParsingException { - assertTrue(extractor.getLength() > 0); - } - - @Test - public void testGetUploadDate() throws ParsingException { - assertTrue(extractor.getUploadDate().length() > 0); - } - - @Test - public void testGetThumbnailUrl() throws ParsingException { - assertTrue(extractor.getThumbnailUrl(), - extractor.getThumbnailUrl().contains(HTTPS)); - } - - @Test - public void testGetUploaderThumbnailUrl() throws ParsingException { - assertTrue(extractor.getUploaderThumbnailUrl(), - extractor.getUploaderThumbnailUrl().contains(HTTPS)); - } - - @Test - public void testGetAudioStreams() throws ParsingException { - // audiostream not always necessary - //assertTrue(!extractor.getAudioStreams().isEmpty()); - } - - @Test - public void testGetVideoStreams() throws ParsingException { - for(VideoStream s : extractor.getVideoStreams()) { - assertTrue(s.url, - s.url.contains(HTTPS)); - assertTrue(s.resolution.length() > 0); - assertTrue(Integer.toString(s.format), - 0 <= s.format && s.format <= 4); - } - } -} diff --git a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java b/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java deleted file mode 100644 index e441af91f..000000000 --- a/src/test/java/org/schabi/newpipe/extractor/services/youtube/youtube/YoutubeStreamUrlIdHandlerTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.schabi.newpipe.extractor.services.youtube.youtube; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.fail; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.schabi.newpipe.Downloader; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.exceptions.FoundAdException; -import org.schabi.newpipe.extractor.exceptions.ParsingException; -import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; - -/** - * Test for {@link YoutubeStreamUrlIdHandler} - */ -public class YoutubeStreamUrlIdHandlerTest { - private static String AD_URL = "https://googleads.g.doubleclick.net/aclk?sa=l&ai=C-2IPgeVTWPf4GcOStgfOnIOADf78n61GvKmmobYDrgIQASDj-5MDKAJg9ZXOgeAEoAGgy_T-A8gBAakC2gkpmquIsT6oAwGqBJMBT9BgD5kVgbN0dX602bFFaDw9vsxq-We-S8VkrXVBi6W_e7brZ36GCz1WO3EPEeklYuJjXLUowwCOKsd-8xr1UlS_tusuFJv9iX35xoBHKTRvs8-0aDbfEIm6in37QDfFuZjqgEMB8-tg0Jn_Pf1RU5OzbuU40B4Gy25NUTnOxhDKthOhKBUSZEksCEerUV8GMu10iAXCxquwApIFBggDEAEYAaAGGsgGlIjthrUDgAfItIsBqAemvhvYBwHSCAUIgGEQAbgT6AE&num=1&sig=AOD64_1DybDd4qAm5O7o9UAbTNRdqXXHFQ&ctype=21&video_id=dMO_IXYPZew&client=ca-pub-6219811747049371&adurl=http://www.youtube.com/watch%3Fv%3DdMO_IXYPZew"; - private YoutubeStreamUrlIdHandler urlIdHandler; - - @Before - public void setUp() throws Exception { - urlIdHandler = YoutubeStreamUrlIdHandler.getInstance(); - NewPipe.init(Downloader.getInstance()); - } - - @Test(expected = NullPointerException.class) - public void getIdWithNullAsUrl() throws ParsingException { - urlIdHandler.getId(null); - } - - @Test(expected = FoundAdException.class) - public void getIdForAd() throws ParsingException { - urlIdHandler.getId(AD_URL); - } - - @Test - public void getIdForInvalidUrls() throws ParsingException { - List invalidUrls = new ArrayList<>(50); - invalidUrls.add("https://www.youtube.com/watch?v=jZViOEv90d"); - invalidUrls.add("https://www.youtube.com/watchjZViOEv90d"); - invalidUrls.add("https://www.youtube.com/"); - for(String invalidUrl: invalidUrls) { - Throwable exception = null; - try { - urlIdHandler.getId(invalidUrl); - } catch (ParsingException e) { - exception = e; - } - if(exception == null) { - fail("Expected ParsingException for url: " + invalidUrl); - } - } - } - @Test - public void getId() throws Exception { - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/watch?v=jZViOEv90dI")); - assertEquals("W-fFHeTX70Q", urlIdHandler.getId("https://www.youtube.com/watch?v=W-fFHeTX70Q")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/watch?v=jZViOEv90dI?t=100")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://WWW.YouTube.com/watch?v=jZViOEv90dI?t=100")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("HTTPS://www.youtube.com/watch?v=jZViOEv90dI?t=100")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://youtu.be/jZViOEv90dI?t=9s")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("HTTPS://Youtu.be/jZViOEv90dI?t=9s")); - assertEquals("uEJuoEs1UxY", urlIdHandler.getId("http://www.youtube.com/watch_popup?v=uEJuoEs1UxY")); - assertEquals("uEJuoEs1UxY", urlIdHandler.getId("http://www.Youtube.com/watch_popup?v=uEJuoEs1UxY")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube.com/embed/jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("https://www.youtube-nocookie.com/embed/jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube.com/watch?v=jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://youtube.com/watch?v=jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://youtu.be/jZViOEv90dI?t=9s")); - assertEquals("7_WWz2DSnT8", urlIdHandler.getId("https://youtu.be/7_WWz2DSnT8")); - assertEquals("oy6NvWeVruY", urlIdHandler.getId("https://m.youtube.com/watch?v=oy6NvWeVruY")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube.com/embed/jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.Youtube.com/embed/jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("http://www.youtube-nocookie.com/embed/jZViOEv90dI")); - assertEquals("EhxJLojIE_o", urlIdHandler.getId("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI")); - assertEquals("jZViOEv90dI", urlIdHandler.getId("vnd.youtube:jZViOEv90dI")); - - // Shared links - String sharedId = "7JIArTByb3E"; - String realId = "Q7JsK50NGaA"; - assertEquals(realId, urlIdHandler.getId("vnd.youtube://www.YouTube.com/shared?ci=" + sharedId + "&feature=twitter-deep-link")); - assertEquals(realId, urlIdHandler.getId("vnd.youtube://www.youtube.com/shared?ci=" + sharedId )); - assertEquals(realId, urlIdHandler.getId("https://www.youtube.com/shared?ci=7JIArTByb3E")); - - } - - - @Test - public void testAcceptUrl() { - assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch?v=jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch?v=jZViOEv90dI?t=100")); - assertTrue(urlIdHandler.acceptUrl("https://WWW.YouTube.com/watch?v=jZViOEv90dI?t=100")); - assertTrue(urlIdHandler.acceptUrl("HTTPS://www.youtube.com/watch?v=jZViOEv90dI?t=100")); - assertTrue(urlIdHandler.acceptUrl("https://youtu.be/jZViOEv90dI?t=9s")); - //assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/watch/jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/embed/jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("https://www.youtube-nocookie.com/embed/jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/watch?v=jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("http://youtu.be/jZViOEv90dI?t=9s")); - assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/embed/jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("http://www.youtube-nocookie.com/embed/jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare")); - assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI")); - assertTrue(urlIdHandler.acceptUrl("vnd.youtube:jZViOEv90dI")); - - assertTrue(urlIdHandler.acceptUrl("vnd.youtube:jZViOEv90dI")); - - String sharedId = "8A940MXKFmQ"; - assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/shared?ci=" + sharedId + "&feature=twitter-deep-link")); - assertTrue(urlIdHandler.acceptUrl("vnd.youtube://www.youtube.com/shared?ci=" + sharedId )); - assertTrue(urlIdHandler.acceptUrl("https://www.youtube.com/shared?ci=" + sharedId)); - } -} \ No newline at end of file