Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f67158a2a7 | ||
![]() |
c22c2009d4 | ||
![]() |
ab4d626ea9 | ||
![]() |
96709d22e9 | ||
![]() |
f0bd171eee | ||
![]() |
c4191077f3 | ||
![]() |
321d090052 | ||
![]() |
32dcb4d281 | ||
![]() |
080159849e | ||
![]() |
d9e690f62c | ||
![]() |
8c0156dea3 | ||
![]() |
038c59ce66 | ||
![]() |
72e08c0447 | ||
![]() |
173eaa8cf8 | ||
![]() |
a04cd24e5e | ||
![]() |
0e11404b3b | ||
![]() |
342807e26a | ||
![]() |
c068f08ff8 |
46
README.md
@@ -1,7 +1,45 @@
|
|||||||
NewPipe
|
# NewPipe
|
||||||
-------
|
|
||||||
|
|
||||||
|
[](https://hosted.weblate.org/engage/NewPipe/)
|
||||||
|
|
||||||
NewPipe is a lightweight youtube frontend for android. It's supposed to be used without the youtube-api and without any google play services. NewPipe only parses the youtube website in order to gain the information it needs.
|
[](http://dasochan.nl/newpipe/)
|
||||||
|
NewPipe: A free lightweight Youtube fronted for Android.
|
||||||
|
|
||||||
This a very early version of the app, so not all functionality is implemented, and there may still be a lot of bugs. But all in all it's doing what it is supposed to do. It makes it possible to watch youtube videos. So don't be cruel to this app. It will improve...
|
[](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Search videos
|
||||||
|
* Display general information about a video
|
||||||
|
* Watch Youtube videos
|
||||||
|
* Listen to Youtube videos (audio only streaming)
|
||||||
|
* Select the streaming player to watch the video with
|
||||||
|
* Download videos (working, but it could be better)
|
||||||
|
* Download audio only (working but, but it could be better)
|
||||||
|
* Open a video in Kodi
|
||||||
|
|
||||||
|
## Coming Features
|
||||||
|
|
||||||
|
* Shows Next/Related videos
|
||||||
|
* Improved Downloading
|
||||||
|
* Bookmarks
|
||||||
|
* View history
|
||||||
|
* Search history
|
||||||
|
* Search channels
|
||||||
|
* Display general information about channels
|
||||||
|
* Subscribe to channels
|
||||||
|
* Watch videos from a channel
|
||||||
|
* Search/Watch Playlists
|
||||||
|
* ... and many more
|
||||||
|
|
||||||
|
### Multi service support
|
||||||
|
Generally NewPipe is designed to not only support YouTube, but many more streaming services. How ever, right now NewPipe is not stable enough to support more than only youtube. But if all works as plant, NewPipe will get such support by the version 2.0.
|
||||||
|
|
||||||
|
# Help is always welcome !!!
|
||||||
|
Whether its about ideas, translation, design changes, code cleaning, or real heavy code changes. Help is always welcome.
|
||||||
|
|
||||||
|
The more is done the better it gets!
|
||||||
|
16
app/app.iml
@@ -71,9 +71,10 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.0.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.0/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.0.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.1.0/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.0.1/jars" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.1.0/jars" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.0/jars" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
|
||||||
@@ -91,11 +92,12 @@
|
|||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" exported="" name="recyclerview-v7-23.1.0" level="project" />
|
||||||
<orderEntry type="library" exported="" name="jsoup-1.8.3" level="project" />
|
<orderEntry type="library" exported="" name="jsoup-1.8.3" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-v4-23.0.1" level="project" />
|
<orderEntry type="library" exported="" name="support-v4-23.1.0" level="project" />
|
||||||
<orderEntry type="library" exported="" name="rhino-1.7.7" level="project" />
|
<orderEntry type="library" exported="" name="rhino-1.7.7" level="project" />
|
||||||
<orderEntry type="library" exported="" name="design-23.0.1" level="project" />
|
<orderEntry type="library" exported="" name="design-23.1.0" level="project" />
|
||||||
<orderEntry type="library" exported="" name="appcompat-v7-23.0.1" level="project" />
|
<orderEntry type="library" exported="" name="appcompat-v7-23.1.0" level="project" />
|
||||||
<orderEntry type="library" exported="" name="support-annotations-23.0.1" level="project" />
|
<orderEntry type="library" exported="" name="support-annotations-23.1.0" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
versionCode 4
|
versionCode 5
|
||||||
versionName "0.4.1"
|
versionName "0.5.0"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -21,9 +21,9 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
compile 'com.android.support:appcompat-v7:23.0.1'
|
compile 'com.android.support:appcompat-v7:23.1.0'
|
||||||
compile 'com.android.support:support-v4:23.0.1'
|
compile 'com.android.support:support-v4:23.1.0'
|
||||||
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'
|
||||||
compile 'com.android.support:design:23.0.1'
|
compile 'com.android.support:design:23.1.0'
|
||||||
}
|
}
|
||||||
|
@@ -34,27 +34,27 @@
|
|||||||
<data
|
<data
|
||||||
android:host="youtube.com"
|
android:host="youtube.com"
|
||||||
android:scheme="http"
|
android:scheme="http"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="youtube.com"
|
android:host="youtube.com"
|
||||||
android:scheme="https"
|
android:scheme="https"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="www.youtube.com"
|
android:host="www.youtube.com"
|
||||||
android:scheme="http"
|
android:scheme="http"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="www.youtube.com"
|
android:host="www.youtube.com"
|
||||||
android:scheme="https"
|
android:scheme="https"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="m.youtube.com"
|
android:host="m.youtube.com"
|
||||||
android:scheme="http"
|
android:scheme="http"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="m.youtube.com"
|
android:host="m.youtube.com"
|
||||||
android:scheme="https"
|
android:scheme="https"
|
||||||
android:pathPrefix="/watch"/>
|
android:pathPattern="/?*#*/*watch"/>
|
||||||
<data
|
<data
|
||||||
android:host="youtu.be"
|
android:host="youtu.be"
|
||||||
android:scheme="https"
|
android:scheme="https"
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 10.08.15.
|
* Created by Christian Schabesberger on 10.08.15.
|
||||||
*
|
*
|
||||||
|
@@ -5,7 +5,6 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -19,15 +18,11 @@ import android.view.Display;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.Surface;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.MediaController;
|
import android.widget.MediaController;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.SeekBar;
|
|
||||||
import android.widget.VideoView;
|
import android.widget.VideoView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +79,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
setContentView(R.layout.activity_play_video);
|
setContentView(R.layout.activity_play_video);
|
||||||
|
|
||||||
isLandscape = checkIfLandscape();
|
isLandscape = checkIfLandscape();
|
||||||
hasSoftKeys = checkIfhasSoftKeys();
|
hasSoftKeys = checkIfHasSoftKeys();
|
||||||
|
|
||||||
actionBar = getSupportActionBar();
|
actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
@@ -138,9 +133,11 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
if (android.os.Build.VERSION.SDK_INT >= 17) {
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||||
|
}
|
||||||
|
|
||||||
prefs = getPreferences(Context.MODE_PRIVATE);
|
prefs = getPreferences(Context.MODE_PRIVATE);
|
||||||
if(prefs.getBoolean(PREF_IS_LANDSCAPE, false) && !isLandscape) {
|
if(prefs.getBoolean(PREF_IS_LANDSCAPE, false) && !isLandscape) {
|
||||||
@@ -203,10 +200,10 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
isLandscape = true;
|
isLandscape = true;
|
||||||
adjustMediaControllMetrics();
|
adjustMediaControlMetrics();
|
||||||
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT){
|
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT){
|
||||||
isLandscape = false;
|
isLandscape = false;
|
||||||
adjustMediaControllMetrics();
|
adjustMediaControlMetrics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +226,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
uiIsHidden = false;
|
uiIsHidden = false;
|
||||||
mediaController.show(100000);
|
mediaController.show(100000);
|
||||||
actionBar.show();
|
actionBar.show();
|
||||||
adjustMediaControllMetrics();
|
adjustMediaControlMetrics();
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.postDelayed(new Runnable() {
|
handler.postDelayed(new Runnable() {
|
||||||
@@ -250,16 +247,18 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
uiIsHidden = true;
|
uiIsHidden = true;
|
||||||
actionBar.hide();
|
actionBar.hide();
|
||||||
mediaController.hide();
|
mediaController.hide();
|
||||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
if (android.os.Build.VERSION.SDK_INT >= 17) {
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||||
|
}
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustMediaControllMetrics() {
|
private void adjustMediaControlMetrics() {
|
||||||
MediaController.LayoutParams mediaControllerLayout
|
MediaController.LayoutParams mediaControllerLayout
|
||||||
= new MediaController.LayoutParams(MediaController.LayoutParams.MATCH_PARENT,
|
= new MediaController.LayoutParams(MediaController.LayoutParams.MATCH_PARENT,
|
||||||
MediaController.LayoutParams.WRAP_CONTENT);
|
MediaController.LayoutParams.WRAP_CONTENT);
|
||||||
@@ -274,7 +273,7 @@ public class PlayVideoActivity extends AppCompatActivity {
|
|||||||
mediaController.setLayoutParams(mediaControllerLayout);
|
mediaController.setLayoutParams(mediaControllerLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfhasSoftKeys(){
|
private boolean checkIfHasSoftKeys(){
|
||||||
if(Build.VERSION.SDK_INT >= 17) {
|
if(Build.VERSION.SDK_INT >= 17) {
|
||||||
return getNavigationBarHeight() != 0 || getNavigationBarWidth() != 0;
|
return getNavigationBarHeight() != 0 || getNavigationBarWidth() != 0;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -110,13 +110,13 @@ public class VideoInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class AudioStream {
|
public static class AudioStream {
|
||||||
public AudioStream(String url, int format, int bandWidth, int samplingRate) {
|
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
|
||||||
this.url = url; this.format = format;
|
this.url = url; this.format = format;
|
||||||
this.bandWidth = bandWidth; this.samplingRate = samplingRate;
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -31,4 +31,5 @@ public class VideoInfoItem {
|
|||||||
public Bitmap thumbnail = null;
|
public Bitmap thumbnail = null;
|
||||||
public String webpage_url = "";
|
public String webpage_url = "";
|
||||||
public String upload_date = "";
|
public String upload_date = "";
|
||||||
|
public String view_count = "";
|
||||||
}
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 24.10.15.
|
||||||
|
*
|
||||||
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
|
* VideoInfoItemViewCreator.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 class VideoInfoItemViewCreator {
|
||||||
|
private static final String TAG = VideoInfoItemViewCreator.class.toString();
|
||||||
|
|
||||||
|
LayoutInflater inflater;
|
||||||
|
|
||||||
|
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
||||||
|
this.inflater = inflater;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoInfoItem info) {
|
||||||
|
ViewHolder holder;
|
||||||
|
if(convertView == null) {
|
||||||
|
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
||||||
|
holder = new ViewHolder();
|
||||||
|
holder.itemThumbnailView = (ImageView) convertView.findViewById(R.id.itemThumbnailView);
|
||||||
|
holder.itemVideoTitleView = (TextView) convertView.findViewById(R.id.itemVideoTitleView);
|
||||||
|
holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView);
|
||||||
|
holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView);
|
||||||
|
holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView);
|
||||||
|
convertView.setTag(holder);
|
||||||
|
} else {
|
||||||
|
holder = (ViewHolder) convertView.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.thumbnail == null) {
|
||||||
|
holder.itemThumbnailView.setImageResource(R.drawable.dummi_thumbnail);
|
||||||
|
} else {
|
||||||
|
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
|
||||||
|
}
|
||||||
|
holder.itemVideoTitleView.setText(info.title);
|
||||||
|
holder.itemUploaderView.setText(info.uploader);
|
||||||
|
holder.itemDurationView.setText(info.duration);
|
||||||
|
if(!info.upload_date.isEmpty()) {
|
||||||
|
holder.itemUploadDateView.setText(info.upload_date);
|
||||||
|
} else {
|
||||||
|
//tewak if nececeary: This is a hack preventing to have a white space in the layout :P
|
||||||
|
holder.itemUploadDateView.setText(info.view_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ViewHolder {
|
||||||
|
public ImageView itemThumbnailView;
|
||||||
|
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,22 +1,14 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.content.ContentProviderOperation;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.WindowManager;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.youtube.YoutubeExtractor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,9 +33,11 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private static final String TAG = VideoItemDetailActivity.class.toString();
|
private static final String TAG = VideoItemDetailActivity.class.toString();
|
||||||
|
|
||||||
|
VideoItemDetailFragment fragment;
|
||||||
|
|
||||||
private String videoUrl;
|
private String videoUrl;
|
||||||
private int currentStreamingService = -1;
|
private int currentStreamingService = -1;
|
||||||
private boolean isLandscape;
|
private Menu menu = null;
|
||||||
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -51,7 +45,6 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
// Show the Up button in the action bar.
|
// Show the Up button in the action bar.
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
ActionBarHandler.getHandler().setupNavMenu(this);
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -83,6 +76,10 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(extractor == null) {
|
||||||
|
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
arguments.putString(VideoItemDetailFragment.VIDEO_URL,
|
||||||
extractor.getVideoUrl(extractor.getVideoId(videoUrl)));
|
extractor.getVideoUrl(extractor.getVideoId(videoUrl)));
|
||||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
||||||
@@ -95,14 +92,27 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
||||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
||||||
}
|
}
|
||||||
// Create the detail fragment and add it to the activity
|
|
||||||
// using a fragment transaction.
|
} else {
|
||||||
VideoItemDetailFragment fragment = new VideoItemDetailFragment();
|
videoUrl = savedInstanceState.getString(VideoItemDetailFragment.VIDEO_URL);
|
||||||
fragment.setArguments(arguments);
|
currentStreamingService = savedInstanceState.getInt(VideoItemDetailFragment.STREAMING_SERVICE);
|
||||||
getSupportFragmentManager().beginTransaction()
|
arguments = savedInstanceState;
|
||||||
.add(R.id.videoitem_detail_container, fragment)
|
|
||||||
.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the detail fragment and add it to the activity
|
||||||
|
// using a fragment transaction.
|
||||||
|
fragment = new VideoItemDetailFragment();
|
||||||
|
fragment.setArguments(arguments);
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.videoitem_detail_container, fragment)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
||||||
|
outState.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
||||||
|
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,17 +131,15 @@ public class VideoItemDetailActivity extends AppCompatActivity {
|
|||||||
NavUtils.navigateUpTo(this, intent);
|
NavUtils.navigateUpTo(this, intent);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
ActionBarHandler.getHandler().onItemSelected(item, this);
|
return fragment.onOptionsItemSelected(item) ||
|
||||||
|
super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreatePanelMenu(featured, menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
fragment.onCreateOptionsMenu(menu, getMenuInflater());
|
||||||
ActionBarHandler.getHandler().setupMenu(menu, inflater, this);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,21 +2,14 @@ package org.schabi.newpipe;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.DisplayMetrics;
|
import android.support.v7.widget.SearchView;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.support.v7.widget.SearchView;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,6 +41,8 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
private String searchQuery = "";
|
private String searchQuery = "";
|
||||||
|
|
||||||
private VideoItemListFragment listFragment;
|
private VideoItemListFragment listFragment;
|
||||||
|
private VideoItemDetailFragment videoFragment = null;
|
||||||
|
Menu menu = null;
|
||||||
|
|
||||||
public class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
|
public class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
@@ -68,7 +63,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
// onQueryTextSubmit to trigger twice when focus is not cleared.
|
// onQueryTextSubmit to trigger twice when focus is not cleared.
|
||||||
// See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once
|
// See: http://stackoverflow.com/questions/17874951/searchview-onquerytextsubmit-runs-twice-while-i-pressed-once
|
||||||
getCurrentFocus().clearFocus();
|
getCurrentFocus().clearFocus();
|
||||||
hideWatermark();
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -82,13 +76,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideWatermark() {
|
|
||||||
ImageView waterMark = (ImageView) findViewById(R.id.list_view_watermark);
|
|
||||||
if(waterMark != null) {
|
|
||||||
waterMark.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
|
||||||
* device.
|
* device.
|
||||||
@@ -114,7 +101,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
searchQuery = savedInstanceState.getString(QUERY);
|
searchQuery = savedInstanceState.getString(QUERY);
|
||||||
currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
currentStreamingServiceId = savedInstanceState.getInt(STREAMING_SERVICE);
|
||||||
if(!searchQuery.isEmpty()) {
|
if(!searchQuery.isEmpty()) {
|
||||||
hideWatermark();
|
|
||||||
listFragment.search(searchQuery);
|
listFragment.search(searchQuery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,9 +125,6 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
searchView.setIconifiedByDefault(false);
|
searchView.setIconifiedByDefault(false);
|
||||||
searchView.setIconified(false);
|
searchView.setIconified(false);
|
||||||
searchView.setOnQueryTextListener(new SearchVideoQueryListener());
|
searchView.setOnQueryTextListener(new SearchVideoQueryListener());
|
||||||
|
|
||||||
ActionBarHandler.getHandler().setupNavMenu(this);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsActivity.initSettings(this);
|
SettingsActivity.initSettings(this);
|
||||||
@@ -167,10 +150,17 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id);
|
arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id);
|
||||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpage_url);
|
arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpage_url);
|
||||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId);
|
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId);
|
||||||
VideoItemDetailFragment fragment = new VideoItemDetailFragment();
|
videoFragment = new VideoItemDetailFragment();
|
||||||
fragment.setArguments(arguments);
|
videoFragment.setArguments(arguments);
|
||||||
|
videoFragment.setOnInvokeCreateOptionsMenuListener(new VideoItemDetailFragment.OnInvokeCreateOptionsMenuListener() {
|
||||||
|
@Override
|
||||||
|
public void createOptionsMenu() {
|
||||||
|
menu.clear();
|
||||||
|
onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
});
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.replace(R.id.videoitem_detail_container, fragment)
|
.replace(R.id.videoitem_detail_container, videoFragment)
|
||||||
.commit();
|
.commit();
|
||||||
} else {
|
} else {
|
||||||
// In single-pane mode, simply start the detail activity
|
// In single-pane mode, simply start the detail activity
|
||||||
@@ -184,10 +174,11 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean onCreatePanelMenu(int featured, Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreatePanelMenu(featured, menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
|
this.menu = menu;
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
if(findViewById(R.id.videoitem_detail_container) == null) {
|
if(findViewById(R.id.videoitem_detail_container) == null) {
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.videoitem_list, menu);
|
inflater.inflate(R.menu.videoitem_list, menu);
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
SearchView searchView = (SearchView) searchItem.getActionView();
|
SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
@@ -195,9 +186,10 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
searchView.setOnQueryTextListener(
|
searchView.setOnQueryTextListener(
|
||||||
new SearchVideoQueryListener());
|
new SearchVideoQueryListener());
|
||||||
|
|
||||||
|
} else if (videoFragment != null){
|
||||||
|
videoFragment.onCreateOptionsMenu(menu, inflater);
|
||||||
} else {
|
} else {
|
||||||
MenuInflater inflater = getMenuInflater();
|
inflater.inflate(R.menu.videoitem_two_pannel, menu);
|
||||||
ActionBarHandler.getHandler().setupMenu(menu, inflater, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -210,8 +202,8 @@ public class VideoItemListActivity extends AppCompatActivity
|
|||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
ActionBarHandler.getHandler().onItemSelected(item, this);
|
return videoFragment.onOptionsItemSelected(item) ||
|
||||||
return super.onOptionsItemSelected(item);
|
super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,6 @@ import android.widget.ListView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
|
||||||
@@ -54,14 +53,14 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
|
|
||||||
private class ResultRunnable implements Runnable {
|
private class ResultRunnable implements Runnable {
|
||||||
private SearchEngine.Result result;
|
private SearchEngine.Result result;
|
||||||
private int reuqestId;
|
private int requestId;
|
||||||
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.reuqestId = requestId;
|
this.requestId = requestId;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
updateListOnResult(result, reuqestId);
|
updateListOnResult(result, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +212,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
loadThumbsThread = new Thread(loadThumbsRunnable);
|
loadThumbsThread = new Thread(loadThumbsRunnable);
|
||||||
loadThumbsThread.start();
|
loadThumbsThread.start();
|
||||||
} catch(java.lang.IllegalStateException e) {
|
} catch(java.lang.IllegalStateException e) {
|
||||||
Log.w(TAG, "Trying to set value while activity is not existing anymore.");
|
Log.w(TAG, "Trying to set value while activity doesn't exist anymore.");
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -230,7 +229,7 @@ public class VideoItemListFragment extends ListFragment {
|
|||||||
}
|
}
|
||||||
if(searchThread != null) {
|
if(searchThread != null) {
|
||||||
searchRunnable.terminate();
|
searchRunnable.terminate();
|
||||||
// No need to join, since we don't realy terminate the thread. We just demand
|
// No need to join, since we don't really terminate the thread. We just demand
|
||||||
// it to post its result runnable into the gui main loop.
|
// it to post its result runnable into the gui main loop.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,14 +6,12 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by the-scrabi on 11.08.15.
|
* Created by Christian Schabesberger on 11.08.15.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
* VideoListAdapter.java is part of NewPipe.
|
* VideoListAdapter.java is part of NewPipe.
|
||||||
@@ -33,18 +31,20 @@ import java.util.Vector;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class VideoListAdapter extends BaseAdapter {
|
public class VideoListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
private static final String TAG = VideoListAdapter.class.toString();
|
private static final String TAG = VideoListAdapter.class.toString();
|
||||||
private LayoutInflater inflater;
|
|
||||||
|
private Context context;
|
||||||
|
private VideoInfoItemViewCreator viewCreator;
|
||||||
private Vector<VideoInfoItem> videoList = new Vector<>();
|
private Vector<VideoInfoItem> videoList = new Vector<>();
|
||||||
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
|
||||||
VideoItemListFragment videoListFragment;
|
VideoItemListFragment videoListFragment;
|
||||||
ListView listView;
|
ListView listView;
|
||||||
|
|
||||||
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
||||||
inflater = LayoutInflater.from(context);
|
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
|
||||||
this.videoListFragment = videoListFragment;
|
this.videoListFragment = videoListFragment;
|
||||||
this.listView = videoListFragment.getListView();
|
this.listView = videoListFragment.getListView();
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVideoList(Vector<VideoInfoItem> videos) {
|
public void addVideoList(Vector<VideoInfoItem> videos) {
|
||||||
@@ -96,30 +96,7 @@ public class VideoListAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
ViewHolder holder;
|
convertView = viewCreator.getViewByVideoInfoItem(convertView, parent, videoList.get(position));
|
||||||
if(convertView == null) {
|
|
||||||
convertView = inflater.inflate(R.layout.video_item, parent, false);
|
|
||||||
holder = new ViewHolder();
|
|
||||||
holder.itemThumbnailView = (ImageView) convertView.findViewById(R.id.itemThumbnailView);
|
|
||||||
holder.itemVideoTitleView = (TextView) convertView.findViewById(R.id.itemVideoTitleView);
|
|
||||||
holder.itemUploaderView = (TextView) convertView.findViewById(R.id.itemUploaderView);
|
|
||||||
holder.itemDurationView = (TextView) convertView.findViewById(R.id.itemDurationView);
|
|
||||||
holder.itemUploadDateView = (TextView) convertView.findViewById(R.id.itemUploadDateView);
|
|
||||||
convertView.setTag(holder);
|
|
||||||
} else {
|
|
||||||
holder = (ViewHolder) convertView.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Context context = parent.getContext();
|
|
||||||
if(videoList.get(position).thumbnail == null) {
|
|
||||||
holder.itemThumbnailView.setImageResource(R.drawable.dummi_thumbnail);
|
|
||||||
} else {
|
|
||||||
holder.itemThumbnailView.setImageBitmap(videoList.get(position).thumbnail);
|
|
||||||
}
|
|
||||||
holder.itemVideoTitleView.setText(videoList.get(position).title);
|
|
||||||
holder.itemUploaderView.setText(videoList.get(position).uploader);
|
|
||||||
holder.itemDurationView.setText(videoList.get(position).duration);
|
|
||||||
holder.itemUploadDateView.setText(videoList.get(position).upload_date);
|
|
||||||
|
|
||||||
if(listView.isItemChecked(position)) {
|
if(listView.isItemChecked(position)) {
|
||||||
convertView.setBackgroundColor(context.getResources().getColor(R.color.primaryColorYoutube));
|
convertView.setBackgroundColor(context.getResources().getColor(R.color.primaryColorYoutube));
|
||||||
@@ -129,9 +106,4 @@ public class VideoListAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ViewHolder {
|
|
||||||
public ImageView itemThumbnailView;
|
|
||||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView;
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,30 +1,29 @@
|
|||||||
package org.schabi.newpipe.youtube;
|
package org.schabi.newpipe.youtube;
|
||||||
import org.jsoup.nodes.Element;
|
|
||||||
import org.schabi.newpipe.Downloader;
|
|
||||||
import org.schabi.newpipe.Extractor;
|
|
||||||
import org.schabi.newpipe.VideoInfo;
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.parser.Parser;
|
||||||
|
import org.mozilla.javascript.Context;
|
||||||
|
import org.mozilla.javascript.Function;
|
||||||
|
import org.mozilla.javascript.ScriptableObject;
|
||||||
|
import org.schabi.newpipe.Downloader;
|
||||||
|
import org.schabi.newpipe.Extractor;
|
||||||
|
import org.schabi.newpipe.VideoInfo;
|
||||||
|
import org.schabi.newpipe.VideoInfoItem;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import org.json.JSONObject;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.jsoup.Jsoup;
|
|
||||||
import org.jsoup.nodes.Document;
|
|
||||||
import org.jsoup.parser.Parser;
|
|
||||||
|
|
||||||
import org.mozilla.javascript.Context;
|
|
||||||
import org.mozilla.javascript.Function;
|
|
||||||
import org.mozilla.javascript.ScriptableObject;
|
|
||||||
import org.schabi.newpipe.VideoInfoItem;
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 06.08.15.
|
* Created by Christian Schabesberger on 06.08.15.
|
||||||
@@ -48,8 +47,6 @@ import org.xmlpull.v1.XmlPullParser;
|
|||||||
|
|
||||||
public class YoutubeExtractor implements Extractor {
|
public class YoutubeExtractor implements Extractor {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static final String TAG = YoutubeExtractor.class.toString();
|
private static final String TAG = YoutubeExtractor.class.toString();
|
||||||
|
|
||||||
// These lists only contain itag formats that are supported by the common Android Video player.
|
// These lists only contain itag formats that are supported by the common Android Video player.
|
||||||
@@ -93,7 +90,7 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decryptoinCode = "";
|
private String decryptionCode = "";
|
||||||
private static final String DECRYPTION_FUNC_NAME="decrypt";
|
private static final String DECRYPTION_FUNC_NAME="decrypt";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,7 +98,12 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
try {
|
try {
|
||||||
URI uri = new URI(videoUrl);
|
URI uri = new URI(videoUrl);
|
||||||
if(uri.getHost().contains("youtube")) {
|
if(uri.getHost().contains("youtube")) {
|
||||||
String query = uri.getQuery();
|
String query = uri.getFragment();
|
||||||
|
if(query == null) {
|
||||||
|
query = uri.getQuery();
|
||||||
|
} else {
|
||||||
|
query = query.replace("/watch?", "");
|
||||||
|
}
|
||||||
String queryElements[] = query.split("&");
|
String queryElements[] = query.split("&");
|
||||||
Map<String, String> queryArguments = new HashMap<>();
|
Map<String, String> queryArguments = new HashMap<>();
|
||||||
for (String e : queryElements) {
|
for (String e : queryElements) {
|
||||||
@@ -156,7 +158,7 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
JSONObject playerArgs = null;
|
JSONObject playerArgs = null;
|
||||||
JSONObject ytAssets = null;
|
JSONObject ytAssets = null;
|
||||||
String dashManifest = "";
|
String dashManifest;
|
||||||
{
|
{
|
||||||
Pattern p = Pattern.compile("ytplayer.config\\s*=\\s*(\\{.*?\\});");
|
Pattern p = Pattern.compile("ytplayer.config\\s*=\\s*(\\{.*?\\});");
|
||||||
Matcher m = p.matcher(site);
|
Matcher m = p.matcher(site);
|
||||||
@@ -183,18 +185,23 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
videoInfo.thumbnail_url = playerArgs.getString("thumbnail_url");
|
videoInfo.thumbnail_url = playerArgs.getString("thumbnail_url");
|
||||||
videoInfo.duration = playerArgs.getInt("length_seconds");
|
videoInfo.duration = playerArgs.getInt("length_seconds");
|
||||||
videoInfo.average_rating = playerArgs.getString("avg_rating");
|
videoInfo.average_rating = playerArgs.getString("avg_rating");
|
||||||
// View Count will be extracted from html
|
|
||||||
dashManifest = playerArgs.getString("dashmpd");
|
|
||||||
String playerUrl = ytAssets.getString("js");
|
String playerUrl = ytAssets.getString("js");
|
||||||
if(playerUrl.startsWith("//")) {
|
if(playerUrl.startsWith("//")) {
|
||||||
playerUrl = "https:" + playerUrl;
|
playerUrl = "https:" + playerUrl;
|
||||||
}
|
}
|
||||||
if(decryptoinCode.isEmpty()) {
|
if(decryptionCode.isEmpty()) {
|
||||||
decryptoinCode = loadDecryptioinCode(playerUrl);
|
decryptionCode = loadDecryptionCode(playerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract audio
|
// extract audio
|
||||||
videoInfo.audioStreams = parseDashManifest(dashManifest, decryptoinCode);
|
try {
|
||||||
|
dashManifest = playerArgs.getString("dashmpd");
|
||||||
|
videoInfo.audioStreams = parseDashManifest(dashManifest, decryptionCode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
//todo: check if the following statement is true
|
||||||
|
Log.e(TAG, "Dash manifest seems not to bee available.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
// extract video stream url
|
// extract video stream url
|
||||||
@@ -213,10 +220,10 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
|
|
||||||
// if video has a signature: decrypt it and add it to the url
|
// if video has a signature: decrypt it and add it to the url
|
||||||
if(tags.get("s") != null) {
|
if(tags.get("s") != null) {
|
||||||
if(decryptoinCode.isEmpty()) {
|
if(decryptionCode.isEmpty()) {
|
||||||
decryptoinCode = loadDecryptioinCode(playerUrl);
|
decryptionCode = loadDecryptionCode(playerUrl);
|
||||||
}
|
}
|
||||||
streamUrl = streamUrl + "&signature=" + decryptSignature(tags.get("s"), decryptoinCode);
|
streamUrl = streamUrl + "&signature=" + decryptSignature(tags.get("s"), decryptionCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resolveFormat(itag) != -1) {
|
if(resolveFormat(itag) != -1) {
|
||||||
@@ -236,7 +243,7 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------
|
//-------------------------------
|
||||||
// extrating from html page
|
// extracting from html page
|
||||||
//-------------------------------
|
//-------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -297,32 +304,28 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
// view count
|
// view count
|
||||||
videoInfo.view_count = doc.select("div[class=\"watch-view-count\"]").first().text();
|
videoInfo.view_count = doc.select("div[class=\"watch-view-count\"]").first().text();
|
||||||
|
|
||||||
/* todo finish this code
|
|
||||||
|
|
||||||
// next video
|
// next video
|
||||||
videoInfo.nextVideo = extractVideoInfoItem(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
videoInfo.nextVideo = extractVideoInfoItem(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
||||||
.select("li").first());
|
.select("li").first());
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
// related videos
|
// related videos
|
||||||
|
videoInfo.relatedVideos = new Vector<>();
|
||||||
for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
|
for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
|
||||||
// first check if we have a playlist. If so leave them out
|
// first check if we have a playlist. If so leave them out
|
||||||
if(li.select("a[class*=\"content-link\"]").first() != null) {
|
if(li.select("a[class*=\"content-link\"]").first() != null) {
|
||||||
//videoInfo.relatedVideos.add(extractVideoInfoItem(li));
|
videoInfo.relatedVideos.add(extractVideoInfoItem(li));
|
||||||
//i++;
|
i++;
|
||||||
//Log.d(TAG, Integer.toString(i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
return videoInfo;
|
return videoInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
|
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
|
||||||
if(!dashManifest.contains("/signature/")) {
|
if(!dashManifest.contains("/signature/")) {
|
||||||
String encryptedSig = "";
|
String encryptedSig = "";
|
||||||
String decryptedSig = "";
|
String decryptedSig;
|
||||||
try {
|
try {
|
||||||
Pattern p = Pattern.compile("/s/([a-fA-F0-9\\.]+)");
|
Pattern p = Pattern.compile("/s/([a-fA-F0-9\\.]+)");
|
||||||
Matcher m = p.matcher(dashManifest);
|
Matcher m = p.matcher(dashManifest);
|
||||||
@@ -406,11 +409,10 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
info.title = li.select("span[class=\"title\"]").first()
|
|
||||||
.text();
|
|
||||||
|
|
||||||
|
info.title = li.select("span[class=\"title\"]").first().text();
|
||||||
|
info.view_count = li.select("span[class*=\"view-count\"]").first().text();
|
||||||
info.uploader = li.select("span[class=\"g-hovercard\"]").first().text();
|
info.uploader = li.select("span[class=\"g-hovercard\"]").first().text();
|
||||||
|
|
||||||
info.duration = li.select("span[class=\"video-time\"]").first().text();
|
info.duration = li.select("span[class=\"video-time\"]").first().text();
|
||||||
|
|
||||||
Element img = li.select("img").first();
|
Element img = li.select("img").first();
|
||||||
@@ -421,7 +423,9 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
if(info.thumbnail_url.contains(".gif")) {
|
if(info.thumbnail_url.contains(".gif")) {
|
||||||
info.thumbnail_url = img.attr("data-thumb");
|
info.thumbnail_url = img.attr("data-thumb");
|
||||||
}
|
}
|
||||||
|
if(info.thumbnail_url.startsWith("//")) {
|
||||||
|
info.thumbnail_url = "https:" + info.thumbnail_url;
|
||||||
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +442,7 @@ public class YoutubeExtractor implements Extractor {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String loadDecryptioinCode(String playerUrl) {
|
private String loadDecryptionCode(String playerUrl) {
|
||||||
String playerCode = Downloader.download(playerUrl);
|
String playerCode = Downloader.download(playerUrl);
|
||||||
String decryptionFuncName = "";
|
String decryptionFuncName = "";
|
||||||
String decryptionFunc = "";
|
String decryptionFunc = "";
|
||||||
|
@@ -1,18 +1,17 @@
|
|||||||
package org.schabi.newpipe.youtube;
|
package org.schabi.newpipe.youtube;
|
||||||
|
|
||||||
import org.schabi.newpipe.Downloader;
|
|
||||||
import org.schabi.newpipe.SearchEngine;
|
|
||||||
import org.schabi.newpipe.VideoInfoItem;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup;
|
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.SearchEngine;
|
||||||
|
import org.schabi.newpipe.VideoInfoItem;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 09.08.15.
|
* Created by Christian Schabesberger on 09.08.15.
|
||||||
|
@@ -42,12 +42,6 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_weight="4">
|
android:layout_weight="4">
|
||||||
|
|
||||||
<ImageView android:id="@+id/list_view_watermark"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:src="@drawable/new_pipe_watermark"/>
|
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/videoitem_detail_container"
|
<FrameLayout android:id="@+id/videoitem_detail_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
208
app/src/main/res/layout-sw600dp/fragment_videoitem_detail.xml
Normal file
@@ -3,12 +3,6 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView android:id="@+id/list_view_watermark"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:src="@drawable/new_pipe_watermark"/>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/videoitem_list"
|
android:id="@+id/videoitem_list"
|
||||||
|
@@ -7,8 +7,9 @@
|
|||||||
android:padding="6dp">
|
android:padding="6dp">
|
||||||
|
|
||||||
<ImageView android:id="@+id/itemThumbnailView"
|
<ImageView android:id="@+id/itemThumbnailView"
|
||||||
android:layout_width="125dp"
|
android:layout_width="142dp"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:src="@drawable/dummi_thumbnail"/>
|
android:src="@drawable/dummi_thumbnail"/>
|
||||||
|
9
app/src/main/res/menu/videoitem_two_pannel.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item android:id="@+id/action_settings"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:title="@string/settings"/>
|
||||||
|
|
||||||
|
</menu>
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -3,7 +3,7 @@
|
|||||||
<string name="app_name">NewPipe</string>
|
<string name="app_name">NewPipe</string>
|
||||||
<string name="title_videoitem_detail">NewPipe</string>
|
<string name="title_videoitem_detail">NewPipe</string>
|
||||||
<string name="nothingFound">Nichts gefunden</string>
|
<string name="nothingFound">Nichts gefunden</string>
|
||||||
<string name="viewSufix">views</string>
|
<string name="viewSufix">Aufrufe</string>
|
||||||
<string name="uploadDatePrefix">Hochgeladen am: </string>
|
<string name="uploadDatePrefix">Hochgeladen am: </string>
|
||||||
<string name="noPlayerFound">Keinen Streamplayer gefunden. Vielleicht möchtest du einen installieren.</string>
|
<string name="noPlayerFound">Keinen Streamplayer gefunden. Vielleicht möchtest du einen installieren.</string>
|
||||||
<string name="installStreamPlayer">Jetzt installieren</string>
|
<string name="installStreamPlayer">Jetzt installieren</string>
|
||||||
@@ -45,4 +45,7 @@
|
|||||||
<item>Video</item>
|
<item>Video</item>
|
||||||
<item>Audio</item>
|
<item>Audio</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string name="nextVideoTitle">Nächstes Video</string>
|
||||||
|
<string name="showNextVideoTitle">Zeige \"Nächstes Video\" Auswahl.</string>
|
||||||
|
<string name="urlNotSupportedText">Url wird nicht unterstützt.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -29,4 +29,20 @@
|
|||||||
<string name="autoPlayThroughIntentTitle">Аутопуштање преко Интента</string>
|
<string name="autoPlayThroughIntentTitle">Аутопуштање преко Интента</string>
|
||||||
<string name="autoPlayThroughIntentSummary">Аутоматски почиње пушта видео по позиву из друге апликације.</string>
|
<string name="autoPlayThroughIntentSummary">Аутоматски почиње пушта видео по позиву из друге апликације.</string>
|
||||||
<string name="defaultResolutionPreferenceTitle">Подразумевана резолуција</string>
|
<string name="defaultResolutionPreferenceTitle">Подразумевана резолуција</string>
|
||||||
</resources>
|
<string name="playWithKodiTitle">Пусти помоћу Кодија</string>
|
||||||
|
<string name="koreNotFound">Апликација Кор није нађена. Кор (Kore) је потребан да бисте пуштали видее у Коди медија центру.</string>
|
||||||
|
<string name="installeKore">Инсталирај Кор</string>
|
||||||
|
<string name="fdroidKoreUrl">https://f-droid.org/repository/browse/?fdfilter=Kore&fdid=org.xbmc.kore</string>
|
||||||
|
<string name="showPlayWithKodiTitle">Прикажи „Пусти помоћу Кодија“</string>
|
||||||
|
<string name="showPlayWithKodiSummary">Приказ опције за пуштање видеа у Коди медија центру.</string>
|
||||||
|
<string name="leftPlayButtonTitle">Прикажи дугме за пуштање на левој страни.</string>
|
||||||
|
<string name="playAudio">Аудио</string>
|
||||||
|
<string name="defaultAudioFormatTitle">Подразумевани формат звука</string>
|
||||||
|
<string name="webMAudioDescription">WebM - слободни формат</string>
|
||||||
|
<string name="m4aAudioDescription">m4a - бољи квалитет</string>
|
||||||
|
<string name="downloadDialogTitle">Преузми</string>
|
||||||
|
<string-array name="downloadOptions">
|
||||||
|
<item>Видео</item>
|
||||||
|
<item>Аудио</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
@@ -23,4 +23,5 @@
|
|||||||
<item>m4a</item>
|
<item>m4a</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string name="defaultAudioFormat">m4a</string>
|
<string name="defaultAudioFormat">m4a</string>
|
||||||
|
<string name="showNextVideo">show_next_video</string>
|
||||||
</resources>
|
</resources>
|