Compare commits
137 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f9ad0f12d0 | ||
![]() |
63b16d925d | ||
![]() |
53b9ffcbc8 | ||
![]() |
842079c928 | ||
![]() |
b0bab07a15 | ||
![]() |
4dbb12c65d | ||
![]() |
db500e9791 | ||
![]() |
fd8f600fec | ||
![]() |
2e2d7d02fb | ||
![]() |
d068cd7f75 | ||
![]() |
5a05ffcbdd | ||
![]() |
d8c7f50b39 | ||
![]() |
988e6e1c82 | ||
![]() |
f6af19444c | ||
![]() |
0581e50e0c | ||
![]() |
a95da9a42d | ||
![]() |
be10b9750f | ||
![]() |
29a3cbc688 | ||
![]() |
4f57d3a201 | ||
![]() |
c9be1398b0 | ||
![]() |
30eef4db12 | ||
![]() |
b932dbf514 | ||
![]() |
320ac82dea | ||
![]() |
af1b21db23 | ||
![]() |
0e892ff60e | ||
![]() |
074963aee0 | ||
![]() |
37d9be9095 | ||
![]() |
78b95f67eb | ||
![]() |
6c63841d0c | ||
![]() |
6ec2d91d91 | ||
![]() |
3299b90c20 | ||
![]() |
7b6d6da9a6 | ||
![]() |
7c7c61fc35 | ||
![]() |
c5408fb6b8 | ||
![]() |
1c49102f67 | ||
![]() |
f9dd88c1cb | ||
![]() |
9ed4a65fd2 | ||
![]() |
10bebf8a89 | ||
![]() |
36260dac18 | ||
![]() |
2e4e993967 | ||
![]() |
ef1bfe98f7 | ||
![]() |
2e123aa617 | ||
![]() |
cf2ef0f2a8 | ||
![]() |
520f40d862 | ||
![]() |
856bdac84b | ||
![]() |
deb7e38fcf | ||
![]() |
e5f47a4563 | ||
![]() |
d1b465d8be | ||
![]() |
5c73b2f324 | ||
![]() |
7d3e992b3f | ||
![]() |
296640930e | ||
![]() |
1c42735e3a | ||
![]() |
0800bc1790 | ||
![]() |
bd76a12b90 | ||
![]() |
d3daea6383 | ||
![]() |
df1acd5413 | ||
![]() |
0d65cc09f6 | ||
![]() |
892b082b9e | ||
![]() |
9069ef1325 | ||
![]() |
47cc493ab5 | ||
![]() |
f43d7837f8 | ||
![]() |
91921ae672 | ||
![]() |
eba6afc12b | ||
![]() |
ff826a9eeb | ||
![]() |
6c01a30af5 | ||
![]() |
53f8d09d31 | ||
![]() |
ae191aaafe | ||
![]() |
d1694d563b | ||
![]() |
75fadf79da | ||
![]() |
23ce5a6b1e | ||
![]() |
bb65e2b84d | ||
![]() |
aa42b1d95a | ||
![]() |
32d5a18198 | ||
![]() |
63faefe9c3 | ||
![]() |
a90c49d030 | ||
![]() |
b1ef3fa4df | ||
![]() |
b8cf67cba9 | ||
![]() |
4780e10ade | ||
![]() |
401e606fbc | ||
![]() |
6b4ef8f397 | ||
![]() |
7c66d07779 | ||
![]() |
92aed0cc3a | ||
![]() |
457b08d3cc | ||
![]() |
1e5f6fd2b8 | ||
![]() |
f6974e8315 | ||
![]() |
2ce6313ac1 | ||
![]() |
e98a113a59 | ||
![]() |
ba7bed9c2c | ||
![]() |
c9ea451c53 | ||
![]() |
4261ff32c7 | ||
![]() |
cb4b20af45 | ||
![]() |
b8a27adb93 | ||
![]() |
15b58128f4 | ||
![]() |
237282db28 | ||
![]() |
71bb59dbb8 | ||
![]() |
e41c46c075 | ||
![]() |
6ca9e52f2f | ||
![]() |
2afee89de3 | ||
![]() |
189bee3e44 | ||
![]() |
6b9a4d5e0a | ||
![]() |
451e2b2182 | ||
![]() |
f6ff41cfb4 | ||
![]() |
d6d144c927 | ||
![]() |
7f86872139 | ||
![]() |
5d28b2400f | ||
![]() |
67324bfc80 | ||
![]() |
3c340e7144 | ||
![]() |
c834405a92 | ||
![]() |
4ee9e26847 | ||
![]() |
94293ca9d9 | ||
![]() |
b9cd9f8d35 | ||
![]() |
812dd9282d | ||
![]() |
fa1d386fcc | ||
![]() |
0392bf6a02 | ||
![]() |
97f771ff50 | ||
![]() |
6adcc72a8a | ||
![]() |
2c11bd1889 | ||
![]() |
23e0196fcc | ||
![]() |
91f98c125e | ||
![]() |
7f01e9a4d9 | ||
![]() |
320a4e2351 | ||
![]() |
0829ce51fc | ||
![]() |
97ec50c202 | ||
![]() |
975a3e8103 | ||
![]() |
658ef2ef26 | ||
![]() |
bd1e531d7b | ||
![]() |
e440d1d1bd | ||
![]() |
2110020165 | ||
![]() |
dac74dc30d | ||
![]() |
fa4b971254 | ||
![]() |
2d31ae0baa | ||
![]() |
5eebfa132f | ||
![]() |
522febef93 | ||
![]() |
a421645ea5 | ||
![]() |
0aac4b1347 | ||
![]() |
36697825cf | ||
![]() |
b0182ed604 |
1
.gitignore
vendored
@@ -6,3 +6,4 @@
|
|||||||
/captures
|
/captures
|
||||||
/app/app.iml
|
/app/app.iml
|
||||||
/.idea
|
/.idea
|
||||||
|
/*.iml
|
||||||
|
29
.travis.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
language: android
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
# The BuildTools version used by NewPipe
|
||||||
|
- build-tools-23.0.2
|
||||||
|
|
||||||
|
# The SDK version used to compile NewPipe
|
||||||
|
- android-23
|
||||||
|
|
||||||
|
# Additional components
|
||||||
|
- extra-android-support
|
||||||
|
- extra-android-m2repository
|
||||||
|
|
||||||
|
# Emulators
|
||||||
|
- sys-img-armeabi-v7a-android-21
|
||||||
|
- sys-img-armeabi-v7a-android-19
|
||||||
|
- sys-img-armeabi-v7a-android-15
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- ADB_INSTALL_TIMEOUT=8 # minutes (2 by default)
|
||||||
|
matrix:
|
||||||
|
- ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
|
||||||
|
- emulator -avd test -no-skin -no-audio -no-window &
|
||||||
|
- android-wait-for-emulator
|
||||||
|
- adb shell input keyevent 82 &
|
51
README.md
@@ -1,30 +1,43 @@
|
|||||||
# NewPipe
|
# NewPipe
|
||||||
|
NewPipe: A free lightweight Youtube frontend for Android.
|
||||||
|
|
||||||
|
[](http://dasochan.nl/newpipe/)
|
||||||
|
|
||||||
|
Project status:
|
||||||
[](https://hosted.weblate.org/engage/NewPipe/)
|
[](https://hosted.weblate.org/engage/NewPipe/)
|
||||||
|
[](https://travis-ci.org/theScrabi/NewPipe)
|
||||||
|
|
||||||
[](http://dasochan.nl/newpipe/)
|
## Get NewPipe
|
||||||
NewPipe: A free lightweight Youtube frontend for Android.
|
|
||||||
|
|
||||||
[](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
|
[](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
[<img src="screenshots/screenshot_1.png" width=150>](screenshots/screenshot_1.png)
|
||||||
|
[<img src="screenshots/screenshot_2.png" width=150>](screenshots/screenshot_2.png)
|
||||||
|
[<img src="screenshots/screenshot_3.png" width=150>](screenshots/screenshot_3.png)
|
||||||
|
[<img src="screenshots/screenshot_4.png" width=150>](screenshots/screenshot_4.png)
|
||||||
|
[<img src="screenshots/screenshot_5.png" width=150>](screenshots/screenshot_5.png)
|
||||||
|
[<img src="screenshots/screenshot_6.png" width=250>](screenshots/screenshot_6.png)
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without G-services installed. Also NewPipe does not store data on the YouTube website (no login), and it's free software.
|
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without Google Services installed. Also, you don't need a YouTube account to use NewPipe, and it's FLOSS.
|
||||||
|
|
||||||
## Features
|
### Features
|
||||||
|
|
||||||
* Search videos
|
* Search videos
|
||||||
* Display general information about a video
|
* Display general information about a video
|
||||||
* Watch Youtube videos
|
* Watch YouTube videos
|
||||||
* Listen to Youtube videos (audio only streaming)
|
* Listen to YouTube videos (audio only streaming)
|
||||||
* Select the streaming player to watch the video with
|
* Select the streaming player to watch the video with
|
||||||
* Download videos (working, but it could be better)
|
* Download videos (working, but it could be better)
|
||||||
* Download audio only (working but, but it could be better)
|
* Download audio only (working, but it could be better)
|
||||||
* Open a video in Kodi
|
* Open a video in Kodi
|
||||||
* Show Next/Related videos
|
* Show Next/Related videos
|
||||||
* Search Youtube in a specific language
|
* Search YouTube in a specific language
|
||||||
|
|
||||||
## Coming Features
|
### Coming Features
|
||||||
|
|
||||||
* Improved Downloading
|
* Improved Downloading
|
||||||
* Bookmarks
|
* Bookmarks
|
||||||
@@ -37,10 +50,20 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
### Multi service support
|
### Multiservice support
|
||||||
Generally NewPipe is designed to not only support YouTube, but many more streaming services. However, right now NewPipe is not stable enough to support more than only YouTube. But if all works as planned, NewPipe will get such support by the version 2.0.
|
Although NewPipe only supports YouTube at the moment, it's designed to support many more streaming services. The plan is, that NewPipe will get such support by the version 2.0.
|
||||||
|
|
||||||
# Help is always welcome !!!
|
|
||||||
Whether it's about ideas, translation, design changes, code cleaning, or real heavy code changes. Help is always welcome.
|
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
Whether you have ideas, translation, design changes, code cleaning, or real heavy code changes, help is always welcome.
|
||||||
The more is done the better it gets!
|
The more is done the better it gets!
|
||||||
|
|
||||||
|
Join our [Slack group](http://invite.chschtsch.ml/) if you like to get involved.
|
||||||
|
|
||||||
|
## License
|
||||||
|
[](http://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|
||||||
|
NewPipe is Free Software: You can use, study share and improve it at your
|
||||||
|
will. Specifically you can redistribute and/or modify it under the terms of the
|
||||||
|
[GNU General Public License](https://www.gnu.org/licenses/gpl.html) as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 23
|
||||||
buildToolsVersion "23.0.1"
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 6
|
versionCode 8
|
||||||
versionName "0.6.0"
|
versionName "0.6.2"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -17,13 +17,22 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
checkReleaseBuilds false
|
||||||
|
// Or, if you prefer, you can continue to check for errors in release builds,
|
||||||
|
// but continue the build even when errors are found:
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
compile 'com.android.support:appcompat-v7:23.1.0'
|
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||||
compile 'com.android.support:support-v4:23.1.0'
|
compile 'com.android.support:support-v4:23.1.1'
|
||||||
compile 'com.android.support:design:23.1.0'
|
compile 'com.android.support:design:23.1.1'
|
||||||
|
compile 'com.android.support:cardview-v7:23.1.1'
|
||||||
|
compile 'com.android.support:recyclerview-v7:23.1.1'
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
compile 'org.jsoup:jsoup:1.8.3'
|
||||||
compile 'org.mozilla:rhino:1.7.7'
|
compile 'org.mozilla:rhino:1.7.7'
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="org.schabi.newpipe" >
|
package="org.schabi.newpipe" >
|
||||||
|
|
||||||
<uses-permission android:name= "android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<application
|
<application
|
||||||
@@ -10,10 +11,11 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:logo="@mipmap/ic_launcher"
|
android:logo="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme" >
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="AllowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name=".VideoItemListActivity"
|
android:name=".VideoItemListActivity"
|
||||||
android:label="@string/app_name" >
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
@@ -22,7 +24,8 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".VideoItemDetailActivity"
|
android:name=".VideoItemDetailActivity"
|
||||||
android:label="@string/title_videoitem_detail" >
|
android:label="@string/title_videoitem_detail"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".VideoItemListActivity" />
|
android:value=".VideoItemListActivity" />
|
||||||
@@ -67,9 +70,9 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".PlayVideoActivity"
|
<activity android:name=".PlayVideoActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
android:theme="@style/FullscreenTheme"
|
android:theme="@style/VideoPlayerTheme"
|
||||||
android:parentActivityName=".VideoItemDetailActivity"
|
android:parentActivityName=".VideoItemDetailActivity"
|
||||||
>
|
tools:ignore="UnusedAttribute">
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
|
@@ -6,7 +6,6 @@ import android.content.SharedPreferences;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
@@ -36,7 +35,8 @@ import android.widget.ArrayAdapter;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class ActionBarHandler {
|
|
||||||
|
class ActionBarHandler {
|
||||||
private static final String TAG = ActionBarHandler.class.toString();
|
private static final String TAG = ActionBarHandler.class.toString();
|
||||||
private static final String KORE_PACKET = "org.xbmc.kore";
|
private static final String KORE_PACKET = "org.xbmc.kore";
|
||||||
|
|
||||||
@@ -47,9 +47,11 @@ public class ActionBarHandler {
|
|||||||
private int selectedStream = -1;
|
private int selectedStream = -1;
|
||||||
private String videoTitle = "";
|
private String videoTitle = "";
|
||||||
|
|
||||||
SharedPreferences defaultPreferences = null;
|
private SharedPreferences defaultPreferences = null;
|
||||||
|
private int startPosition;
|
||||||
|
|
||||||
class FormatItemSelectListener implements ActionBar.OnNavigationListener {
|
@SuppressWarnings("deprecation")
|
||||||
|
private class FormatItemSelectListener implements ActionBar.OnNavigationListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
||||||
selectFormatItem((int)itemId);
|
selectFormatItem((int)itemId);
|
||||||
@@ -61,11 +63,17 @@ public class ActionBarHandler {
|
|||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"deprecation", "ConstantConditions"})
|
||||||
public void setupNavMenu(AppCompatActivity activity) {
|
public void setupNavMenu(AppCompatActivity activity) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
try {
|
||||||
|
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||||
|
} catch(NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
|
public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
|
||||||
this.videoStreams = videoStreams;
|
this.videoStreams = videoStreams;
|
||||||
selectedStream = 0;
|
selectedStream = 0;
|
||||||
@@ -83,12 +91,14 @@ public class ActionBarHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<String>(activity.getBaseContext(),
|
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
||||||
if(activity != null) {
|
if(activity != null) {
|
||||||
ActionBar ab = activity.getSupportActionBar();
|
ActionBar ab = activity.getSupportActionBar();
|
||||||
ab.setListNavigationCallbacks(itemAdapter
|
assert ab != null : "Could not get actionbar";
|
||||||
,new FormatItemSelectListener());
|
ab.setListNavigationCallbacks(itemAdapter
|
||||||
|
, new FormatItemSelectListener());
|
||||||
|
|
||||||
ab.setSelectedNavigationItem(defaultResolutionPos);
|
ab.setSelectedNavigationItem(defaultResolutionPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +126,7 @@ public class ActionBarHandler {
|
|||||||
selectedStream = i;
|
selectedStream = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setupMenu(Menu menu, MenuInflater inflater) {
|
public void setupMenu(Menu menu, MenuInflater inflater) {
|
||||||
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
||||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
||||||
|
|
||||||
@@ -127,8 +137,6 @@ public class ActionBarHandler {
|
|||||||
|
|
||||||
castItem.setVisible(defaultPreferences
|
castItem.setVisible(defaultPreferences
|
||||||
.getBoolean(activity.getString(R.string.showPlayWidthKodiPreference), false));
|
.getBoolean(activity.getString(R.string.showPlayWidthKodiPreference), false));
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onItemSelected(MenuItem item) {
|
public boolean onItemSelected(MenuItem item) {
|
||||||
@@ -216,13 +224,19 @@ public class ActionBarHandler {
|
|||||||
intent.putExtra(PlayVideoActivity.VIDEO_TITLE, videoTitle);
|
intent.putExtra(PlayVideoActivity.VIDEO_TITLE, videoTitle);
|
||||||
intent.putExtra(PlayVideoActivity.STREAM_URL, videoStreams[selectedStream].url);
|
intent.putExtra(PlayVideoActivity.STREAM_URL, videoStreams[selectedStream].url);
|
||||||
intent.putExtra(PlayVideoActivity.VIDEO_URL, websiteUrl);
|
intent.putExtra(PlayVideoActivity.VIDEO_URL, websiteUrl);
|
||||||
activity.startActivity(intent);
|
intent.putExtra(PlayVideoActivity.START_POSITION, startPosition);
|
||||||
|
activity.startActivity(intent); //also HERE !!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// --------------------------------------------
|
// --------------------------------------------
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadVideo() {
|
public void setStartPosition(int startPositionSeconds)
|
||||||
|
{
|
||||||
|
this.startPosition = startPositionSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadVideo() {
|
||||||
if(!videoTitle.isEmpty()) {
|
if(!videoTitle.isEmpty()) {
|
||||||
String videoSuffix = "." + MediaFormat.getSuffixById(videoStreams[selectedStream].format);
|
String videoSuffix = "." + MediaFormat.getSuffixById(videoStreams[selectedStream].format);
|
||||||
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
||||||
@@ -238,7 +252,7 @@ public class ActionBarHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void openInBrowser() {
|
private void openInBrowser() {
|
||||||
if(!videoTitle.isEmpty()) {
|
if(!videoTitle.isEmpty()) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
@@ -248,7 +262,7 @@ public class ActionBarHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playWithKodi() {
|
private void playWithKodi() {
|
||||||
if(!videoTitle.isEmpty()) {
|
if(!videoTitle.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
@@ -279,7 +293,7 @@ public class ActionBarHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playAudio() {
|
private void playAudio() {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
try {
|
try {
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
@@ -8,6 +8,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -42,8 +43,9 @@ public class DownloadDialog extends DialogFragment {
|
|||||||
public static final String FILE_SUFFIX_VIDEO = "file_suffix_video";
|
public static final String FILE_SUFFIX_VIDEO = "file_suffix_video";
|
||||||
public static final String AUDIO_URL = "audio_url";
|
public static final String AUDIO_URL = "audio_url";
|
||||||
public static final String VIDEO_URL = "video_url";
|
public static final String VIDEO_URL = "video_url";
|
||||||
Bundle arguments;
|
private Bundle arguments;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
arguments = getArguments();
|
arguments = getArguments();
|
||||||
|
@@ -31,6 +31,11 @@ public class Downloader {
|
|||||||
|
|
||||||
private static final String USER_AGENT = "Mozilla/5.0";
|
private static final String USER_AGENT = "Mozilla/5.0";
|
||||||
|
|
||||||
|
/**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 static String download(String siteUrl, String language) {
|
public static String download(String siteUrl, String language) {
|
||||||
String ret = "";
|
String ret = "";
|
||||||
try {
|
try {
|
||||||
@@ -44,9 +49,9 @@ public class Downloader {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/**Common functionality between download(String url) and download(String url, String language)*/
|
||||||
private static String dl(HttpURLConnection con) {
|
private static String dl(HttpURLConnection con) throws IOException {
|
||||||
StringBuffer response = new StringBuffer();
|
StringBuilder response = new StringBuilder();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
con.setRequestMethod("GET");
|
con.setRequestMethod("GET");
|
||||||
@@ -66,13 +71,14 @@ public class Downloader {
|
|||||||
uhe.printStackTrace();
|
uhe.printStackTrace();
|
||||||
//Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show();
|
//Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return response.toString();
|
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 static String download(String siteUrl) {
|
public static String download(String siteUrl) {
|
||||||
String ret = "";
|
String ret = "";
|
||||||
|
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 10.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* Extractor.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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public interface Extractor {
|
|
||||||
VideoInfo getVideoInfo(String siteUrl);
|
|
||||||
String getVideoUrl(String videoId);
|
|
||||||
String getVideoId(String videoUrl);
|
|
||||||
}
|
|
@@ -21,6 +21,9 @@ package org.schabi.newpipe;
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**Static data about various media formats support by Newpipe, eg mime type, extension*/
|
||||||
|
|
||||||
public enum MediaFormat {
|
public enum MediaFormat {
|
||||||
// id name suffix mime type
|
// id name suffix mime type
|
||||||
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
|
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
|
||||||
@@ -30,7 +33,9 @@ public enum MediaFormat {
|
|||||||
WEBMA (0x4, "WebM", "webm", "audio/webm");
|
WEBMA (0x4, "WebM", "webm", "audio/webm");
|
||||||
|
|
||||||
public final int id;
|
public final int id;
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public final String name;
|
public final String name;
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public final String suffix;
|
public final String suffix;
|
||||||
public final String mimeType;
|
public final String mimeType;
|
||||||
|
|
||||||
@@ -41,6 +46,10 @@ public enum MediaFormat {
|
|||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**Return the friendly name of the media format with the supplied id
|
||||||
|
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
||||||
|
* @return the friendly name of the MediaFormat associated with this ids,
|
||||||
|
* or an empty String if none match it.*/
|
||||||
public static String getNameById(int ident) {
|
public static String getNameById(int ident) {
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
for (MediaFormat vf : MediaFormat.values()) {
|
||||||
if(vf.id == ident) return vf.name;
|
if(vf.id == ident) return vf.name;
|
||||||
@@ -48,6 +57,10 @@ public enum MediaFormat {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**Return the file extension of the media format with the supplied id
|
||||||
|
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
||||||
|
* @return the file extension of the MediaFormat associated with this ids,
|
||||||
|
* or an empty String if none match it.*/
|
||||||
public static String getSuffixById(int ident) {
|
public static String getSuffixById(int ident) {
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
for (MediaFormat vf : MediaFormat.values()) {
|
||||||
if(vf.id == ident) return vf.suffix;
|
if(vf.id == ident) return vf.suffix;
|
||||||
@@ -55,6 +68,10 @@ public enum MediaFormat {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**Return the MIME type of the media format with the supplied id
|
||||||
|
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
||||||
|
* @return the MIME type of the MediaFormat associated with this ids,
|
||||||
|
* or an empty String if none match it.*/
|
||||||
public static String getMimeById(int ident) {
|
public static String getMimeById(int ident) {
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
for (MediaFormat vf : MediaFormat.values()) {
|
||||||
if(vf.id == ident) return vf.mimeType;
|
if(vf.id == ident) return vf.mimeType;
|
||||||
|
@@ -15,6 +15,7 @@ import android.support.v7.app.AppCompatActivity;
|
|||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -52,9 +53,9 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
public static final String STREAM_URL = "stream_url";
|
public static final String STREAM_URL = "stream_url";
|
||||||
public static final String VIDEO_TITLE = "video_title";
|
public static final String VIDEO_TITLE = "video_title";
|
||||||
private static final String POSITION = "position";
|
private static final String POSITION = "position";
|
||||||
|
public static final String START_POSITION = "start_position";
|
||||||
|
|
||||||
private static final long HIDING_DELAY = 3000;
|
private static final long HIDING_DELAY = 3000;
|
||||||
private static final long TAB_HIDING_DELAY = 100;
|
|
||||||
|
|
||||||
private String videoUrl = "";
|
private String videoUrl = "";
|
||||||
|
|
||||||
@@ -82,12 +83,38 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
hasSoftKeys = checkIfHasSoftKeys();
|
hasSoftKeys = checkIfHasSoftKeys();
|
||||||
|
|
||||||
actionBar = getSupportActionBar();
|
actionBar = getSupportActionBar();
|
||||||
|
assert actionBar != null;
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
if(mediaController == null) {
|
if(mediaController == null) {
|
||||||
mediaController = new MediaController(this);
|
//prevents back button hiding media controller controls (after showing them)
|
||||||
|
//instead of exiting video
|
||||||
|
//see http://stackoverflow.com/questions/6051825
|
||||||
|
//also solves https://github.com/theScrabi/NewPipe/issues/99
|
||||||
|
mediaController = new MediaController(this) {
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
|
int keyCode = event.getKeyCode();
|
||||||
|
final boolean uniqueDown = event.getRepeatCount() == 0
|
||||||
|
&& event.getAction() == KeyEvent.ACTION_DOWN;
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
if (uniqueDown)
|
||||||
|
{
|
||||||
|
if (isShowing()) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.dispatchKeyEvent(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position = intent.getIntExtra(START_POSITION, 0)*1000;//convert from seconds to milliseconds
|
||||||
|
|
||||||
videoView = (VideoView) findViewById(R.id.video_view);
|
videoView = (VideoView) findViewById(R.id.video_view);
|
||||||
progressBar = (ProgressBar) findViewById(R.id.play_video_progress_bar);
|
progressBar = (ProgressBar) findViewById(R.id.play_video_progress_bar);
|
||||||
try {
|
try {
|
||||||
@@ -145,11 +172,6 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
|
||||||
super.onPostCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
||||||
super.onCreatePanelMenu(featured, menu);
|
super.onCreatePanelMenu(featured, menu);
|
||||||
@@ -159,11 +181,6 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
@@ -274,11 +291,9 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfHasSoftKeys(){
|
private boolean checkIfHasSoftKeys(){
|
||||||
if(Build.VERSION.SDK_INT >= 17) {
|
return Build.VERSION.SDK_INT >= 17 ||
|
||||||
return getNavigationBarHeight() != 0 || getNavigationBarWidth() != 0;
|
getNavigationBarHeight() != 0 ||
|
||||||
} else {
|
getNavigationBarWidth() != 0;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getNavigationBarHeight() {
|
private int getNavigationBarHeight() {
|
||||||
@@ -315,7 +330,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkIfLandscape() {
|
private boolean checkIfLandscape() {
|
||||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||||
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
return displayMetrics.heightPixels < displayMetrics.widthPixels;
|
return displayMetrics.heightPixels < displayMetrics.widthPixels;
|
||||||
@@ -331,6 +346,6 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putBoolean(PREF_IS_LANDSCAPE, isLandscape);
|
editor.putBoolean(PREF_IS_LANDSCAPE, isLandscape);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,10 +9,9 @@ import android.preference.PreferenceActivity;
|
|||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import android.support.v7.app.AppCompatDelegate;
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -70,14 +69,11 @@ public class SettingsActivity extends PreferenceActivity {
|
|||||||
getDelegate().onPostCreate(savedInstanceState);
|
getDelegate().onPostCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionBar getSupportActionBar() {
|
private ActionBar getSupportActionBar() {
|
||||||
return getDelegate().getSupportActionBar();
|
return getDelegate().getSupportActionBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
@NonNull
|
||||||
getDelegate().setSupportActionBar(toolbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MenuInflater getMenuInflater() {
|
public MenuInflater getMenuInflater() {
|
||||||
return getDelegate().getMenuInflater();
|
return getDelegate().getMenuInflater();
|
||||||
@@ -162,7 +158,7 @@ public class SettingsActivity extends PreferenceActivity {
|
|||||||
Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe";
|
Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe";
|
||||||
spEditor.putString(context.getString(R.string.downloadPathPreference)
|
spEditor.putString(context.getString(R.string.downloadPathPreference)
|
||||||
, newPipeDownloadStorage);
|
, newPipeDownloadStorage);
|
||||||
spEditor.commit();
|
spEditor.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import org.schabi.newpipe.services.AbstractVideoInfo;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 26.08.15.
|
* Created by Christian Schabesberger on 26.08.15.
|
||||||
@@ -27,53 +25,77 @@ import java.util.Vector;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**Info object for opened videos, ie the video ready to play.*/
|
/**Info object for opened videos, ie the video ready to play.*/
|
||||||
public class VideoInfo {
|
@SuppressWarnings("ALL")
|
||||||
public String id = "";
|
public class VideoInfo extends AbstractVideoInfo {
|
||||||
public String title = "";
|
|
||||||
public String uploader = "";
|
|
||||||
public String thumbnail_url = "";
|
|
||||||
public Bitmap thumbnail = null;
|
|
||||||
public String webpage_url = "";
|
|
||||||
public String upload_date = "";
|
|
||||||
public long view_count = 0;
|
|
||||||
|
|
||||||
public String uploader_thumbnail_url = "";
|
public String uploader_thumbnail_url = "";
|
||||||
public Bitmap uploader_thumbnail = null;
|
|
||||||
public String description = "";
|
public String description = "";
|
||||||
public int duration = -1;
|
|
||||||
public int age_limit = 0;
|
|
||||||
public int like_count = 0;
|
|
||||||
public int dislike_count = 0;
|
|
||||||
public String average_rating = "";
|
|
||||||
public VideoStream[] videoStreams = null;
|
public VideoStream[] videoStreams = null;
|
||||||
public AudioStream[] audioStreams = null;
|
public AudioStream[] audioStreams = null;
|
||||||
public VideoInfoItem nextVideo = null;
|
|
||||||
public VideoInfoItem[] relatedVideos = null;
|
|
||||||
public int videoAvailableStatus = VIDEO_AVAILABLE;
|
public int videoAvailableStatus = VIDEO_AVAILABLE;
|
||||||
|
public int duration = -1;
|
||||||
|
|
||||||
private static final String TAG = VideoInfo.class.toString();
|
/*YouTube-specific fields
|
||||||
|
todo: move these to a subclass*/
|
||||||
|
public int age_limit = 0;
|
||||||
|
public int like_count = -1;
|
||||||
|
public int dislike_count = -1;
|
||||||
|
public String average_rating = "";
|
||||||
|
public VideoPreviewInfo nextVideo = null;
|
||||||
|
public List<VideoPreviewInfo> relatedVideos = null;
|
||||||
|
public int startPosition = -1;//in seconds. some metadata is not passed using a VideoInfo object!
|
||||||
|
|
||||||
public static final int VIDEO_AVAILABLE = 0x00;
|
public static final int VIDEO_AVAILABLE = 0x00;
|
||||||
public static final int VIDEO_UNAVAILABLE = 0x01;
|
public static final int VIDEO_UNAVAILABLE = 0x01;
|
||||||
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
|
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
|
||||||
|
|
||||||
public static class VideoStream {
|
|
||||||
public VideoStream(String url, int format, String res) {
|
public VideoInfo() {}
|
||||||
this.url = url; this.format = format; resolution = res;
|
|
||||||
|
|
||||||
|
/**Creates a new VideoInfo object from an existing AbstractVideoInfo.
|
||||||
|
* All the shared properties are copied to the new VideoInfo.*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public VideoInfo(AbstractVideoInfo avi) {
|
||||||
|
this.id = avi.id;
|
||||||
|
this.title = avi.title;
|
||||||
|
this.uploader = avi.uploader;
|
||||||
|
this.thumbnail_url = avi.thumbnail_url;
|
||||||
|
this.thumbnail = avi.thumbnail;
|
||||||
|
this.webpage_url = avi.webpage_url;
|
||||||
|
this.upload_date = avi.upload_date;
|
||||||
|
this.upload_date = avi.upload_date;
|
||||||
|
this.view_count = avi.view_count;
|
||||||
|
|
||||||
|
//todo: better than this
|
||||||
|
if(avi instanceof VideoPreviewInfo) {//shitty String to convert code
|
||||||
|
String dur = ((VideoPreviewInfo)avi).duration;
|
||||||
|
int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":")));
|
||||||
|
int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length()));
|
||||||
|
this.duration = (minutes*60)+seconds;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VideoStream {
|
||||||
public String url = ""; //url of the stream
|
public String url = ""; //url of the stream
|
||||||
public int format = -1;
|
public int format = -1;
|
||||||
public String resolution = "";
|
public String resolution = "";
|
||||||
|
|
||||||
|
public VideoStream(String url, int format, String res) {
|
||||||
|
this.url = url; this.format = format; resolution = res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static class AudioStream {
|
public static class AudioStream {
|
||||||
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
|
|
||||||
this.url = url; this.format = format;
|
|
||||||
this.bandwidth = bandwidth; this.samplingRate = samplingRate;
|
|
||||||
}
|
|
||||||
public String url = "";
|
public String url = "";
|
||||||
public int format = -1;
|
public int format = -1;
|
||||||
public int bandwidth = -1;
|
public int bandwidth = -1;
|
||||||
public int samplingRate = -1;
|
public int samplingRate = -1;
|
||||||
|
|
||||||
|
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
|
||||||
|
this.url = url; this.format = format;
|
||||||
|
this.bandwidth = bandwidth; this.samplingRate = samplingRate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -26,16 +26,14 @@ import android.widget.TextView;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class VideoInfoItemViewCreator {
|
class VideoInfoItemViewCreator {
|
||||||
private static final String TAG = VideoInfoItemViewCreator.class.toString();
|
private final LayoutInflater inflater;
|
||||||
|
|
||||||
LayoutInflater inflater;
|
|
||||||
|
|
||||||
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
||||||
this.inflater = inflater;
|
this.inflater = inflater;
|
||||||
}
|
}
|
||||||
|
|
||||||
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoInfoItem info) {
|
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) {
|
||||||
ViewHolder holder;
|
ViewHolder holder;
|
||||||
if(convertView == null) {
|
if(convertView == null) {
|
||||||
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
||||||
@@ -62,7 +60,7 @@ public class VideoInfoItemViewCreator {
|
|||||||
holder.itemUploadDateView.setText(info.upload_date);
|
holder.itemUploadDateView.setText(info.upload_date);
|
||||||
} else {
|
} else {
|
||||||
//tweak if necessary: This is a hack to prevent having white space in the layout :P
|
//tweak if necessary: This is a hack to prevent having white space in the layout :P
|
||||||
holder.itemUploadDateView.setText(info.view_count);
|
holder.itemUploadDateView.setText(String.format("%d", info.view_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
|
@@ -7,10 +7,12 @@ import android.support.v4.app.NavUtils;
|
|||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.services.ServiceList;
|
||||||
|
import org.schabi.newpipe.services.StreamingService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
@@ -34,7 +36,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private static final String TAG = VideoItemDetailActivity.class.toString();
|
private static final String TAG = VideoItemDetailActivity.class.toString();
|
||||||
|
|
||||||
VideoItemDetailFragment fragment;
|
private VideoItemDetailFragment fragment;
|
||||||
|
|
||||||
private String videoUrl;
|
private String videoUrl;
|
||||||
private int currentStreamingService = -1;
|
private int currentStreamingService = -1;
|
||||||
@@ -44,7 +46,13 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
setContentView(R.layout.activity_videoitem_detail);
|
setContentView(R.layout.activity_videoitem_detail);
|
||||||
|
|
||||||
// Show the Up button in the action bar.
|
// Show the Up button in the action bar.
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
} catch(Exception e) {
|
||||||
|
Log.d(TAG, "Could not get SupportActionBar");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
// savedInstanceState is non-null when there is fragment state
|
// savedInstanceState is non-null when there is fragment state
|
||||||
// saved from previous configurations of this activity
|
// saved from previous configurations of this activity
|
||||||
@@ -61,27 +69,24 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
// this means the video was called though another app
|
// this means the video was called though another app
|
||||||
if (getIntent().getData() != null) {
|
if (getIntent().getData() != null) {
|
||||||
videoUrl = getIntent().getData().toString();
|
videoUrl = getIntent().getData().toString();
|
||||||
Log.i(TAG, "video URL passed:\"" + videoUrl + "\"");
|
|
||||||
StreamingService[] serviceList = ServiceList.getServices();
|
StreamingService[] serviceList = ServiceList.getServices();
|
||||||
Extractor extractor = null;
|
//VideoExtractor videoExtractor = null;
|
||||||
for (int i = 0; i < serviceList.length; i++) {
|
for (int i = 0; i < serviceList.length; i++) {
|
||||||
if (serviceList[i].acceptUrl(videoUrl)) {
|
if (serviceList[i].acceptUrl(videoUrl)) {
|
||||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
|
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
|
||||||
try {
|
currentStreamingService = i;
|
||||||
currentStreamingService = i;
|
//videoExtractor = ServiceList.getService(i).getExtractorInstance();
|
||||||
extractor = ServiceList.getService(i).getExtractorInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(extractor == null) {
|
if(currentStreamingService == -1) {
|
||||||
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
|
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
//arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
||||||
extractor.getVideoUrl(extractor.getVideoId(videoUrl)));
|
// videoExtractor.getVideoUrl(videoExtractor.getVideoId(videoUrl)));//cleans URL
|
||||||
|
arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
||||||
|
|
||||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
.getBoolean(getString(R.string.autoPlayThroughIntent), false));
|
.getBoolean(getString(R.string.autoPlayThroughIntent), false));
|
||||||
|
@@ -3,8 +3,6 @@ package org.schabi.newpipe;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
@@ -14,10 +12,10 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
|
import org.schabi.newpipe.services.ServiceList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
@@ -59,9 +57,9 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
|
|
||||||
private VideoItemListFragment listFragment;
|
private VideoItemListFragment listFragment;
|
||||||
private VideoItemDetailFragment videoFragment = null;
|
private VideoItemDetailFragment videoFragment = null;
|
||||||
Menu menu = null;
|
private Menu menu = null;
|
||||||
|
|
||||||
public class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
|
private class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
@@ -72,8 +70,14 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
// hide virtual keyboard
|
// hide virtual keyboard
|
||||||
InputMethodManager inputManager =
|
InputMethodManager inputManager =
|
||||||
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
inputManager.hideSoftInputFromWindow(
|
try {
|
||||||
getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
//noinspection ConstantConditions
|
||||||
|
inputManager.hideSoftInputFromWindow(
|
||||||
|
getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
|
} catch(NullPointerException e) {
|
||||||
|
Log.e(TAG, "Could not get widget with focus");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
// clear focus
|
// clear focus
|
||||||
// 1. to not open up the keyboard after switching back to this
|
// 1. to not open up the keyboard after switching back to this
|
||||||
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
|
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
|
||||||
@@ -116,10 +120,16 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
|
|
||||||
if(arguments != null) {
|
if(arguments != null) {
|
||||||
//Parcelable[] p = arguments.getParcelableArray(VIDEO_INFO_ITEMS);
|
//Parcelable[] p = arguments.getParcelableArray(VIDEO_INFO_ITEMS);
|
||||||
ArrayList<VideoInfoItem> p = arguments.getParcelableArrayList(VIDEO_INFO_ITEMS);
|
ArrayList<VideoPreviewInfo> p = arguments.getParcelableArrayList(VIDEO_INFO_ITEMS);
|
||||||
if(p != null) {
|
if(p != null) {
|
||||||
mode = PRESENT_VIDEOS_MODE;
|
mode = PRESENT_VIDEOS_MODE;
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
try {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
Log.e(TAG, "Could not get SupportActionBar");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
listFragment.present(p);
|
listFragment.present(p);
|
||||||
}
|
}
|
||||||
|
@@ -15,10 +15,12 @@ import android.widget.ListView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.services.SearchEngine;
|
||||||
|
import org.schabi.newpipe.services.StreamingService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
@@ -62,8 +64,8 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
private ListView list;
|
private ListView list;
|
||||||
|
|
||||||
private class ResultRunnable implements Runnable {
|
private class ResultRunnable implements Runnable {
|
||||||
private SearchEngine.Result result;
|
private final SearchEngine.Result result;
|
||||||
private int requestId;
|
private final int requestId;
|
||||||
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.requestId = requestId;
|
this.requestId = requestId;
|
||||||
@@ -75,12 +77,12 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SearchRunnable implements Runnable {
|
private class SearchRunnable implements Runnable {
|
||||||
private SearchEngine engine;
|
private final SearchEngine engine;
|
||||||
private String query;
|
private final String query;
|
||||||
private int page;
|
private final int page;
|
||||||
Handler h = new Handler();
|
final Handler h = new Handler();
|
||||||
private volatile boolean run = true;
|
private volatile boolean run = true;
|
||||||
private int requestId;
|
private final int requestId;
|
||||||
public SearchRunnable(SearchEngine engine, String query, int page, int requestId) {
|
public SearchRunnable(SearchEngine engine, String query, int page, int requestId) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
this.query = query;
|
this.query = query;
|
||||||
@@ -114,14 +116,14 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class LoadThumbsRunnable implements Runnable {
|
private class LoadThumbsRunnable implements Runnable {
|
||||||
private Vector<String> thumbnailUrlList = new Vector<>();
|
private final Vector<String> thumbnailUrlList = new Vector<>();
|
||||||
private Vector<Boolean> downloadedList;
|
private final Vector<Boolean> downloadedList;
|
||||||
Handler h = new Handler();
|
final Handler h = new Handler();
|
||||||
private volatile boolean run = true;
|
private volatile boolean run = true;
|
||||||
private int requestId;
|
private final int requestId;
|
||||||
public LoadThumbsRunnable(Vector<VideoInfoItem> videoList,
|
public LoadThumbsRunnable(Vector<VideoPreviewInfo> videoList,
|
||||||
Vector<Boolean> downloadedList, int requestId) {
|
Vector<Boolean> downloadedList, int requestId) {
|
||||||
for(VideoInfoItem item : videoList) {
|
for(VideoPreviewInfo item : videoList) {
|
||||||
thumbnailUrlList.add(item.thumbnail_url);
|
thumbnailUrlList.add(item.thumbnail_url);
|
||||||
}
|
}
|
||||||
this.downloadedList = downloadedList;
|
this.downloadedList = downloadedList;
|
||||||
@@ -137,7 +139,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
public void run() {
|
public void run() {
|
||||||
for(int i = 0; i < thumbnailUrlList.size() && run; i++) {
|
for(int i = 0; i < thumbnailUrlList.size() && run; i++) {
|
||||||
if(!downloadedList.get(i)) {
|
if(!downloadedList.get(i)) {
|
||||||
Bitmap thumbnail = null;
|
Bitmap thumbnail;
|
||||||
try {
|
try {
|
||||||
thumbnail = BitmapFactory.decodeStream(
|
thumbnail = BitmapFactory.decodeStream(
|
||||||
new URL(thumbnailUrlList.get(i)).openConnection().getInputStream());
|
new URL(thumbnailUrlList.get(i)).openConnection().getInputStream());
|
||||||
@@ -151,9 +153,9 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SetThumbnailRunnable implements Runnable {
|
private class SetThumbnailRunnable implements Runnable {
|
||||||
private int index;
|
private final int index;
|
||||||
private Bitmap thumbnail;
|
private final Bitmap thumbnail;
|
||||||
private int requestId;
|
private final int requestId;
|
||||||
public SetThumbnailRunnable(int index, Bitmap thumbnail, int requestId) {
|
public SetThumbnailRunnable(int index, Bitmap thumbnail, int requestId) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.thumbnail = thumbnail;
|
this.thumbnail = thumbnail;
|
||||||
@@ -162,13 +164,13 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if(requestId == currentRequestId) {
|
if(requestId == currentRequestId) {
|
||||||
videoListAdapter.updateDownloadedThumbnailList(index, true);
|
videoListAdapter.updateDownloadedThumbnailList(index);
|
||||||
videoListAdapter.setThumbnail(index, thumbnail);
|
videoListAdapter.setThumbnail(index, thumbnail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void present(List<VideoInfoItem> videoList) {
|
public void present(List<VideoPreviewInfo> videoList) {
|
||||||
mode = PRESENT_VIDEOS_MODE;
|
mode = PRESENT_VIDEOS_MODE;
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
getListView().smoothScrollToPosition(0);
|
getListView().smoothScrollToPosition(0);
|
||||||
@@ -186,7 +188,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
getListView().smoothScrollToPosition(0);
|
getListView().smoothScrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void nextPage() {
|
private void nextPage() {
|
||||||
lastPage++;
|
lastPage++;
|
||||||
Log.d(TAG, getString(R.string.searchPage) + Integer.toString(lastPage));
|
Log.d(TAG, getString(R.string.searchPage) + Integer.toString(lastPage));
|
||||||
startSearch(query, lastPage);
|
startSearch(query, lastPage);
|
||||||
@@ -205,7 +207,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
this.streamingService = streamingService;
|
this.streamingService = streamingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateListOnResult(SearchEngine.Result result, int requestId) {
|
private void updateListOnResult(SearchEngine.Result result, int requestId) {
|
||||||
if(requestId == currentRequestId) {
|
if(requestId == currentRequestId) {
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
if (result.resultList.isEmpty()) {
|
if (result.resultList.isEmpty()) {
|
||||||
@@ -220,7 +222,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateList(List<VideoInfoItem> list) {
|
private void updateList(List<VideoPreviewInfo> list) {
|
||||||
try {
|
try {
|
||||||
videoListAdapter.addVideoList(list);
|
videoListAdapter.addVideoList(list);
|
||||||
terminateThreads();
|
terminateThreads();
|
||||||
@@ -235,7 +237,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void terminateThreads() {
|
private void terminateThreads() {
|
||||||
if(loadThumbsRunnable != null && loadThumbsRunnable.isRunning()) {
|
if(loadThumbsRunnable != null && loadThumbsRunnable.isRunning()) {
|
||||||
loadThumbsRunnable.terminate();
|
loadThumbsRunnable.terminate();
|
||||||
try {
|
try {
|
||||||
@@ -274,12 +276,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
void onItemSelected(String id);
|
void onItemSelected(String id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Callbacks mCallbacks = null;
|
private Callbacks mCallbacks = null;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
@@ -331,11 +328,6 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
mCallbacks = (Callbacks) context;
|
mCallbacks = (Callbacks) context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetach() {
|
|
||||||
super.onDetach();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onListItemClick(ListView listView, View view, int position, long id) {
|
public void onListItemClick(ListView listView, View view, int position, long id) {
|
||||||
super.onListItemClick(listView, view, position, id);
|
super.onListItemClick(listView, view, position, id);
|
||||||
@@ -343,22 +335,11 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
mCallbacks.onItemSelected(Long.toString(id));
|
mCallbacks.onItemSelected(Long.toString(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
/*
|
|
||||||
if (mActivatedPosition != ListView.INVALID_POSITION) {
|
|
||||||
// Serialize and persist the activated item position.
|
|
||||||
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns on activate-on-click mode. When this mode is on, list items will be
|
* Turns on activate-on-click mode. When this mode is on, list items will be
|
||||||
* given the 'activated' state when touched.
|
* given the 'activated' state when touched.
|
||||||
*/
|
*/
|
||||||
public void setActivateOnItemClick(boolean activateOnItemClick) {
|
public void setActivateOnItemClick(@SuppressWarnings("SameParameterValue") boolean activateOnItemClick) {
|
||||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
||||||
// give items the 'activated' state when touched.
|
// give items the 'activated' state when touched.
|
||||||
getListView().setChoiceMode(activateOnItemClick
|
getListView().setChoiceMode(activateOnItemClick
|
||||||
|
@@ -32,24 +32,22 @@ import java.util.Vector;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class VideoListAdapter extends BaseAdapter {
|
class VideoListAdapter extends BaseAdapter {
|
||||||
private static final String TAG = VideoListAdapter.class.toString();
|
private final Context context;
|
||||||
|
private final VideoInfoItemViewCreator viewCreator;
|
||||||
private Context context;
|
private Vector<VideoPreviewInfo> videoList = new Vector<>();
|
||||||
private VideoInfoItemViewCreator viewCreator;
|
|
||||||
private Vector<VideoInfoItem> videoList = new Vector<>();
|
|
||||||
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
||||||
VideoItemListFragment videoListFragment;
|
private final ListView listView;
|
||||||
ListView listView;
|
|
||||||
|
|
||||||
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
||||||
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
|
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
|
||||||
this.videoListFragment = videoListFragment;
|
|
||||||
this.listView = videoListFragment.getListView();
|
this.listView = videoListFragment.getListView();
|
||||||
|
this.listView.setDivider(null);
|
||||||
|
this.listView.setDividerHeight(0);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVideoList(List<VideoInfoItem> videos) {
|
public void addVideoList(List<VideoPreviewInfo> videos) {
|
||||||
videoList.addAll(videos);
|
videoList.addAll(videos);
|
||||||
for(int i = 0; i < videos.size(); i++) {
|
for(int i = 0; i < videos.size(); i++) {
|
||||||
downloadedThumbnailList.add(false);
|
downloadedThumbnailList.add(false);
|
||||||
@@ -63,12 +61,12 @@ public class VideoListAdapter extends BaseAdapter {
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector<VideoInfoItem> getVideoList() {
|
public Vector<VideoPreviewInfo> getVideoList() {
|
||||||
return videoList;
|
return videoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDownloadedThumbnailList(int index, boolean val) {
|
public void updateDownloadedThumbnailList(int index) {
|
||||||
downloadedThumbnailList.set(index, val);
|
downloadedThumbnailList.set(index, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector<Boolean> getDownloadedThumbnailList() {
|
public Vector<Boolean> getDownloadedThumbnailList() {
|
||||||
|
@@ -4,11 +4,13 @@ import android.graphics.Bitmap;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.services.AbstractVideoInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 26.08.15.
|
* Created by Christian Schabesberger on 26.08.15.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
* VideoInfoItem.java is part of NewPipe.
|
* VideoPreviewInfo.java is part of NewPipe.
|
||||||
*
|
*
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,19 +27,10 @@ import android.os.Parcelable;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**Info object for previews of unopened videos, eg search results, related videos*/
|
/**Info object for previews of unopened videos, eg search results, related videos*/
|
||||||
public class VideoInfoItem implements Parcelable {
|
public class VideoPreviewInfo extends AbstractVideoInfo implements Parcelable {
|
||||||
public String id = "";
|
|
||||||
public String title = "";
|
|
||||||
public String uploader = "";
|
|
||||||
public String thumbnail_url = "";
|
|
||||||
public Bitmap thumbnail = null;
|
|
||||||
public String webpage_url = "";
|
|
||||||
public String upload_date = "";
|
|
||||||
public String view_count = "";
|
|
||||||
|
|
||||||
public String duration = "";
|
public String duration = "";
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
protected VideoInfoItem(Parcel in) {
|
protected VideoPreviewInfo(Parcel in) {
|
||||||
id = in.readString();
|
id = in.readString();
|
||||||
title = in.readString();
|
title = in.readString();
|
||||||
uploader = in.readString();
|
uploader = in.readString();
|
||||||
@@ -46,10 +39,10 @@ public class VideoInfoItem implements Parcelable {
|
|||||||
thumbnail = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
|
thumbnail = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
|
||||||
webpage_url = in.readString();
|
webpage_url = in.readString();
|
||||||
upload_date = in.readString();
|
upload_date = in.readString();
|
||||||
view_count = in.readString();
|
view_count = in.readLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VideoInfoItem() {
|
public VideoPreviewInfo() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,19 +61,19 @@ public class VideoInfoItem implements Parcelable {
|
|||||||
dest.writeValue(thumbnail);
|
dest.writeValue(thumbnail);
|
||||||
dest.writeString(webpage_url);
|
dest.writeString(webpage_url);
|
||||||
dest.writeString(upload_date);
|
dest.writeString(upload_date);
|
||||||
dest.writeString(view_count);
|
dest.writeLong(view_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static final Parcelable.Creator<VideoInfoItem> CREATOR = new Parcelable.Creator<VideoInfoItem>() {
|
public static final Parcelable.Creator<VideoPreviewInfo> CREATOR = new Parcelable.Creator<VideoPreviewInfo>() {
|
||||||
@Override
|
@Override
|
||||||
public VideoInfoItem createFromParcel(Parcel in) {
|
public VideoPreviewInfo createFromParcel(Parcel in) {
|
||||||
return new VideoInfoItem(in);
|
return new VideoPreviewInfo(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoInfoItem[] newArray(int size) {
|
public VideoPreviewInfo[] newArray(int size) {
|
||||||
return new VideoInfoItem[size];
|
return new VideoPreviewInfo[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
package org.schabi.newpipe.services;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
/**Common properties between VideoInfo and VideoPreviewInfo.*/
|
||||||
|
public abstract class AbstractVideoInfo {
|
||||||
|
public String id = "";
|
||||||
|
public String title = "";
|
||||||
|
public String uploader = "";
|
||||||
|
//public int duration = -1;
|
||||||
|
public String thumbnail_url = "";
|
||||||
|
public Bitmap thumbnail = null;
|
||||||
|
public String webpage_url = "";
|
||||||
|
public String upload_date = "";
|
||||||
|
public long view_count = -1;
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe.services;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.VideoPreviewInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
@@ -23,13 +25,14 @@ import java.util.Vector;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public interface SearchEngine {
|
public interface SearchEngine {
|
||||||
|
|
||||||
|
|
||||||
class Result {
|
class Result {
|
||||||
public String errorMessage = "";
|
public String errorMessage = "";
|
||||||
public String suggestion = "";
|
public String suggestion = "";
|
||||||
public Vector<VideoInfoItem> resultList = new Vector<>();
|
public final Vector<VideoPreviewInfo> resultList = new Vector<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> suggestionList(String query);
|
ArrayList<String> suggestionList(String query);
|
@@ -1,8 +1,8 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe.services;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.schabi.newpipe.youtube.YoutubeService;
|
import org.schabi.newpipe.services.youtube.YoutubeService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 23.08.15.
|
* Created by Christian Schabesberger on 23.08.15.
|
||||||
@@ -24,6 +24,10 @@ import org.schabi.newpipe.youtube.YoutubeService;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**Provides access to the video streaming services supported by NewPipe.
|
||||||
|
* Currently only Youtube until the API becomes more stable.*/
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public class ServiceList {
|
public class ServiceList {
|
||||||
private static final String TAG = ServiceList.class.toString();
|
private static final String TAG = ServiceList.class.toString();
|
||||||
private static final StreamingService[] services = {
|
private static final StreamingService[] services = {
|
||||||
@@ -40,7 +44,7 @@ public class ServiceList {
|
|||||||
}
|
}
|
||||||
public static int getIdOfService(String serviceName) {
|
public static int getIdOfService(String serviceName) {
|
||||||
for(int i = 0; i < services.length; i++) {
|
for(int i = 0; i < services.length; i++) {
|
||||||
if(services[i].getServiceInfo().name == serviceName) {
|
if(services[i].getServiceInfo().name.equals(serviceName)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe.services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 23.08.15.
|
* Created by Christian Schabesberger on 23.08.15.
|
||||||
@@ -25,7 +25,7 @@ public interface StreamingService {
|
|||||||
public String name = "";
|
public String name = "";
|
||||||
}
|
}
|
||||||
ServiceInfo getServiceInfo();
|
ServiceInfo getServiceInfo();
|
||||||
Extractor getExtractorInstance();
|
VideoExtractor getExtractorInstance(String url);
|
||||||
SearchEngine getSearchEngineInstance();
|
SearchEngine getSearchEngineInstance();
|
||||||
|
|
||||||
/**When a VIEW_ACTION is caught this function will test if the url delivered within the calling
|
/**When a VIEW_ACTION is caught this function will test if the url delivered within the calling
|
@@ -0,0 +1,118 @@
|
|||||||
|
package org.schabi.newpipe.services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 10.08.15.
|
||||||
|
*
|
||||||
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
|
* VideoExtractor.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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.schabi.newpipe.VideoInfo;
|
||||||
|
|
||||||
|
/**Scrapes information from a video streaming service (eg, YouTube).*/
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
|
public abstract class VideoExtractor {
|
||||||
|
protected final String pageUrl;
|
||||||
|
protected VideoInfo videoInfo;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public VideoExtractor(String url) {
|
||||||
|
this.pageUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Fills out the video info fields which are common to all services.
|
||||||
|
* Probably needs to be overridden by subclasses*/
|
||||||
|
public VideoInfo getVideoInfo()
|
||||||
|
{
|
||||||
|
if(videoInfo == null) {
|
||||||
|
videoInfo = new VideoInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.webpage_url.isEmpty()) {
|
||||||
|
videoInfo.webpage_url = pageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.title.isEmpty()) {
|
||||||
|
videoInfo.title = getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.duration < 1) {
|
||||||
|
videoInfo.duration = getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(videoInfo.uploader.isEmpty()) {
|
||||||
|
videoInfo.uploader = getUploader();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.description.isEmpty()) {
|
||||||
|
videoInfo.description = getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.view_count == -1) {
|
||||||
|
videoInfo.view_count = getViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.upload_date.isEmpty()) {
|
||||||
|
videoInfo.upload_date = getUploadDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.thumbnail_url.isEmpty()) {
|
||||||
|
videoInfo.thumbnail_url = getThumbnailUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.id.isEmpty()) {
|
||||||
|
videoInfo.id = getVideoId(pageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Load and extract audio*/
|
||||||
|
if(videoInfo.audioStreams == null) {
|
||||||
|
videoInfo.audioStreams = getAudioStreams();
|
||||||
|
}
|
||||||
|
/** Extract video stream url*/
|
||||||
|
if(videoInfo.videoStreams == null) {
|
||||||
|
videoInfo.videoStreams = getVideoStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.uploader_thumbnail_url.isEmpty()) {
|
||||||
|
videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(videoInfo.startPosition < 0) {
|
||||||
|
videoInfo.startPosition = getTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bitmap thumbnail = null;
|
||||||
|
//Bitmap uploader_thumbnail = null;
|
||||||
|
//int videoAvailableStatus = VIDEO_AVAILABLE;
|
||||||
|
return videoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getVideoUrl(String videoId);
|
||||||
|
protected abstract String getVideoId(String siteUrl);
|
||||||
|
protected abstract int getTimeStamp();
|
||||||
|
protected abstract String getTitle();
|
||||||
|
protected abstract String getDescription();
|
||||||
|
protected abstract String getUploader();
|
||||||
|
protected abstract int getLength();
|
||||||
|
protected abstract int getViews();
|
||||||
|
protected abstract String getUploadDate();
|
||||||
|
protected abstract String getThumbnailUrl();
|
||||||
|
protected abstract String getUploaderThumbnailUrl();
|
||||||
|
protected abstract VideoInfo.AudioStream[] getAudioStreams();
|
||||||
|
protected abstract VideoInfo.VideoStream[] getVideoStreams();
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package org.schabi.newpipe.youtube;
|
package org.schabi.newpipe.services.youtube;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -7,8 +7,8 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
import org.schabi.newpipe.Downloader;
|
import org.schabi.newpipe.Downloader;
|
||||||
import org.schabi.newpipe.SearchEngine;
|
import org.schabi.newpipe.services.SearchEngine;
|
||||||
import org.schabi.newpipe.VideoInfoItem;
|
import org.schabi.newpipe.VideoPreviewInfo;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
@@ -62,7 +62,7 @@ public class YoutubeSearchEngine implements SearchEngine {
|
|||||||
String site;
|
String site;
|
||||||
String url = builder.build().toString();
|
String url = builder.build().toString();
|
||||||
//if we've been passed a valid language code, append it to the URL
|
//if we've been passed a valid language code, append it to the URL
|
||||||
if(languageCode.length() > 0) {
|
if(!languageCode.isEmpty()) {
|
||||||
//assert Pattern.matches("[a-z]{2}(-([A-Z]{2}|[0-9]{1,3}))?", languageCode);
|
//assert Pattern.matches("[a-z]{2}(-([A-Z]{2}|[0-9]{1,3}))?", languageCode);
|
||||||
site = Downloader.download(url, languageCode);
|
site = Downloader.download(url, languageCode);
|
||||||
}
|
}
|
||||||
@@ -95,26 +95,26 @@ public class YoutubeSearchEngine implements SearchEngine {
|
|||||||
// both types of spell correction item
|
// both types of spell correction item
|
||||||
if(!((el = item.select("div[class*=\"spell-correction\"]").first()) == null)) {
|
if(!((el = item.select("div[class*=\"spell-correction\"]").first()) == null)) {
|
||||||
result.suggestion = el.select("a").first().text();
|
result.suggestion = el.select("a").first().text();
|
||||||
// search message item
|
// search message item
|
||||||
} else if(!((el = item.select("div[class*=\"search-message\"]").first()) == null)) {
|
} else if(!((el = item.select("div[class*=\"search-message\"]").first()) == null)) {
|
||||||
result.errorMessage = el.text();
|
result.errorMessage = el.text();
|
||||||
|
|
||||||
// video item type
|
// video item type
|
||||||
} else if(!((el = item.select("div[class*=\"yt-lockup-video\"").first()) == null)) {
|
} else if(!((el = item.select("div[class*=\"yt-lockup-video\"").first()) == null)) {
|
||||||
VideoInfoItem resultItem = new VideoInfoItem();
|
VideoPreviewInfo resultItem = new VideoPreviewInfo();
|
||||||
Element dl = el.select("h3").first().select("a").first();
|
Element dl = el.select("h3").first().select("a").first();
|
||||||
resultItem.webpage_url = dl.attr("abs:href");
|
resultItem.webpage_url = dl.attr("abs:href");
|
||||||
try {
|
try {
|
||||||
Pattern p = Pattern.compile("v=([0-9a-zA-Z-]*)");
|
Pattern p = Pattern.compile("v=([0-9a-zA-Z-]*)");
|
||||||
Matcher m = p.matcher(resultItem.webpage_url);
|
Matcher m = p.matcher(resultItem.webpage_url);
|
||||||
m.find();
|
|
||||||
resultItem.id=m.group(1);
|
resultItem.id=m.group(1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
resultItem.title = dl.text();
|
resultItem.title = dl.text();
|
||||||
resultItem.duration = item.select("span[class=\"video-time\"]").first()
|
|
||||||
.text();
|
resultItem.duration = item.select("span[class=\"video-time\"]").first().text();
|
||||||
|
|
||||||
resultItem.uploader = item.select("div[class=\"yt-lockup-byline\"]").first()
|
resultItem.uploader = item.select("div[class=\"yt-lockup-byline\"]").first()
|
||||||
.select("a").first()
|
.select("a").first()
|
||||||
.text();
|
.text();
|
||||||
@@ -132,7 +132,8 @@ public class YoutubeSearchEngine implements SearchEngine {
|
|||||||
}
|
}
|
||||||
result.resultList.add(resultItem);
|
result.resultList.add(resultItem);
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "GREAT FUCKING ERROR");
|
//noinspection ConstantConditions
|
||||||
|
Log.e(TAG, "unexpected element found:\""+el+"\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
@@ -1,8 +1,8 @@
|
|||||||
package org.schabi.newpipe.youtube;
|
package org.schabi.newpipe.services.youtube;
|
||||||
|
|
||||||
import org.schabi.newpipe.StreamingService;
|
import org.schabi.newpipe.services.StreamingService;
|
||||||
import org.schabi.newpipe.Extractor;
|
import org.schabi.newpipe.services.VideoExtractor;
|
||||||
import org.schabi.newpipe.SearchEngine;
|
import org.schabi.newpipe.services.SearchEngine;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,8 +33,13 @@ public class YoutubeService implements StreamingService {
|
|||||||
return serviceInfo;
|
return serviceInfo;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Extractor getExtractorInstance() {
|
public VideoExtractor getExtractorInstance(String url) {
|
||||||
return new YoutubeExtractor();
|
if(acceptUrl(url)) {
|
||||||
|
return new YoutubeVideoExtractor(url);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("supplied String is not a valid Youtube URL");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public SearchEngine getSearchEngineInstance() {
|
public SearchEngine getSearchEngineInstance() {
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable-nodpi/dummy_thumbnail_dark.png
Normal file
After Width: | Height: | Size: 188 B |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |