Merge branch 'advancedErrorHandling'
This commit is contained in:
commit
3aecd15916
35 changed files with 1234 additions and 416 deletions
|
@ -42,4 +42,5 @@ dependencies {
|
||||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||||
|
compile 'org.apache.directory.studio:org.apache.commons.lang:2.6'
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ package org.schabi.newpipe.extractor.youtube;
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||||
|
import org.schabi.newpipe.extractor.SearchResult;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
import org.schabi.newpipe.extractor.SearchEngine;
|
import org.schabi.newpipe.extractor.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeSearchEngine;
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeSearchEngine;
|
||||||
import org.schabi.newpipe.Downloader;
|
import org.schabi.newpipe.Downloader;
|
||||||
|
@ -30,7 +32,7 @@ import java.util.ArrayList;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class YoutubeSearchEngineTest extends AndroidTestCase {
|
public class YoutubeSearchEngineTest extends AndroidTestCase {
|
||||||
private SearchEngine.Result result;
|
private SearchResult result;
|
||||||
private ArrayList<String> suggestionReply;
|
private ArrayList<String> suggestionReply;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,12 +41,13 @@ public class YoutubeSearchEngineTest extends AndroidTestCase {
|
||||||
SearchEngine engine = new YoutubeSearchEngine();
|
SearchEngine engine = new YoutubeSearchEngine();
|
||||||
|
|
||||||
result = engine.search("bla",
|
result = engine.search("bla",
|
||||||
0, "de", new Downloader());
|
0, "de", new Downloader()).getSearchResult();
|
||||||
suggestionReply = engine.suggestionList("hello","de",new Downloader());
|
suggestionReply = engine.suggestionList("hello","de",new Downloader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIfNoErrorOccur() {
|
public void testIfNoErrorOccur() {
|
||||||
assertEquals(result.errorMessage, "");
|
assertTrue(result.errors.isEmpty() ? "" : ExceptionUtils.getStackTrace(result.errors.get(0))
|
||||||
|
,result.errors.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIfListIsNotEmpty() {
|
public void testIfListIsNotEmpty() {
|
||||||
|
@ -52,44 +55,44 @@ public class YoutubeSearchEngineTest extends AndroidTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testItemsHaveTitle() {
|
public void testItemsHaveTitle() {
|
||||||
for(VideoPreviewInfo i : result.resultList) {
|
for(StreamPreviewInfo i : result.resultList) {
|
||||||
assertEquals(i.title.isEmpty(), false);
|
assertEquals(i.title.isEmpty(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testItemsHaveUploader() {
|
public void testItemsHaveUploader() {
|
||||||
for(VideoPreviewInfo i : result.resultList) {
|
for(StreamPreviewInfo i : result.resultList) {
|
||||||
assertEquals(i.uploader.isEmpty(), false);
|
assertEquals(i.uploader.isEmpty(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testItemsHaveRightDuration() {
|
public void testItemsHaveRightDuration() {
|
||||||
for(VideoPreviewInfo i : result.resultList) {
|
for(StreamPreviewInfo i : result.resultList) {
|
||||||
assertTrue(i.duration, i.duration.contains(":"));
|
assertTrue(i.duration, i.duration.contains(":"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testItemsHaveRightThumbnail() {
|
public void testItemsHaveRightThumbnail() {
|
||||||
for (VideoPreviewInfo i : result.resultList) {
|
for (StreamPreviewInfo i : result.resultList) {
|
||||||
assertTrue(i.thumbnail_url, i.thumbnail_url.contains("https://"));
|
assertTrue(i.thumbnail_url, i.thumbnail_url.contains("https://"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testItemsHaveRightVideoUrl() {
|
public void testItemsHaveRightVideoUrl() {
|
||||||
for (VideoPreviewInfo i : result.resultList) {
|
for (StreamPreviewInfo i : result.resultList) {
|
||||||
assertTrue(i.webpage_url, i.webpage_url.contains("https://"));
|
assertTrue(i.webpage_url, i.webpage_url.contains("https://"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testViewCount() {
|
public void testViewCount() {
|
||||||
/*
|
/*
|
||||||
for(VideoPreviewInfo i : result.resultList) {
|
for(StreamPreviewInfo i : result.resultList) {
|
||||||
assertTrue(Long.toString(i.view_count), i.view_count != -1);
|
assertTrue(Long.toString(i.view_count), i.view_count != -1);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// that specific link used for this test, there are no videos with less
|
// that specific link used for this test, there are no videos with less
|
||||||
// than 10.000 views, so we can test against that.
|
// than 10.000 views, so we can test against that.
|
||||||
for(VideoPreviewInfo i : result.resultList) {
|
for(StreamPreviewInfo i : result.resultList) {
|
||||||
assertTrue(Long.toString(i.view_count), i.view_count >= 10000);
|
assertTrue(Long.toString(i.view_count), i.view_count >= 10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.schabi.newpipe.Downloader;
|
||||||
import org.schabi.newpipe.extractor.ExtractionException;
|
import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.VideoInfo;
|
import org.schabi.newpipe.extractor.StreamInfo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetVideoStreams() throws ParsingException {
|
public void testGetVideoStreams() throws ParsingException {
|
||||||
for(VideoInfo.VideoStream s : extractor.getVideoStreams()) {
|
for(StreamInfo.VideoStream s : extractor.getVideoStreams()) {
|
||||||
assertTrue(s.url,
|
assertTrue(s.url,
|
||||||
s.url.contains("https://"));
|
s.url.contains("https://"));
|
||||||
assertTrue(s.resolution.length() > 0);
|
assertTrue(s.resolution.length() > 0);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.test.AndroidTestCase;
|
||||||
import org.schabi.newpipe.Downloader;
|
import org.schabi.newpipe.Downloader;
|
||||||
import org.schabi.newpipe.extractor.ExtractionException;
|
import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.VideoInfo;
|
import org.schabi.newpipe.extractor.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -73,7 +73,7 @@ public class YoutubeStreamExtractorRestrictedTest extends AndroidTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetVideoStreams() throws ParsingException {
|
public void testGetVideoStreams() throws ParsingException {
|
||||||
for(VideoInfo.VideoStream s : extractor.getVideoStreams()) {
|
for(StreamInfo.VideoStream s : extractor.getVideoStreams()) {
|
||||||
assertTrue(s.url,
|
assertTrue(s.url,
|
||||||
s.url.contains("https://"));
|
s.url.contains("https://"));
|
||||||
assertTrue(s.resolution.length() > 0);
|
assertTrue(s.resolution.length() > 0);
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
<?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"
|
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.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:logo="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:logo="@mipmap/ic_launcher"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="AllowBackup">
|
tools:ignore="AllowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name=".VideoItemListActivity"
|
android:name=".VideoItemListActivity"
|
||||||
android:label="@string/app_name"
|
android:configChanges="orientation|screenSize"
|
||||||
android:configChanges="orientation|screenSize">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
@ -26,10 +28,10 @@
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".VideoItemDetailActivity"
|
android:name=".VideoItemDetailActivity"
|
||||||
android:label="@string/title_videoitem_detail"
|
|
||||||
android:theme="@style/AppTheme"
|
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:screenOrientation="portrait">
|
android:label="@string/title_videoitem_detail"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
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" />
|
||||||
|
@ -76,20 +78,21 @@
|
||||||
<data android:scheme="vnd.youtube.launch" />
|
<data android:scheme="vnd.youtube.launch" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".PlayVideoActivity"
|
<activity
|
||||||
|
android:name=".PlayVideoActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
android:theme="@style/VideoPlayerTheme"
|
|
||||||
android:parentActivityName=".VideoItemDetailActivity"
|
android:parentActivityName=".VideoItemDetailActivity"
|
||||||
tools:ignore="UnusedAttribute">
|
android:theme="@style/VideoPlayerTheme"
|
||||||
</activity>
|
tools:ignore="UnusedAttribute"></activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".BackgroundPlayer"
|
android:name=".BackgroundPlayer"
|
||||||
android:label="@string/background_player_name"
|
android:exported="false"
|
||||||
android:exported="false" />
|
android:label="@string/background_player_name" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
android:label="@string/settings_activity_title" >
|
android:label="@string/settings_activity_title"></activity>
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".PanicResponderActivity"
|
android:name=".PanicResponderActivity"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
|
@ -97,11 +100,15 @@
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ExitActivity"
|
android:name=".ExitActivity"
|
||||||
|
android:label="@string/general_error"
|
||||||
android:theme="@android:style/Theme.NoDisplay" />
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
|
<activity android:name=".ErrorActivity"></activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import android.view.MenuItem;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.VideoInfo;
|
import org.schabi.newpipe.extractor.StreamInfo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class ActionBarHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupStreamList(final List<VideoInfo.VideoStream> videoStreams) {
|
public void setupStreamList(final List<StreamInfo.VideoStream> videoStreams) {
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
selectedVideoStream = 0;
|
selectedVideoStream = 0;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class ActionBarHandler {
|
||||||
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
||||||
String[] itemArray = new String[videoStreams.size()];
|
String[] itemArray = new String[videoStreams.size()];
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
VideoInfo.VideoStream item = videoStreams.get(i);
|
StreamInfo.VideoStream item = videoStreams.get(i);
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
||||||
}
|
}
|
||||||
int defaultResolution = getDefaultResolution(videoStreams);
|
int defaultResolution = getDefaultResolution(videoStreams);
|
||||||
|
@ -108,13 +108,13 @@ class ActionBarHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int getDefaultResolution(final List<VideoInfo.VideoStream> videoStreams) {
|
private int getDefaultResolution(final List<StreamInfo.VideoStream> videoStreams) {
|
||||||
String defaultResolution = defaultPreferences
|
String defaultResolution = defaultPreferences
|
||||||
.getString(activity.getString(R.string.default_resolution_key),
|
.getString(activity.getString(R.string.default_resolution_key),
|
||||||
activity.getString(R.string.default_resolution_value));
|
activity.getString(R.string.default_resolution_value));
|
||||||
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
for (int i = 0; i < videoStreams.size(); i++) {
|
||||||
VideoInfo.VideoStream item = videoStreams.get(i);
|
StreamInfo.VideoStream item = videoStreams.get(i);
|
||||||
if (defaultResolution.equals(item.resolution)) {
|
if (defaultResolution.equals(item.resolution)) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,12 @@ package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton:
|
* Singleton:
|
||||||
* Used to send data between certain Activity/Services within the same process.
|
* Used to send data between certain Activity/Services within the same process.
|
||||||
* This can be considered as hack inside the Android universe. **/
|
* This can be considered as an ugly hack inside the Android universe. **/
|
||||||
public class ActivityCommunicator {
|
public class ActivityCommunicator {
|
||||||
|
|
||||||
private static ActivityCommunicator activityCommunicator = null;
|
private static ActivityCommunicator activityCommunicator = null;
|
||||||
|
@ -39,4 +41,9 @@ public class ActivityCommunicator {
|
||||||
|
|
||||||
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
|
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
|
||||||
public volatile Bitmap backgroundPlayerThumbnail;
|
public volatile Bitmap backgroundPlayerThumbnail;
|
||||||
|
|
||||||
|
// Sent from any activity to ErrorActivity.
|
||||||
|
public volatile List<Exception> errorList;
|
||||||
|
public volatile Class returnActivity;
|
||||||
|
public volatile ErrorActivity.ErrorInfo errorInfo;
|
||||||
}
|
}
|
||||||
|
|
385
app/src/main/java/org/schabi/newpipe/ErrorActivity.java
Normal file
385
app/src/main/java/org/schabi/newpipe/ErrorActivity.java
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
|
||||||
|
|
||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.NavUtils;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.schabi.newpipe.extractor.Parser;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 24.10.15.
|
||||||
|
* <p>
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* ErrorActivity.java is part of NewPipe.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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 ErrorActivity extends AppCompatActivity {
|
||||||
|
public static class ErrorInfo {
|
||||||
|
public int userAction;
|
||||||
|
public String request;
|
||||||
|
public String serviceName;
|
||||||
|
public int message;
|
||||||
|
|
||||||
|
public static ErrorInfo make(int userAction, String serviceName, String request, int message) {
|
||||||
|
ErrorInfo info = new ErrorInfo();
|
||||||
|
info.userAction = userAction;
|
||||||
|
info.serviceName = serviceName;
|
||||||
|
info.request = request;
|
||||||
|
info.message = message;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String TAG = ErrorActivity.class.toString();
|
||||||
|
public static final int SEARCHED = 0;
|
||||||
|
public static final int REQUESTED_STREAM = 1;
|
||||||
|
public static final int GET_SUGGESTIONS = 2;
|
||||||
|
public static final String SEARCHED_STRING = "searched";
|
||||||
|
public static final String REQUESTED_STREAM_STRING = "requested stream";
|
||||||
|
public static final String GET_SUGGESTIONS_STRING = "get suggestions";
|
||||||
|
|
||||||
|
public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org";
|
||||||
|
public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME;
|
||||||
|
|
||||||
|
private List<Exception> errorList;
|
||||||
|
private ErrorInfo errorInfo;
|
||||||
|
private Class returnActivity;
|
||||||
|
private String currentTimeStamp;
|
||||||
|
private String globIpRange;
|
||||||
|
Thread globIpRangeThread = null;
|
||||||
|
|
||||||
|
// views
|
||||||
|
private TextView errorView;
|
||||||
|
private EditText userCommentBox;
|
||||||
|
private Button reportButton;
|
||||||
|
private TextView infoView;
|
||||||
|
private TextView errorMessageView;
|
||||||
|
|
||||||
|
public static void reportError(final Context context, final List<Exception> el,
|
||||||
|
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||||
|
|
||||||
|
if (rootView != null) {
|
||||||
|
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
|
||||||
|
.setActionTextColor(Color.YELLOW)
|
||||||
|
.setAction(R.string.error_snackbar_action, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||||
|
ac.errorList = el;
|
||||||
|
ac.returnActivity = returnAcitivty;
|
||||||
|
ac.errorInfo = errorInfo;
|
||||||
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
} else {
|
||||||
|
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||||
|
ac.errorList = el;
|
||||||
|
ac.returnActivity = returnAcitivty;
|
||||||
|
ac.errorInfo = errorInfo;
|
||||||
|
Intent intent = new Intent(context, ErrorActivity.class);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reportError(final Context context, final Exception e,
|
||||||
|
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||||
|
List<Exception> el = new Vector<>();
|
||||||
|
el.add(e);
|
||||||
|
reportError(context, el, returnAcitivty, rootView, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// async call
|
||||||
|
public static void reportError(Handler handler, final Context context, final Exception e,
|
||||||
|
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
||||||
|
List<Exception> el = new Vector<>();
|
||||||
|
el.add(e);
|
||||||
|
reportError(handler, context, el, returnAcitivty, rootView, errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// async call
|
||||||
|
public static void reportError(Handler handler, final Context context, final List<Exception> el,
|
||||||
|
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
reportError(context, el, returnAcitivty, rootView, errorInfo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_error);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
|
||||||
|
errorList = ac.errorList;
|
||||||
|
returnActivity = ac.returnActivity;
|
||||||
|
errorInfo = ac.errorInfo;
|
||||||
|
|
||||||
|
reportButton = (Button) findViewById(R.id.errorReportButton);
|
||||||
|
userCommentBox = (EditText) findViewById(R.id.errorCommentBox);
|
||||||
|
errorView = (TextView) findViewById(R.id.errorView);
|
||||||
|
infoView = (TextView) findViewById(R.id.errorInfosView);
|
||||||
|
errorMessageView = (TextView) findViewById(R.id.errorMessageView);
|
||||||
|
|
||||||
|
errorView.setText(formErrorText(errorList));
|
||||||
|
|
||||||
|
//importand add gurumeditaion
|
||||||
|
addGuruMeditaion();
|
||||||
|
currentTimeStamp = getCurrentTimeStamp();
|
||||||
|
buildInfo(errorInfo);
|
||||||
|
|
||||||
|
reportButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||||
|
intent.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
|
||||||
|
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
|
||||||
|
.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intent, "Send Email"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reportButton.setEnabled(false);
|
||||||
|
|
||||||
|
globIpRangeThread = new Thread(new IpRagneRequester());
|
||||||
|
globIpRangeThread.start();
|
||||||
|
|
||||||
|
if(errorInfo.message != 0) {
|
||||||
|
errorMessageView.setText(errorInfo.message);
|
||||||
|
} else {
|
||||||
|
errorMessageView.setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.messageWhatHappenedView).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.error_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
switch (id) {
|
||||||
|
case android.R.id.home:
|
||||||
|
goToReturnActivity();
|
||||||
|
break;
|
||||||
|
case R.id.menu_item_share_error: {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SEND);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, buildJson());
|
||||||
|
intent.setType("text/plain");
|
||||||
|
startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formErrorText(List<Exception> el) {
|
||||||
|
String text = "";
|
||||||
|
for (Exception e : el) {
|
||||||
|
text += "-------------------------------------\n"
|
||||||
|
+ ExceptionUtils.getStackTrace(e);
|
||||||
|
}
|
||||||
|
text += "-------------------------------------";
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void goToReturnActivity() {
|
||||||
|
if (returnActivity == null) {
|
||||||
|
super.onBackPressed();
|
||||||
|
} else {
|
||||||
|
Intent intent;
|
||||||
|
if (returnActivity != null &&
|
||||||
|
returnActivity.isAssignableFrom(Activity.class)) {
|
||||||
|
intent = new Intent(this, returnActivity);
|
||||||
|
} else {
|
||||||
|
intent = new Intent(this, VideoItemListActivity.class);
|
||||||
|
}
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
NavUtils.navigateUpTo(this, intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildInfo(ErrorInfo info) {
|
||||||
|
TextView infoLabelView = (TextView) findViewById(R.id.errorInfoLabelsView);
|
||||||
|
TextView infoView = (TextView) findViewById(R.id.errorInfosView);
|
||||||
|
String text = "";
|
||||||
|
|
||||||
|
infoLabelView.setText(getString(R.string.info_labels).replace("\\n", "\n"));
|
||||||
|
|
||||||
|
text += getUserActionString(info.userAction)
|
||||||
|
+ "\n" + info.request
|
||||||
|
+ "\n" + getContentLangString()
|
||||||
|
+ "\n" + info.serviceName
|
||||||
|
+ "\n" + currentTimeStamp
|
||||||
|
+ "\n" + BuildConfig.VERSION_NAME
|
||||||
|
+ "\n" + getOsString();
|
||||||
|
|
||||||
|
infoView.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildJson() {
|
||||||
|
JSONObject errorObject = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
errorObject.put("user_action", getUserActionString(errorInfo.userAction))
|
||||||
|
.put("request", errorInfo.request)
|
||||||
|
.put("content_language", getContentLangString())
|
||||||
|
.put("service", errorInfo.serviceName)
|
||||||
|
.put("version", BuildConfig.VERSION_NAME)
|
||||||
|
.put("os", getOsString())
|
||||||
|
.put("time", currentTimeStamp)
|
||||||
|
.put("ip_range", globIpRange);
|
||||||
|
|
||||||
|
JSONArray exceptionArray = new JSONArray();
|
||||||
|
for (Exception e : errorList) {
|
||||||
|
exceptionArray.put(ExceptionUtils.getStackTrace(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
errorObject.put("exceptions", exceptionArray);
|
||||||
|
errorObject.put("user_comment", userCommentBox.getText().toString());
|
||||||
|
|
||||||
|
return errorObject.toString(3);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error while erroring: Could not build json");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserActionString(int userAction) {
|
||||||
|
switch (userAction) {
|
||||||
|
case REQUESTED_STREAM:
|
||||||
|
return REQUESTED_STREAM_STRING;
|
||||||
|
case SEARCHED:
|
||||||
|
return SEARCHED_STRING;
|
||||||
|
case GET_SUGGESTIONS:
|
||||||
|
return GET_SUGGESTIONS_STRING;
|
||||||
|
default:
|
||||||
|
return "Your description is in another castle.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getContentLangString() {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
.getString(this.getString(R.string.search_language_key), "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOsString() {
|
||||||
|
String osBase = Build.VERSION.SDK_INT >= 23 ? Build.VERSION.BASE_OS : "Android";
|
||||||
|
return System.getProperty("os.name")
|
||||||
|
+ " " + (osBase.isEmpty() ? "Android" : osBase)
|
||||||
|
+ " " + Build.VERSION.RELEASE
|
||||||
|
+ " - " + Integer.toString(Build.VERSION.SDK_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGuruMeditaion() {
|
||||||
|
//just an easter egg
|
||||||
|
TextView sorryView = (TextView) findViewById(R.id.errorSorryView);
|
||||||
|
String text = sorryView.getText().toString();
|
||||||
|
text += "\n" + getString(R.string.guru_meditation);
|
||||||
|
sorryView.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
//super.onBackPressed();
|
||||||
|
goToReturnActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentTimeStamp() {
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
return df.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IpRagneRequester implements Runnable {
|
||||||
|
Handler h = new Handler();
|
||||||
|
public void run() {
|
||||||
|
String ipRange = "none";
|
||||||
|
try {
|
||||||
|
Downloader dl = new Downloader();
|
||||||
|
String ip = dl.download("https://ifcfg.me/ip");
|
||||||
|
|
||||||
|
ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip)
|
||||||
|
+ "0.0";
|
||||||
|
} catch(Exception e) {
|
||||||
|
Log.d(TAG, "Error while error: could not get iprange");
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
h.post(new IpRageReturnRunnable(ipRange));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class IpRageReturnRunnable implements Runnable {
|
||||||
|
String ipRange;
|
||||||
|
public IpRageReturnRunnable(String ipRange) {
|
||||||
|
this.ipRange = ipRange;
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
globIpRange = ipRange;
|
||||||
|
if(infoView != null) {
|
||||||
|
String text = infoView.getText().toString();
|
||||||
|
text += "\n" + globIpRange;
|
||||||
|
infoView.setText(text);
|
||||||
|
reportButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
|
@ -40,8 +41,10 @@ class VideoInfoItemViewCreator {
|
||||||
this.inflater = inflater;
|
this.inflater = inflater;
|
||||||
}
|
}
|
||||||
|
|
||||||
public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info, Context context) {
|
public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, StreamPreviewInfo info) {
|
||||||
ViewHolder holder;
|
ViewHolder holder;
|
||||||
|
|
||||||
|
// generate 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);
|
||||||
holder = new ViewHolder();
|
holder = new ViewHolder();
|
||||||
|
@ -56,20 +59,41 @@ class VideoInfoItemViewCreator {
|
||||||
holder = (ViewHolder) convertView.getTag();
|
holder = (ViewHolder) convertView.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fill with information
|
||||||
|
|
||||||
|
/*
|
||||||
if(info.thumbnail == null) {
|
if(info.thumbnail == null) {
|
||||||
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
||||||
} else {
|
} else {
|
||||||
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
|
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
holder.itemVideoTitleView.setText(info.title);
|
holder.itemVideoTitleView.setText(info.title);
|
||||||
holder.itemUploaderView.setText(info.uploader);
|
if(info.uploader != null && !info.uploader.isEmpty()) {
|
||||||
holder.itemDurationView.setText(info.duration);
|
holder.itemUploaderView.setText(info.uploader);
|
||||||
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
} else {
|
||||||
|
holder.itemDurationView.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
if(info.duration != null && !info.duration.isEmpty()) {
|
||||||
|
holder.itemDurationView.setText(info.duration);
|
||||||
|
} else {
|
||||||
|
holder.itemDurationView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if(info.view_count >= 0) {
|
||||||
|
holder.itemViewCountView.setText(shortViewCount(info.view_count));
|
||||||
|
} else {
|
||||||
|
holder.itemViewCountView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
if(!info.upload_date.isEmpty()) {
|
if(!info.upload_date.isEmpty()) {
|
||||||
holder.itemUploadDateView.setText(info.upload_date+" • ");
|
holder.itemUploadDateView.setText(info.upload_date+" • ");
|
||||||
}
|
}
|
||||||
|
|
||||||
imageLoader.displayImage(info.thumbnail_url, holder.itemThumbnailView, displayImageOptions);
|
if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
||||||
|
imageLoader.displayImage(info.thumbnail_url, holder.itemThumbnailView, displayImageOptions);
|
||||||
|
} else {
|
||||||
|
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,9 @@ import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
import org.schabi.newpipe.extractor.StreamExtractor;
|
import org.schabi.newpipe.extractor.StreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.schabi.newpipe.extractor.StreamInfo;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.VideoInfo;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,12 +127,34 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
VideoInfo videoInfo = null;
|
StreamInfo streamInfo = null;
|
||||||
try {
|
try {
|
||||||
streamExtractor = service.getExtractorInstance(videoUrl, new Downloader());
|
streamExtractor = service.getExtractorInstance(videoUrl, new Downloader());
|
||||||
videoInfo = VideoInfo.getVideoInfo(streamExtractor, new Downloader());
|
streamInfo = StreamInfo.getVideoInfo(streamExtractor, new Downloader());
|
||||||
|
|
||||||
h.post(new VideoResultReturnedRunnable(videoInfo));
|
h.post(new VideoResultReturnedRunnable(streamInfo));
|
||||||
|
|
||||||
|
// look for errors during extraction
|
||||||
|
// this if statement only covers extra information.
|
||||||
|
// if these are not available or caused an error, they are just not available
|
||||||
|
// but don't render the stream information unusalbe.
|
||||||
|
if(streamInfo != null &&
|
||||||
|
!streamInfo.errors.isEmpty()) {
|
||||||
|
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
||||||
|
for (Exception e : streamInfo.errors) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e(TAG, "------");
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity a = getActivity();
|
||||||
|
View rootView = a != null ? a.findViewById(R.id.videoitem_detail) : null;
|
||||||
|
ErrorActivity.reportError(h, getActivity(),
|
||||||
|
streamInfo.errors, null, rootView,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
||||||
|
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These errors render the stream information unusable.
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
postNewErrorToast(h, R.string.network_error);
|
postNewErrorToast(h, R.string.network_error);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -165,37 +187,66 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} catch(StreamInfo.StreamExctractException e) {
|
||||||
|
if(!streamInfo.errors.isEmpty()) {
|
||||||
|
// !!! if this case ever kicks in someone gets kicked out !!!
|
||||||
|
ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
||||||
|
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
||||||
|
} else {
|
||||||
|
ErrorActivity.reportError(h, getActivity(), streamInfo.errors, VideoItemListActivity.class, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
||||||
|
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
||||||
|
}
|
||||||
|
h.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
e.printStackTrace();
|
||||||
} catch (ParsingException e) {
|
} catch (ParsingException e) {
|
||||||
postNewErrorToast(h, e.getMessage());
|
ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
||||||
|
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
|
||||||
|
h.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
postNewErrorToast(h, R.string.general_error);
|
ErrorActivity.reportError(h, getActivity(), e, VideoItemListActivity.class, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
||||||
|
service.getServiceInfo().name, videoUrl, R.string.general_error));
|
||||||
|
h.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
|
||||||
if(videoInfo != null &&
|
|
||||||
!videoInfo.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
|
||||||
for(Exception e : videoInfo.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VideoResultReturnedRunnable implements Runnable {
|
private class VideoResultReturnedRunnable implements Runnable {
|
||||||
private final VideoInfo videoInfo;
|
private final StreamInfo streamInfo;
|
||||||
public VideoResultReturnedRunnable(VideoInfo videoInfo) {
|
public VideoResultReturnedRunnable(StreamInfo streamInfo) {
|
||||||
this.videoInfo = videoInfo;
|
this.streamInfo = streamInfo;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean show_age_restricted_content = PreferenceManager.getDefaultSharedPreferences(getActivity())
|
Activity a = getActivity();
|
||||||
.getBoolean(activity.getString(R.string.show_age_restricted_content), false);
|
if(a != null) {
|
||||||
if(videoInfo.age_limit == 0 || show_age_restricted_content) {
|
boolean show_age_restricted_content = PreferenceManager.getDefaultSharedPreferences(a)
|
||||||
updateInfo(videoInfo);
|
.getBoolean(activity.getString(R.string.show_age_restricted_content), false);
|
||||||
} else {
|
if (streamInfo.age_limit == 0 || show_age_restricted_content) {
|
||||||
onNotSpecifiedContentErrorWithMessage(R.string.video_is_age_restricted);
|
updateInfo(streamInfo);
|
||||||
|
} else {
|
||||||
|
onNotSpecifiedContentErrorWithMessage(R.string.video_is_age_restricted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +271,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
public void onLoadingCancelled(String imageUri, View view) {}
|
public void onLoadingCancelled(String imageUri, View view) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateInfo(final VideoInfo info) {
|
private void updateInfo(final StreamInfo info) {
|
||||||
try {
|
try {
|
||||||
Context c = getContext();
|
Context c = getContext();
|
||||||
VideoInfoItemViewCreator videoItemViewCreator =
|
VideoInfoItemViewCreator videoItemViewCreator =
|
||||||
|
@ -249,7 +300,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
View nextVideoView = null;
|
View nextVideoView = null;
|
||||||
if(info.next_video != null) {
|
if(info.next_video != null) {
|
||||||
nextVideoView = videoItemViewCreator
|
nextVideoView = videoItemViewCreator
|
||||||
.getViewFromVideoInfoItem(null, nextVideoFrame, info.next_video, getContext());
|
.getViewFromVideoInfoItem(null, nextVideoFrame, info.next_video);
|
||||||
} else {
|
} else {
|
||||||
activity.findViewById(R.id.detailNextVidButtonAndContentLayout).setVisibility(View.GONE);
|
activity.findViewById(R.id.detailNextVidButtonAndContentLayout).setVisibility(View.GONE);
|
||||||
activity.findViewById(R.id.detailNextVideoTitle).setVisibility(View.GONE);
|
activity.findViewById(R.id.detailNextVideoTitle).setVisibility(View.GONE);
|
||||||
|
@ -337,8 +388,8 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
|
||||||
// parse streams
|
// parse streams
|
||||||
Vector<VideoInfo.VideoStream> streamsToUse = new Vector<>();
|
Vector<StreamInfo.VideoStream> streamsToUse = new Vector<>();
|
||||||
for (VideoInfo.VideoStream i : info.video_streams) {
|
for (StreamInfo.VideoStream i : info.video_streams) {
|
||||||
if (useStream(i, streamsToUse)) {
|
if (useStream(i, streamsToUse)) {
|
||||||
streamsToUse.add(i);
|
streamsToUse.add(i);
|
||||||
}
|
}
|
||||||
|
@ -394,7 +445,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initThumbnailViews(VideoInfo info, View nextVideoFrame) {
|
private void initThumbnailViews(StreamInfo info, View nextVideoFrame) {
|
||||||
ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView);
|
ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView);
|
||||||
ImageView uploaderThumb
|
ImageView uploaderThumb
|
||||||
= (ImageView) activity.findViewById(R.id.detailUploaderThumbnailView);
|
= (ImageView) activity.findViewById(R.id.detailUploaderThumbnailView);
|
||||||
|
@ -437,7 +488,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupActionBarHandler(final VideoInfo info) {
|
private void setupActionBarHandler(final StreamInfo info) {
|
||||||
actionBarHandler.setupStreamList(info.video_streams);
|
actionBarHandler.setupStreamList(info.video_streams);
|
||||||
|
|
||||||
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
actionBarHandler.setOnShareListener(new ActionBarHandler.OnActionListener() {
|
||||||
|
@ -504,7 +555,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
// website which was crawled. Then the ui has to understand this and act right.
|
// website which was crawled. Then the ui has to understand this and act right.
|
||||||
|
|
||||||
if (info.audio_streams != null) {
|
if (info.audio_streams != null) {
|
||||||
VideoInfo.AudioStream audioStream =
|
StreamInfo.AudioStream audioStream =
|
||||||
info.audio_streams.get(getPreferredAudioStreamId(info));
|
info.audio_streams.get(getPreferredAudioStreamId(info));
|
||||||
|
|
||||||
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
|
||||||
|
@ -513,7 +564,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.video_streams != null) {
|
if (info.video_streams != null) {
|
||||||
VideoInfo.VideoStream selectedStreamItem = info.video_streams.get(selectedStreamId);
|
StreamInfo.VideoStream selectedStreamItem = info.video_streams.get(selectedStreamId);
|
||||||
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
|
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
|
||||||
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
|
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
|
||||||
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
|
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
|
||||||
|
@ -540,7 +591,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
||||||
Intent intent;
|
Intent intent;
|
||||||
VideoInfo.AudioStream audioStream =
|
StreamInfo.AudioStream audioStream =
|
||||||
info.audio_streams.get(getPreferredAudioStreamId(info));
|
info.audio_streams.get(getPreferredAudioStreamId(info));
|
||||||
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
|
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
|
||||||
//internal music player: explicit intent
|
//internal music player: explicit intent
|
||||||
|
@ -599,7 +650,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPreferredAudioStreamId(final VideoInfo info) {
|
private int getPreferredAudioStreamId(final StreamInfo info) {
|
||||||
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(getActivity())
|
String preferredFormatString = PreferenceManager.getDefaultSharedPreferences(getActivity())
|
||||||
.getString(activity.getString(R.string.default_audio_format_key), "webm");
|
.getString(activity.getString(R.string.default_audio_format_key), "webm");
|
||||||
|
|
||||||
|
@ -626,12 +677,12 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSimilarVideos(final VideoInfo info, VideoInfoItemViewCreator videoItemViewCreator) {
|
private void initSimilarVideos(final StreamInfo info, VideoInfoItemViewCreator videoItemViewCreator) {
|
||||||
LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similarVideosView);
|
LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similarVideosView);
|
||||||
ArrayList<VideoPreviewInfo> similar = new ArrayList<>(info.related_videos);
|
ArrayList<StreamPreviewInfo> similar = new ArrayList<>(info.related_videos);
|
||||||
for (final VideoPreviewInfo item : similar) {
|
for (final StreamPreviewInfo item : similar) {
|
||||||
View similarView = videoItemViewCreator
|
View similarView = videoItemViewCreator
|
||||||
.getViewFromVideoInfoItem(null, similarLayout, item, getContext());
|
.getViewFromVideoInfoItem(null, similarLayout, item);
|
||||||
|
|
||||||
similarView.setClickable(true);
|
similarView.setClickable(true);
|
||||||
similarView.setFocusable(true);
|
similarView.setFocusable(true);
|
||||||
|
@ -703,8 +754,8 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean useStream(VideoInfo.VideoStream stream, Vector<VideoInfo.VideoStream> streams) {
|
private boolean useStream(StreamInfo.VideoStream stream, Vector<StreamInfo.VideoStream> streams) {
|
||||||
for(VideoInfo.VideoStream i : streams) {
|
for(StreamInfo.VideoStream i : streams) {
|
||||||
if(i.resolution.equals(stream.resolution)) {
|
if(i.resolution.equals(stream.resolution)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -794,9 +845,9 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playVideo(final VideoInfo info) {
|
public void playVideo(final StreamInfo info) {
|
||||||
// ----------- THE MAGIC MOMENT ---------------
|
// ----------- THE MAGIC MOMENT ---------------
|
||||||
VideoInfo.VideoStream selectedVideoStream =
|
StreamInfo.VideoStream selectedVideoStream =
|
||||||
info.video_streams.get(actionBarHandler.getSelectedVideoStream());
|
info.video_streams.get(actionBarHandler.getSelectedVideoStream());
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(activity)
|
if (PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.SearchEngine;
|
import org.schabi.newpipe.extractor.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.ServiceList;
|
import org.schabi.newpipe.extractor.ServiceList;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -173,11 +172,17 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||||
ArrayList<String>suggestions = engine.suggestionList(query,searchLanguage,new Downloader());
|
ArrayList<String>suggestions = engine.suggestionList(query,searchLanguage,new Downloader());
|
||||||
h.post(new SuggestionResultRunnable(suggestions));
|
h.post(new SuggestionResultRunnable(suggestions));
|
||||||
} catch (ExtractionException e) {
|
} catch (ExtractionException e) {
|
||||||
postNewErrorToast(h, R.string.parsing_error);
|
ErrorActivity.reportError(h, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list),
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.parsing_error));
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
postNewErrorToast(h, R.string.network_error);
|
postNewErrorToast(h, R.string.network_error);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ErrorActivity.reportError(h, VideoItemListActivity.this, e, null, findViewById(R.id.videoitem_list),
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.general_error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -17,8 +18,8 @@ import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.ExtractionException;
|
import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.SearchResult;
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
import org.schabi.newpipe.extractor.SearchEngine;
|
import org.schabi.newpipe.extractor.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
|
||||||
|
@ -68,9 +69,9 @@ public class VideoItemListFragment extends ListFragment {
|
||||||
private boolean loadingNextPage = true;
|
private boolean loadingNextPage = true;
|
||||||
|
|
||||||
private class ResultRunnable implements Runnable {
|
private class ResultRunnable implements Runnable {
|
||||||
private final SearchEngine.Result result;
|
private final SearchResult result;
|
||||||
private final int requestId;
|
private final int requestId;
|
||||||
public ResultRunnable(SearchEngine.Result result, int requestId) {
|
public ResultRunnable(SearchResult result, int requestId) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.requestId = requestId;
|
this.requestId = requestId;
|
||||||
}
|
}
|
||||||
|
@ -101,32 +102,60 @@ public class VideoItemListFragment extends ListFragment {
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
SearchResult result = null;
|
||||||
try {
|
try {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
String searchLanguageKey = getContext().getString(R.string.search_language_key);
|
String searchLanguageKey = getContext().getString(R.string.search_language_key);
|
||||||
String searchLanguage = sp.getString(searchLanguageKey,
|
String searchLanguage = sp.getString(searchLanguageKey,
|
||||||
getString(R.string.default_language_value));
|
getString(R.string.default_language_value));
|
||||||
SearchEngine.Result result = engine.search(query, page, searchLanguage,
|
result = SearchResult
|
||||||
new Downloader());
|
.getSearchResult(engine, query, page, searchLanguage, new Downloader());
|
||||||
|
|
||||||
//Log.i(TAG, "language code passed:\""+searchLanguage+"\"");
|
|
||||||
if(runs) {
|
if(runs) {
|
||||||
h.post(new ResultRunnable(result, requestId));
|
h.post(new ResultRunnable(result, requestId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// look for errors during extraction
|
||||||
|
// soft errors:
|
||||||
|
if(result != null &&
|
||||||
|
!result.errors.isEmpty()) {
|
||||||
|
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
|
||||||
|
for(Exception e : result.errors) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e(TAG, "------");
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity a = getActivity();
|
||||||
|
View rootView = a.findViewById(R.id.videoitem_list);
|
||||||
|
ErrorActivity.reportError(h, getActivity(), result.errors, null, rootView,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.light_parsing_error));
|
||||||
|
|
||||||
|
}
|
||||||
|
// hard errors:
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
postNewErrorToast(h, R.string.network_error);
|
postNewNothingFoundToast(h, R.string.network_error);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch(ExtractionException ce) {
|
} catch(SearchEngine.NothingFoundException e) {
|
||||||
postNewErrorToast(h, R.string.parsing_error);
|
postNewErrorToast(h, e.getMessage());
|
||||||
ce.printStackTrace();
|
} catch(ExtractionException e) {
|
||||||
|
ErrorActivity.reportError(h, getActivity(), e, null, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.parsing_error));
|
||||||
|
//postNewErrorToast(h, R.string.parsing_error);
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
postNewErrorToast(h, R.string.general_error);
|
ErrorActivity.reportError(h, getActivity(), e, null, null,
|
||||||
|
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
|
||||||
|
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.general_error));
|
||||||
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void present(List<VideoPreviewInfo> videoList) {
|
public void present(List<StreamPreviewInfo> videoList) {
|
||||||
mode = PRESENT_VIDEOS_MODE;
|
mode = PRESENT_VIDEOS_MODE;
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
getListView().smoothScrollToPosition(0);
|
getListView().smoothScrollToPosition(0);
|
||||||
|
@ -166,22 +195,19 @@ public class VideoItemListFragment extends ListFragment {
|
||||||
this.streamingService = streamingService;
|
this.streamingService = streamingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateListOnResult(SearchEngine.Result result, int requestId) {
|
private void updateListOnResult(SearchResult result, int requestId) {
|
||||||
if(requestId == currentRequestId) {
|
if(requestId == currentRequestId) {
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
if (result.resultList.isEmpty()) {
|
updateList(result.resultList);
|
||||||
Toast.makeText(getActivity(), result.errorMessage, Toast.LENGTH_LONG).show();
|
if(!result.suggestion.isEmpty()) {
|
||||||
} else {
|
Toast.makeText(getActivity(),
|
||||||
if (!result.suggestion.isEmpty()) {
|
String.format(getString(R.string.did_you_mean), result.suggestion),
|
||||||
Toast.makeText(getActivity(), getString(R.string.did_you_mean) + result.suggestion + " ?",
|
Toast.LENGTH_LONG).show();
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
updateList(result.resultList);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateList(List<VideoPreviewInfo> list) {
|
private void updateList(List<StreamPreviewInfo> list) {
|
||||||
try {
|
try {
|
||||||
videoListAdapter.addVideoList(list);
|
videoListAdapter.addVideoList(list);
|
||||||
terminateThreads();
|
terminateThreads();
|
||||||
|
@ -318,13 +344,24 @@ public class VideoItemListFragment extends ListFragment {
|
||||||
mActivatedPosition = position;
|
mActivatedPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
private void postNewErrorToast(Handler h, final String message) {
|
||||||
|
h.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
setListShown(true);
|
||||||
|
Toast.makeText(getActivity(), message,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postNewNothingFoundToast(Handler h, final int stringResource) {
|
||||||
h.post(new Runnable() {
|
h.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
Toast.makeText(getActivity(), getString(stringResource),
|
Toast.makeText(getActivity(), getString(stringResource),
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import android.view.ViewGroup;
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
@ -36,7 +36,7 @@ import java.util.Vector;
|
||||||
class VideoListAdapter extends BaseAdapter {
|
class VideoListAdapter extends BaseAdapter {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final VideoInfoItemViewCreator viewCreator;
|
private final VideoInfoItemViewCreator viewCreator;
|
||||||
private Vector<VideoPreviewInfo> videoList = new Vector<>();
|
private Vector<StreamPreviewInfo> videoList = new Vector<>();
|
||||||
private final ListView listView;
|
private final ListView listView;
|
||||||
|
|
||||||
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
|
||||||
|
@ -47,7 +47,7 @@ class VideoListAdapter extends BaseAdapter {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVideoList(List<VideoPreviewInfo> videos) {
|
public void addVideoList(List<StreamPreviewInfo> videos) {
|
||||||
videoList.addAll(videos);
|
videoList.addAll(videos);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class VideoListAdapter extends BaseAdapter {
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector<VideoPreviewInfo> getVideoList() {
|
public Vector<StreamPreviewInfo> getVideoList() {
|
||||||
return videoList;
|
return videoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class VideoListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position), context);
|
convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position));
|
||||||
|
|
||||||
if(listView.isItemChecked(position)) {
|
if(listView.isItemChecked(position)) {
|
||||||
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.light_youtube_primary_color));
|
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.light_youtube_primary_color));
|
||||||
|
|
|
@ -20,7 +20,7 @@ import android.graphics.Bitmap;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**Common properties between VideoInfo and VideoPreviewInfo.*/
|
/**Common properties between StreamInfo and StreamPreviewInfo.*/
|
||||||
public abstract class AbstractVideoInfo {
|
public abstract class AbstractVideoInfo {
|
||||||
public String id = "";
|
public String id = "";
|
||||||
public String title = "";
|
public String title = "";
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class DashMpdParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<VideoInfo.AudioStream> getAudioStreams(String dashManifestUrl,
|
public static List<StreamInfo.AudioStream> getAudioStreams(String dashManifestUrl,
|
||||||
Downloader downloader)
|
Downloader downloader)
|
||||||
throws DashMpdParsingException {
|
throws DashMpdParsingException {
|
||||||
String dashDoc;
|
String dashDoc;
|
||||||
|
@ -46,7 +46,7 @@ public class DashMpdParser {
|
||||||
} catch(IOException ioe) {
|
} catch(IOException ioe) {
|
||||||
throw new DashMpdParsingException("Could not get dash mpd: " + dashManifestUrl, ioe);
|
throw new DashMpdParsingException("Could not get dash mpd: " + dashManifestUrl, ioe);
|
||||||
}
|
}
|
||||||
Vector<VideoInfo.AudioStream> audioStreams = new Vector<>();
|
Vector<StreamInfo.AudioStream> audioStreams = new Vector<>();
|
||||||
try {
|
try {
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
parser.setInput(new StringReader(dashDoc));
|
parser.setInput(new StringReader(dashDoc));
|
||||||
|
@ -83,7 +83,7 @@ public class DashMpdParser {
|
||||||
} else if(currentMimeType.equals(MediaFormat.M4A.mimeType)) {
|
} else if(currentMimeType.equals(MediaFormat.M4A.mimeType)) {
|
||||||
format = MediaFormat.M4A.id;
|
format = MediaFormat.M4A.id;
|
||||||
}
|
}
|
||||||
audioStreams.add(new VideoInfo.AudioStream(parser.getText(),
|
audioStreams.add(new StreamInfo.AudioStream(parser.getText(),
|
||||||
format, currentBandwidth, currentSamplingRate));
|
format, currentBandwidth, currentSamplingRate));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -21,8 +21,6 @@ package org.schabi.newpipe.extractor;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class ExtractionException extends Exception {
|
public class ExtractionException extends Exception {
|
||||||
public ExtractionException() {}
|
|
||||||
|
|
||||||
public ExtractionException(String message) {
|
public ExtractionException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,9 @@ package org.schabi.newpipe.extractor;
|
||||||
|
|
||||||
|
|
||||||
public class ParsingException extends ExtractionException {
|
public class ParsingException extends ExtractionException {
|
||||||
public ParsingException() {}
|
|
||||||
public ParsingException(String message) {
|
public ParsingException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
public ParsingException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
public ParsingException(String message, Throwable cause) {
|
public ParsingException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,16 @@ import java.util.Vector;
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
public interface SearchEngine {
|
public interface SearchEngine {
|
||||||
class Result {
|
public class NothingFoundException extends ExtractionException {
|
||||||
public String errorMessage = "";
|
public NothingFoundException(String message) {
|
||||||
public String suggestion = "";
|
super(message);
|
||||||
public final List<VideoPreviewInfo> resultList = new Vector<>();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> suggestionList(String query,String contentCountry, Downloader dl)
|
ArrayList<String> suggestionList(String query,String contentCountry, Downloader dl)
|
||||||
throws ExtractionException, IOException;
|
throws ExtractionException, IOException;
|
||||||
|
|
||||||
//Result search(String query, int page);
|
//Result search(String query, int page);
|
||||||
Result search(String query, int page, String contentCountry, Downloader dl)
|
StreamPreviewInfoCollector search(String query, int page, String contentCountry, Downloader dl)
|
||||||
throws ExtractionException, IOException;
|
throws ExtractionException, IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.schabi.newpipe.extractor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 29.02.16.
|
||||||
|
*
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* SearchResult.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 SearchResult {
|
||||||
|
public static SearchResult getSearchResult(SearchEngine engine, String query,
|
||||||
|
int page, String languageCode, Downloader dl)
|
||||||
|
throws ExtractionException, IOException {
|
||||||
|
|
||||||
|
SearchResult result = engine.search(query, page, languageCode, dl).getSearchResult();
|
||||||
|
if(result.resultList.isEmpty()) {
|
||||||
|
if(result.suggestion.isEmpty()) {
|
||||||
|
throw new ExtractionException("Empty result despite no error");
|
||||||
|
} else {
|
||||||
|
// This is used as a fallback. Do not relay on it !!!
|
||||||
|
throw new SearchEngine.NothingFoundException(result.suggestion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String suggestion = "";
|
||||||
|
public final List<StreamPreviewInfo> resultList = new Vector<>();
|
||||||
|
public List<Exception> errors = new Vector<>();
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor;
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 10.08.15.
|
* Created by Christian Schabesberger on 10.08.15.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* StreamExtractor.java is part of NewPipe.
|
* StreamExtractor.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
|
||||||
|
@ -29,7 +29,6 @@ import java.util.List;
|
||||||
public interface StreamExtractor {
|
public interface StreamExtractor {
|
||||||
|
|
||||||
public class ExctractorInitException extends ExtractionException {
|
public class ExctractorInitException extends ExtractionException {
|
||||||
public ExctractorInitException() {}
|
|
||||||
public ExctractorInitException(String message) {
|
public ExctractorInitException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
@ -42,13 +41,9 @@ public interface StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContentNotAvailableException extends ParsingException {
|
public class ContentNotAvailableException extends ParsingException {
|
||||||
public ContentNotAvailableException() {}
|
|
||||||
public ContentNotAvailableException(String message) {
|
public ContentNotAvailableException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
public ContentNotAvailableException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
public ContentNotAvailableException(String message, Throwable cause) {
|
public ContentNotAvailableException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
@ -63,16 +58,16 @@ public interface StreamExtractor {
|
||||||
public abstract String getUploadDate() throws ParsingException;
|
public abstract String getUploadDate() throws ParsingException;
|
||||||
public abstract String getThumbnailUrl() throws ParsingException;
|
public abstract String getThumbnailUrl() throws ParsingException;
|
||||||
public abstract String getUploaderThumbnailUrl() throws ParsingException;
|
public abstract String getUploaderThumbnailUrl() throws ParsingException;
|
||||||
public abstract List<VideoInfo.AudioStream> getAudioStreams() throws ParsingException;
|
public abstract List<StreamInfo.AudioStream> getAudioStreams() throws ParsingException;
|
||||||
public abstract List<VideoInfo.VideoStream> getVideoStreams() throws ParsingException;
|
public abstract List<StreamInfo.VideoStream> getVideoStreams() throws ParsingException;
|
||||||
public abstract List<VideoInfo.VideoStream> getVideoOnlyStreams() throws ParsingException;
|
public abstract List<StreamInfo.VideoStream> getVideoOnlyStreams() throws ParsingException;
|
||||||
public abstract String getDashMpdUrl() throws ParsingException;
|
public abstract String getDashMpdUrl() throws ParsingException;
|
||||||
public abstract int getAgeLimit() throws ParsingException;
|
public abstract int getAgeLimit() throws ParsingException;
|
||||||
public abstract String getAverageRating() throws ParsingException;
|
public abstract String getAverageRating() throws ParsingException;
|
||||||
public abstract int getLikeCount() throws ParsingException;
|
public abstract int getLikeCount() throws ParsingException;
|
||||||
public abstract int getDislikeCount() throws ParsingException;
|
public abstract int getDislikeCount() throws ParsingException;
|
||||||
public abstract VideoPreviewInfo getNextVideo() throws ParsingException;
|
public abstract StreamPreviewInfo getNextVideo() throws ParsingException;
|
||||||
public abstract List<VideoPreviewInfo> getRelatedVideos() throws ParsingException;
|
public abstract List<StreamPreviewInfo> getRelatedVideos() throws ParsingException;
|
||||||
public abstract VideoUrlIdHandler getUrlIdConverter();
|
public abstract StreamUrlIdHandler getUrlIdConverter();
|
||||||
public abstract String getPageUrl();
|
public abstract String getPageUrl();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import java.util.Vector;
|
||||||
/**
|
/**
|
||||||
* 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 2016 <chris.schabesberger@mailbox.org>
|
||||||
* VideoInfo.java is part of NewPipe.
|
* StreamInfo.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
|
||||||
|
@ -26,181 +26,188 @@ 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.*/
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
public class VideoInfo extends AbstractVideoInfo {
|
public class StreamInfo extends AbstractVideoInfo {
|
||||||
|
|
||||||
|
public static class StreamExctractException extends ExtractionException {
|
||||||
|
StreamExctractException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**Fills out the video info fields which are common to all services.
|
/**Fills out the video info fields which are common to all services.
|
||||||
* Probably needs to be overridden by subclasses*/
|
* Probably needs to be overridden by subclasses*/
|
||||||
public static VideoInfo getVideoInfo(StreamExtractor extractor, Downloader downloader)
|
public static StreamInfo getVideoInfo(StreamExtractor extractor, Downloader downloader)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
VideoInfo videoInfo = new VideoInfo();
|
StreamInfo streamInfo = new StreamInfo();
|
||||||
|
|
||||||
videoInfo = extractImportantData(videoInfo, extractor, downloader);
|
streamInfo = extractImportantData(streamInfo, extractor, downloader);
|
||||||
videoInfo = extractStreams(videoInfo, extractor, downloader);
|
streamInfo = extractStreams(streamInfo, extractor, downloader);
|
||||||
videoInfo = extractOptionalData(videoInfo, extractor, downloader);
|
streamInfo = extractOptionalData(streamInfo, extractor, downloader);
|
||||||
|
|
||||||
return videoInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoInfo extractImportantData(
|
private static StreamInfo extractImportantData(
|
||||||
VideoInfo videoInfo, StreamExtractor extractor, Downloader downloader)
|
StreamInfo streamInfo, StreamExtractor extractor, Downloader downloader)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
/* ---- importand data, withoug the video can't be displayed goes here: ---- */
|
/* ---- importand data, withoug the video can't be displayed goes here: ---- */
|
||||||
// if one of these is not available an exception is ment to be thrown directly into the frontend.
|
// if one of these is not available an exception is ment to be thrown directly into the frontend.
|
||||||
|
|
||||||
VideoUrlIdHandler uiconv = extractor.getUrlIdConverter();
|
StreamUrlIdHandler uiconv = extractor.getUrlIdConverter();
|
||||||
|
|
||||||
videoInfo.webpage_url = extractor.getPageUrl();
|
streamInfo.webpage_url = extractor.getPageUrl();
|
||||||
videoInfo.id = uiconv.getVideoId(extractor.getPageUrl());
|
streamInfo.id = uiconv.getVideoId(extractor.getPageUrl());
|
||||||
videoInfo.title = extractor.getTitle();
|
streamInfo.title = extractor.getTitle();
|
||||||
videoInfo.age_limit = extractor.getAgeLimit();
|
streamInfo.age_limit = extractor.getAgeLimit();
|
||||||
|
|
||||||
if((videoInfo.webpage_url == null || videoInfo.webpage_url.isEmpty())
|
if((streamInfo.webpage_url == null || streamInfo.webpage_url.isEmpty())
|
||||||
|| (videoInfo.id == null || videoInfo.id.isEmpty())
|
|| (streamInfo.id == null || streamInfo.id.isEmpty())
|
||||||
|| (videoInfo.title == null /* videoInfo.title can be empty of course */)
|
|| (streamInfo.title == null /* streamInfo.title can be empty of course */)
|
||||||
|| (videoInfo.age_limit == -1));
|
|| (streamInfo.age_limit == -1));
|
||||||
|
|
||||||
return videoInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoInfo extractStreams(
|
private static StreamInfo extractStreams(
|
||||||
VideoInfo videoInfo, StreamExtractor extractor, Downloader downloader)
|
StreamInfo streamInfo, StreamExtractor extractor, Downloader downloader)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
/* ---- stream extraction goes here ---- */
|
/* ---- stream extraction goes here ---- */
|
||||||
// At least one type of stream has to be available,
|
// At least one type of stream has to be available,
|
||||||
// otherwise an exception will be thrown directly into the frontend.
|
// otherwise an exception will be thrown directly into the frontend.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
videoInfo.dashMpdUrl = extractor.getDashMpdUrl();
|
streamInfo.dashMpdUrl = extractor.getDashMpdUrl();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(new ExtractionException("Couldn't get Dash manifest", e));
|
streamInfo.addException(new ExtractionException("Couldn't get Dash manifest", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load and extract audio */
|
/* Load and extract audio */
|
||||||
try {
|
try {
|
||||||
videoInfo.audio_streams = extractor.getAudioStreams();
|
streamInfo.audio_streams = extractor.getAudioStreams();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(new ExtractionException("Couldn't get audio streams", e));
|
streamInfo.addException(new ExtractionException("Couldn't get audio streams", e));
|
||||||
}
|
}
|
||||||
// also try to get streams from the dashMpd
|
// also try to get streams from the dashMpd
|
||||||
if(videoInfo.dashMpdUrl != null && !videoInfo.dashMpdUrl.isEmpty()) {
|
if(streamInfo.dashMpdUrl != null && !streamInfo.dashMpdUrl.isEmpty()) {
|
||||||
if(videoInfo.audio_streams == null) {
|
if(streamInfo.audio_streams == null) {
|
||||||
videoInfo.audio_streams = new Vector<AudioStream>();
|
streamInfo.audio_streams = new Vector<AudioStream>();
|
||||||
}
|
}
|
||||||
//todo: make this quick and dirty solution a real fallback
|
//todo: make this quick and dirty solution a real fallback
|
||||||
// same as the quick and dirty aboth
|
// same as the quick and dirty aboth
|
||||||
try {
|
try {
|
||||||
videoInfo.audio_streams.addAll(
|
streamInfo.audio_streams.addAll(
|
||||||
DashMpdParser.getAudioStreams(videoInfo.dashMpdUrl, downloader));
|
DashMpdParser.getAudioStreams(streamInfo.dashMpdUrl, downloader));
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(
|
streamInfo.addException(
|
||||||
new ExtractionException("Couldn't get audio streams from dash mpd", e));
|
new ExtractionException("Couldn't get audio streams from dash mpd", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Extract video stream url*/
|
/* Extract video stream url*/
|
||||||
try {
|
try {
|
||||||
videoInfo.video_streams = extractor.getVideoStreams();
|
streamInfo.video_streams = extractor.getVideoStreams();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
videoInfo.addException(
|
streamInfo.addException(
|
||||||
new ExtractionException("Couldn't get video streams", e));
|
new ExtractionException("Couldn't get video streams", e));
|
||||||
}
|
}
|
||||||
/* Extract video only stream url*/
|
/* Extract video only stream url*/
|
||||||
try {
|
try {
|
||||||
videoInfo.video_only_streams = extractor.getVideoOnlyStreams();
|
streamInfo.video_only_streams = extractor.getVideoOnlyStreams();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(
|
streamInfo.addException(
|
||||||
new ExtractionException("Couldn't get video only streams", e));
|
new ExtractionException("Couldn't get video only streams", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// either dash_mpd audio_only or video has to be available, otherwise we didn't get a stream,
|
// either dash_mpd audio_only or video has to be available, otherwise we didn't get a stream,
|
||||||
// and therefore failed. (Since video_only_streams are just optional they don't caunt).
|
// and therefore failed. (Since video_only_streams are just optional they don't caunt).
|
||||||
if((videoInfo.video_streams == null || videoInfo.video_streams.isEmpty())
|
if((streamInfo.video_streams == null || streamInfo.video_streams.isEmpty())
|
||||||
&& (videoInfo.audio_streams == null || videoInfo.audio_streams.isEmpty())
|
&& (streamInfo.audio_streams == null || streamInfo.audio_streams.isEmpty())
|
||||||
&& (videoInfo.dashMpdUrl == null || videoInfo.dashMpdUrl.isEmpty())) {
|
&& (streamInfo.dashMpdUrl == null || streamInfo.dashMpdUrl.isEmpty())) {
|
||||||
throw new ExtractionException("Could not get any stream. See error variable to get further details.");
|
throw new StreamExctractException(
|
||||||
|
"Could not get any stream. See error variable to get further details.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VideoInfo extractOptionalData(
|
private static StreamInfo extractOptionalData(
|
||||||
VideoInfo videoInfo, StreamExtractor extractor, Downloader downloader) {
|
StreamInfo streamInfo, StreamExtractor extractor, Downloader downloader) {
|
||||||
/* ---- optional data goes here: ---- */
|
/* ---- optional data goes here: ---- */
|
||||||
// If one of these failes, the frontend neets to handle that they are not available.
|
// If one of these failes, the frontend neets to handle that they are not available.
|
||||||
// Exceptions are therfore not thrown into the frontend, but stored into the error List,
|
// Exceptions are therfore not thrown into the frontend, but stored into the error List,
|
||||||
// so the frontend can afterwads check where errors happend.
|
// so the frontend can afterwads check where errors happend.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
videoInfo.thumbnail_url = extractor.getThumbnailUrl();
|
streamInfo.thumbnail_url = extractor.getThumbnailUrl();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.duration = extractor.getLength();
|
streamInfo.duration = extractor.getLength();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.uploader = extractor.getUploader();
|
streamInfo.uploader = extractor.getUploader();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.description = extractor.getDescription();
|
streamInfo.description = extractor.getDescription();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.view_count = extractor.getViewCount();
|
streamInfo.view_count = extractor.getViewCount();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.upload_date = extractor.getUploadDate();
|
streamInfo.upload_date = extractor.getUploadDate();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.uploader_thumbnail_url = extractor.getUploaderThumbnailUrl();
|
streamInfo.uploader_thumbnail_url = extractor.getUploaderThumbnailUrl();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.start_position = extractor.getTimeStamp();
|
streamInfo.start_position = extractor.getTimeStamp();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.average_rating = extractor.getAverageRating();
|
streamInfo.average_rating = extractor.getAverageRating();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.like_count = extractor.getLikeCount();
|
streamInfo.like_count = extractor.getLikeCount();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.dislike_count = extractor.getDislikeCount();
|
streamInfo.dislike_count = extractor.getDislikeCount();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.next_video = extractor.getNextVideo();
|
streamInfo.next_video = extractor.getNextVideo();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
videoInfo.related_videos = extractor.getRelatedVideos();
|
streamInfo.related_videos = extractor.getRelatedVideos();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
videoInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String uploader_thumbnail_url = "";
|
public String uploader_thumbnail_url = "";
|
||||||
|
@ -220,20 +227,20 @@ public class VideoInfo extends AbstractVideoInfo {
|
||||||
public int like_count = -1;
|
public int like_count = -1;
|
||||||
public int dislike_count = -1;
|
public int dislike_count = -1;
|
||||||
public String average_rating = "";
|
public String average_rating = "";
|
||||||
public VideoPreviewInfo next_video = null;
|
public StreamPreviewInfo next_video = null;
|
||||||
public List<VideoPreviewInfo> related_videos = null;
|
public List<StreamPreviewInfo> related_videos = null;
|
||||||
//in seconds. some metadata is not passed using a VideoInfo object!
|
//in seconds. some metadata is not passed using a StreamInfo object!
|
||||||
public int start_position = 0;
|
public int start_position = 0;
|
||||||
//todo: public int service_id = -1;
|
//todo: public int service_id = -1;
|
||||||
|
|
||||||
public List<Exception> errors = new Vector<>();
|
public List<Exception> errors = new Vector<>();
|
||||||
|
|
||||||
public VideoInfo() {}
|
public StreamInfo() {}
|
||||||
|
|
||||||
/**Creates a new VideoInfo object from an existing AbstractVideoInfo.
|
/**Creates a new StreamInfo object from an existing AbstractVideoInfo.
|
||||||
* All the shared properties are copied to the new VideoInfo.*/
|
* All the shared properties are copied to the new StreamInfo.*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public VideoInfo(AbstractVideoInfo avi) {
|
public StreamInfo(AbstractVideoInfo avi) {
|
||||||
this.id = avi.id;
|
this.id = avi.id;
|
||||||
this.title = avi.title;
|
this.title = avi.title;
|
||||||
this.uploader = avi.uploader;
|
this.uploader = avi.uploader;
|
||||||
|
@ -245,9 +252,9 @@ public class VideoInfo extends AbstractVideoInfo {
|
||||||
this.view_count = avi.view_count;
|
this.view_count = avi.view_count;
|
||||||
|
|
||||||
//todo: better than this
|
//todo: better than this
|
||||||
if(avi instanceof VideoPreviewInfo) {
|
if(avi instanceof StreamPreviewInfo) {
|
||||||
//shitty String to convert code
|
//shitty String to convert code
|
||||||
String dur = ((VideoPreviewInfo)avi).duration;
|
String dur = ((StreamPreviewInfo)avi).duration;
|
||||||
int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":")));
|
int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":")));
|
||||||
int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length()));
|
int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length()));
|
||||||
this.duration = (minutes*60)+seconds;
|
this.duration = (minutes*60)+seconds;
|
|
@ -1,14 +1,10 @@
|
||||||
package org.schabi.newpipe.extractor;
|
package org.schabi.newpipe.extractor;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 2016 <chris.schabesberger@mailbox.org>
|
||||||
* VideoPreviewInfo.java is part of NewPipe.
|
* StreamPreviewInfo.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,6 +21,6 @@ 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 VideoPreviewInfo extends AbstractVideoInfo {
|
public class StreamPreviewInfo extends AbstractVideoInfo {
|
||||||
public String duration = "";
|
public String duration = "";
|
||||||
}
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.schabi.newpipe.extractor;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 28.02.16.
|
||||||
|
*
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* StreamPreviewInfoCollector.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 StreamPreviewInfoCollector {
|
||||||
|
SearchResult result = new SearchResult();
|
||||||
|
StreamUrlIdHandler urlIdHandler = null;
|
||||||
|
|
||||||
|
public StreamPreviewInfoCollector(StreamUrlIdHandler handler) {
|
||||||
|
urlIdHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuggestion(String suggestion) {
|
||||||
|
result.suggestion = suggestion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(Exception e) {
|
||||||
|
result.errors.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchResult getSearchResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void commit(StreamPreviewInfoExtractor extractor) throws ParsingException {
|
||||||
|
try {
|
||||||
|
StreamPreviewInfo resultItem = new StreamPreviewInfo();
|
||||||
|
// importand information
|
||||||
|
resultItem.webpage_url = extractor.getWebPageUrl();
|
||||||
|
if (urlIdHandler == null) {
|
||||||
|
throw new ParsingException("Error: UrlIdHandler not set");
|
||||||
|
} else {
|
||||||
|
resultItem.id = (new YoutubeStreamUrlIdHandler()).getVideoId(resultItem.webpage_url);
|
||||||
|
}
|
||||||
|
resultItem.title = extractor.getTitle();
|
||||||
|
|
||||||
|
// optional iformation
|
||||||
|
try {
|
||||||
|
resultItem.duration = extractor.getDuration();
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resultItem.uploader = extractor.getUploader();
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resultItem.upload_date = extractor.getUploadDate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resultItem.view_count = extractor.getViewCount();
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
resultItem.thumbnail_url = extractor.getThumbnailUrl();
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result.resultList.add(resultItem);
|
||||||
|
} catch (Exception e) {
|
||||||
|
addError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.schabi.newpipe.extractor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 28.02.16.
|
||||||
|
*
|
||||||
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
|
* StreamPreviewInfoExtractor.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 StreamPreviewInfoExtractor {
|
||||||
|
String getWebPageUrl() throws ParsingException;
|
||||||
|
String getTitle() throws ParsingException;
|
||||||
|
String getDuration() throws ParsingException;
|
||||||
|
String getUploader() throws ParsingException;
|
||||||
|
String getUploadDate() throws ParsingException;
|
||||||
|
long getViewCount() throws ParsingException;
|
||||||
|
String getThumbnailUrl() throws ParsingException;
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ package org.schabi.newpipe.extractor;
|
||||||
* Created by Christian Schabesberger on 02.02.16.
|
* Created by Christian Schabesberger on 02.02.16.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* VideoUrlIdHandler.java is part of NewPipe.
|
* StreamUrlIdHandler.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
|
||||||
|
@ -20,7 +20,7 @@ package org.schabi.newpipe.extractor;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface VideoUrlIdHandler {
|
public interface StreamUrlIdHandler {
|
||||||
String getVideoUrl(String videoId);
|
String getVideoUrl(String videoId);
|
||||||
String getVideoId(String siteUrl) throws ParsingException;
|
String getVideoId(String siteUrl) throws ParsingException;
|
||||||
String cleanUrl(String siteUrl) throws ParsingException;
|
String cleanUrl(String siteUrl) throws ParsingException;
|
|
@ -5,7 +5,7 @@ import java.io.IOException;
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 23.08.15.
|
* Created by Christian Schabesberger on 23.08.15.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* StreamingService.java is part of NewPipe.
|
* StreamingService.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
|
||||||
|
@ -31,7 +31,7 @@ public interface StreamingService {
|
||||||
throws IOException, ExtractionException;
|
throws IOException, ExtractionException;
|
||||||
SearchEngine getSearchEngineInstance();
|
SearchEngine getSearchEngineInstance();
|
||||||
|
|
||||||
VideoUrlIdHandler getUrlIdHandler();
|
StreamUrlIdHandler getUrlIdHandler();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,13 @@ 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.extractor.Downloader;
|
import org.schabi.newpipe.extractor.Downloader;
|
||||||
|
import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.Parser;
|
import org.schabi.newpipe.extractor.Parser;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.SearchEngine;
|
import org.schabi.newpipe.extractor.SearchEngine;
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
import org.schabi.newpipe.extractor.StreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfoCollector;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
|
||||||
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;
|
||||||
|
@ -49,9 +52,10 @@ public class YoutubeSearchEngine implements SearchEngine {
|
||||||
private static final String TAG = YoutubeSearchEngine.class.toString();
|
private static final String TAG = YoutubeSearchEngine.class.toString();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result search(String query, int page, String languageCode, Downloader downloader)
|
public StreamPreviewInfoCollector search(String query, int page, String languageCode, Downloader downloader)
|
||||||
throws IOException, ParsingException {
|
throws IOException, ExtractionException {
|
||||||
Result result = new Result();
|
StreamPreviewInfoCollector collector = new StreamPreviewInfoCollector(
|
||||||
|
new YoutubeStreamUrlIdHandler());
|
||||||
Uri.Builder builder = new Uri.Builder();
|
Uri.Builder builder = new Uri.Builder();
|
||||||
builder.scheme("https")
|
builder.scheme("https")
|
||||||
.authority("www.youtube.com")
|
.authority("www.youtube.com")
|
||||||
|
@ -71,12 +75,11 @@ public class YoutubeSearchEngine implements SearchEngine {
|
||||||
site = downloader.download(url);
|
site = downloader.download(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
Document doc = Jsoup.parse(site, url);
|
Document doc = Jsoup.parse(site, url);
|
||||||
Element list = doc.select("ol[class=\"item-section\"]").first();
|
Element list = doc.select("ol[class=\"item-section\"]").first();
|
||||||
|
|
||||||
for (Element item : list.children()) {
|
for (Element item : list.children()) {
|
||||||
/* First we need to determine which kind of item we are working with.
|
/* First we need to determine which kind of item we are working with.
|
||||||
Youtube depicts five different kinds of items on its search result page. These are
|
Youtube depicts five different kinds of items on its search result page. These are
|
||||||
regular videos, playlists, channels, two types of video suggestions, and a "no video
|
regular videos, playlists, channels, two types of video suggestions, and a "no video
|
||||||
|
@ -88,66 +91,33 @@ public class YoutubeSearchEngine implements SearchEngine {
|
||||||
playlists now.
|
playlists now.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Element el;
|
Element el;
|
||||||
|
|
||||||
// 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();
|
collector.setSuggestion(el.select("a").first().text());
|
||||||
// search message item
|
if(list.children().size() == 1) {
|
||||||
} else if (!((el = item.select("div[class*=\"search-message\"]").first()) == null)) {
|
throw new NothingFoundException("Did you mean: " + el.select("a").first().text());
|
||||||
result.errorMessage = el.text();
|
|
||||||
|
|
||||||
// video item type
|
|
||||||
} else if (!((el = item.select("div[class*=\"yt-lockup-video\"").first()) == null)) {
|
|
||||||
VideoPreviewInfo resultItem = new VideoPreviewInfo();
|
|
||||||
|
|
||||||
// importand information
|
|
||||||
resultItem.webpage_url = getWebpageUrl(item);
|
|
||||||
resultItem.id = (new YoutubeVideoUrlIdHandler()).getVideoId(resultItem.webpage_url);
|
|
||||||
resultItem.title = getTitle(item);
|
|
||||||
|
|
||||||
// optional iformation
|
|
||||||
//todo: make this a proper error handling
|
|
||||||
try {
|
|
||||||
resultItem.duration = getDuration(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
resultItem.uploader = getUploader(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
resultItem.upload_date = getUploadDate(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
resultItem.view_count = getViewCount(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
resultItem.thumbnail_url = getThumbnailUrl(item);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.resultList.add(resultItem);
|
|
||||||
} else {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
Log.e(TAG, "unexpected element found:\"" + el + "\"");
|
|
||||||
}
|
}
|
||||||
|
// search message item
|
||||||
|
} else if (!((el = item.select("div[class*=\"search-message\"]").first()) == null)) {
|
||||||
|
//result.errorMessage = el.text();
|
||||||
|
throw new NothingFoundException(el.text());
|
||||||
|
|
||||||
|
// video item type
|
||||||
|
} else if (!((el = item.select("div[class*=\"yt-lockup-video\"").first()) == null)) {
|
||||||
|
collector.commit(extractPreviewInfo(el));
|
||||||
|
} else {
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
collector.addError(new Exception("unexpected element found:\"" + el + "\""));
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
|
||||||
throw new ParsingException(e);
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
|
return collector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<String> suggestionList(String query,String contentCountry, Downloader dl)
|
public ArrayList<String> suggestionList(String query, String contentCountry, Downloader dl)
|
||||||
throws IOException, ParsingException {
|
throws IOException, ParsingException {
|
||||||
|
|
||||||
ArrayList<String> suggestions = new ArrayList<>();
|
ArrayList<String> suggestions = new ArrayList<>();
|
||||||
|
@ -167,103 +137,115 @@ public class YoutubeSearchEngine implements SearchEngine {
|
||||||
|
|
||||||
String response = dl.download(url);
|
String response = dl.download(url);
|
||||||
|
|
||||||
|
//TODO: Parse xml data using Jsoup not done
|
||||||
|
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder dBuilder;
|
||||||
|
org.w3c.dom.Document doc = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
dBuilder = dbFactory.newDocumentBuilder();
|
||||||
|
doc = dBuilder.parse(new InputSource(
|
||||||
|
new ByteArrayInputStream(response.getBytes("utf-8"))));
|
||||||
|
doc.getDocumentElement().normalize();
|
||||||
|
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||||
|
throw new ParsingException("Could not parse document.");
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Parse xml data using Jsoup not done
|
try {
|
||||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
NodeList nList = doc.getElementsByTagName("CompleteSuggestion");
|
||||||
DocumentBuilder dBuilder;
|
for (int temp = 0; temp < nList.getLength(); temp++) {
|
||||||
org.w3c.dom.Document doc = null;
|
|
||||||
|
|
||||||
try {
|
NodeList nList1 = doc.getElementsByTagName("suggestion");
|
||||||
dBuilder = dbFactory.newDocumentBuilder();
|
Node nNode1 = nList1.item(temp);
|
||||||
doc = dBuilder.parse(new InputSource(
|
if (nNode1.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
new ByteArrayInputStream(response.getBytes("utf-8"))));
|
org.w3c.dom.Element eElement = (org.w3c.dom.Element) nNode1;
|
||||||
doc.getDocumentElement().normalize();
|
suggestions.add(eElement.getAttribute("data"));
|
||||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doc != null) {
|
|
||||||
NodeList nList = doc.getElementsByTagName("CompleteSuggestion");
|
|
||||||
for (int temp = 0; temp < nList.getLength(); temp++) {
|
|
||||||
|
|
||||||
NodeList nList1 = doc.getElementsByTagName("suggestion");
|
|
||||||
Node nNode1 = nList1.item(temp);
|
|
||||||
if (nNode1.getNodeType() == Node.ELEMENT_NODE) {
|
|
||||||
org.w3c.dom.Element eElement = (org.w3c.dom.Element) nNode1;
|
|
||||||
suggestions.add(eElement.getAttribute("data"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Log.e(TAG, "GREAT FUCKING ERROR");
|
|
||||||
}
|
}
|
||||||
return suggestions;
|
return suggestions;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new ParsingException(e);
|
throw new ParsingException("Could not get suggestions form document.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getWebpageUrl(Element item) {
|
private StreamPreviewInfoExtractor extractPreviewInfo(final Element item) {
|
||||||
Element el = item.select("div[class*=\"yt-lockup-video\"").first();
|
return new StreamPreviewInfoExtractor() {
|
||||||
Element dl = el.select("h3").first().select("a").first();
|
@Override
|
||||||
return dl.attr("abs:href");
|
public String getWebPageUrl() throws ParsingException {
|
||||||
}
|
Element el = item.select("div[class*=\"yt-lockup-video\"").first();
|
||||||
|
Element dl = el.select("h3").first().select("a").first();
|
||||||
|
return dl.attr("abs:href");
|
||||||
|
}
|
||||||
|
|
||||||
private String getTitle(Element item) {
|
@Override
|
||||||
Element el = item.select("div[class*=\"yt-lockup-video\"").first();
|
public String getTitle() throws ParsingException {
|
||||||
Element dl = el.select("h3").first().select("a").first();
|
Element el = item.select("div[class*=\"yt-lockup-video\"").first();
|
||||||
return dl.text();
|
Element dl = el.select("h3").first().select("a").first();
|
||||||
}
|
return dl.text();
|
||||||
|
}
|
||||||
|
|
||||||
private String getDuration(Element item) {
|
@Override
|
||||||
try {
|
public String getDuration() throws ParsingException {
|
||||||
return item.select("span[class=\"video-time\"]").first().text();
|
try {
|
||||||
} catch(Exception e) {
|
return item.select("span[class=\"video-time\"]").first().text();
|
||||||
e.printStackTrace();
|
} catch(Exception e) {
|
||||||
}
|
e.printStackTrace();
|
||||||
return "";
|
}
|
||||||
}
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
private String getUploader(Element item) {
|
@Override
|
||||||
return item.select("div[class=\"yt-lockup-byline\"]").first()
|
public String getUploader() throws ParsingException {
|
||||||
.select("a").first()
|
return item.select("div[class=\"yt-lockup-byline\"]").first()
|
||||||
.text();
|
.select("a").first()
|
||||||
}
|
.text();
|
||||||
|
}
|
||||||
|
|
||||||
private String getUploadDate(Element item) {
|
@Override
|
||||||
return item.select("div[class=\"yt-lockup-meta\"]").first()
|
public String getUploadDate() throws ParsingException {
|
||||||
.select("li").first()
|
return item.select("div[class=\"yt-lockup-meta\"]").first()
|
||||||
.text();
|
.select("li").first()
|
||||||
}
|
.text();
|
||||||
|
}
|
||||||
|
|
||||||
private long getViewCount(Element item) throws Parser.RegexException{
|
@Override
|
||||||
String output;
|
public long getViewCount() throws ParsingException {
|
||||||
String input = item.select("div[class=\"yt-lockup-meta\"]").first()
|
String output;
|
||||||
.select("li").get(1)
|
String input = item.select("div[class=\"yt-lockup-meta\"]").first()
|
||||||
.text();
|
.select("li").get(1)
|
||||||
output = Parser.matchGroup1("([0-9,\\. ]*)", input)
|
.text();
|
||||||
.replace(" ", "")
|
output = Parser.matchGroup1("([0-9,\\. ]*)", input)
|
||||||
.replace(".", "")
|
.replace(" ", "")
|
||||||
.replace(",", "");
|
.replace(".", "")
|
||||||
|
.replace(",", "");
|
||||||
|
|
||||||
if(Long.parseLong(output) == 30) {
|
try {
|
||||||
Log.d(TAG, "bla");
|
return Long.parseLong(output);
|
||||||
}
|
} catch (NumberFormatException e) {
|
||||||
return Long.parseLong(output);
|
// if this happens the video probably has no views
|
||||||
}
|
if(!input.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
throw new ParsingException("Could not handle input: " + input, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getThumbnailUrl(Element item) {
|
@Override
|
||||||
String url;
|
public String getThumbnailUrl() throws ParsingException {
|
||||||
Element te = item.select("div[class=\"yt-thumb video-thumb\"]").first()
|
String url;
|
||||||
.select("img").first();
|
Element te = item.select("div[class=\"yt-thumb video-thumb\"]").first()
|
||||||
url = te.attr("abs:src");
|
.select("img").first();
|
||||||
// Sometimes youtube sends links to gif files which somehow seem to not exist
|
url = te.attr("abs:src");
|
||||||
// anymore. Items with such gif also offer a secondary image source. So we are going
|
// Sometimes youtube sends links to gif files which somehow seem to not exist
|
||||||
// to use that if we've caught such an item.
|
// anymore. Items with such gif also offer a secondary image source. So we are going
|
||||||
if (url.contains(".gif")) {
|
// to use that if we've caught such an item.
|
||||||
url = te.attr("abs:data-thumb");
|
if (url.contains(".gif")) {
|
||||||
}
|
url = te.attr("abs:data-thumb");
|
||||||
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.Downloader;
|
import org.schabi.newpipe.extractor.Downloader;
|
||||||
import org.schabi.newpipe.extractor.StreamExtractor;
|
import org.schabi.newpipe.extractor.StreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.VideoUrlIdHandler;
|
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
|
||||||
import org.schabi.newpipe.extractor.SearchEngine;
|
import org.schabi.newpipe.extractor.SearchEngine;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -40,7 +40,7 @@ public class YoutubeService implements StreamingService {
|
||||||
@Override
|
@Override
|
||||||
public StreamExtractor getExtractorInstance(String url, Downloader downloader)
|
public StreamExtractor getExtractorInstance(String url, Downloader downloader)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
VideoUrlIdHandler urlIdHandler = new YoutubeVideoUrlIdHandler();
|
StreamUrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler();
|
||||||
if(urlIdHandler.acceptUrl(url)) {
|
if(urlIdHandler.acceptUrl(url)) {
|
||||||
return new YoutubeStreamExtractor(url, downloader) ;
|
return new YoutubeStreamExtractor(url, downloader) ;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class YoutubeService implements StreamingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoUrlIdHandler getUrlIdHandler() {
|
public StreamUrlIdHandler getUrlIdHandler() {
|
||||||
return new YoutubeVideoUrlIdHandler();
|
return new YoutubeStreamUrlIdHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ import org.schabi.newpipe.extractor.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.Downloader;
|
import org.schabi.newpipe.extractor.Downloader;
|
||||||
import org.schabi.newpipe.extractor.Parser;
|
import org.schabi.newpipe.extractor.Parser;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.VideoUrlIdHandler;
|
import org.schabi.newpipe.extractor.StreamInfo;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
|
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
|
||||||
import org.schabi.newpipe.extractor.StreamExtractor;
|
import org.schabi.newpipe.extractor.StreamExtractor;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.VideoInfo;
|
|
||||||
import org.schabi.newpipe.extractor.VideoPreviewInfo;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -52,9 +52,6 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
// exceptions
|
// exceptions
|
||||||
|
|
||||||
public class DecryptException extends ParsingException {
|
public class DecryptException extends ParsingException {
|
||||||
DecryptException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
DecryptException(String message, Throwable cause) {
|
DecryptException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
@ -69,8 +66,8 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LiveStreamException extends ContentNotAvailableException {
|
public class LiveStreamException extends ContentNotAvailableException {
|
||||||
LiveStreamException() {
|
LiveStreamException(String message) {
|
||||||
super();
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +176,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
// cached values
|
// cached values
|
||||||
private static volatile String decryptionCode = "";
|
private static volatile String decryptionCode = "";
|
||||||
|
|
||||||
VideoUrlIdHandler urlidhandler = new YoutubeVideoUrlIdHandler();
|
StreamUrlIdHandler urlidhandler = new YoutubeStreamUrlIdHandler();
|
||||||
String pageUrl = "";
|
String pageUrl = "";
|
||||||
|
|
||||||
private Downloader downloader;
|
private Downloader downloader;
|
||||||
|
@ -250,7 +247,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
throw new ParsingException("Could not parse yt player config", e);
|
throw new ParsingException("Could not parse yt player config", e);
|
||||||
}
|
}
|
||||||
if (isLiveStream) {
|
if (isLiveStream) {
|
||||||
throw new LiveStreamException();
|
throw new LiveStreamException("This is a Life stream. Can't use those right now.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return playerArgs;
|
return playerArgs;
|
||||||
|
@ -433,8 +430,8 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VideoInfo.AudioStream> getAudioStreams() throws ParsingException {
|
public List<StreamInfo.AudioStream> getAudioStreams() throws ParsingException {
|
||||||
Vector<VideoInfo.AudioStream> audioStreams = new Vector<>();
|
Vector<StreamInfo.AudioStream> audioStreams = new Vector<>();
|
||||||
try{
|
try{
|
||||||
String encoded_url_map;
|
String encoded_url_map;
|
||||||
// playerArgs could be null if the video is age restricted
|
// playerArgs could be null if the video is age restricted
|
||||||
|
@ -461,7 +458,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
+ decryptSignature(tags.get("s"), decryptionCode);
|
+ decryptSignature(tags.get("s"), decryptionCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioStreams.add(new VideoInfo.AudioStream(streamUrl,
|
audioStreams.add(new StreamInfo.AudioStream(streamUrl,
|
||||||
itagItem.mediaFormatId,
|
itagItem.mediaFormatId,
|
||||||
itagItem.bandWidth,
|
itagItem.bandWidth,
|
||||||
itagItem.samplingRate));
|
itagItem.samplingRate));
|
||||||
|
@ -475,8 +472,8 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VideoInfo.VideoStream> getVideoStreams() throws ParsingException {
|
public List<StreamInfo.VideoStream> getVideoStreams() throws ParsingException {
|
||||||
Vector<VideoInfo.VideoStream> videoStreams = new Vector<>();
|
Vector<StreamInfo.VideoStream> videoStreams = new Vector<>();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
String encoded_url_map;
|
String encoded_url_map;
|
||||||
|
@ -504,7 +501,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
streamUrl = streamUrl + "&signature="
|
streamUrl = streamUrl + "&signature="
|
||||||
+ decryptSignature(tags.get("s"), decryptionCode);
|
+ decryptSignature(tags.get("s"), decryptionCode);
|
||||||
}
|
}
|
||||||
videoStreams.add(new VideoInfo.VideoStream(
|
videoStreams.add(new StreamInfo.VideoStream(
|
||||||
streamUrl,
|
streamUrl,
|
||||||
itagItem.mediaFormatId,
|
itagItem.mediaFormatId,
|
||||||
itagItem.resolutionString));
|
itagItem.resolutionString));
|
||||||
|
@ -527,7 +524,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VideoInfo.VideoStream> getVideoOnlyStreams() throws ParsingException {
|
public List<StreamInfo.VideoStream> getVideoOnlyStreams() throws ParsingException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,7 +635,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoPreviewInfo getNextVideo() throws ParsingException {
|
public StreamPreviewInfo getNextVideo() throws ParsingException {
|
||||||
try {
|
try {
|
||||||
return extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
return extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
||||||
.select("li").first());
|
.select("li").first());
|
||||||
|
@ -648,9 +645,9 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector<VideoPreviewInfo> getRelatedVideos() throws ParsingException {
|
public Vector<StreamPreviewInfo> getRelatedVideos() throws ParsingException {
|
||||||
try {
|
try {
|
||||||
Vector<VideoPreviewInfo> relatedVideos = new Vector<>();
|
Vector<StreamPreviewInfo> 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) {
|
||||||
|
@ -664,8 +661,8 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoUrlIdHandler getUrlIdConverter() {
|
public StreamUrlIdHandler getUrlIdConverter() {
|
||||||
return new YoutubeVideoUrlIdHandler();
|
return new YoutubeStreamUrlIdHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -674,10 +671,10 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Provides information about links to other videos on the video page, such as related videos.
|
/**Provides information about links to other videos on the video page, such as related videos.
|
||||||
* This is encapsulated in a VideoPreviewInfo object,
|
* This is encapsulated in a StreamPreviewInfo object,
|
||||||
* which is a subset of the fields in a full VideoInfo.*/
|
* which is a subset of the fields in a full StreamInfo.*/
|
||||||
private VideoPreviewInfo extractVideoPreviewInfo(Element li) throws ParsingException {
|
private StreamPreviewInfo extractVideoPreviewInfo(Element li) throws ParsingException {
|
||||||
VideoPreviewInfo info = new VideoPreviewInfo();
|
StreamPreviewInfo info = new StreamPreviewInfo();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
info.webpage_url = li.select("a.content-link").first()
|
info.webpage_url = li.select("a.content-link").first()
|
||||||
|
@ -718,7 +715,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
info.thumbnail_url = "https:" + info.thumbnail_url;
|
info.thumbnail_url = "https:" + info.thumbnail_url;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ParsingException(e);
|
throw new ParsingException("Could not get video preview info", e);
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -772,7 +769,7 @@ public class YoutubeStreamExtractor implements StreamExtractor {
|
||||||
Function decryptionFunc = (Function) scope.get("decrypt", scope);
|
Function decryptionFunc = (Function) scope.get("decrypt", scope);
|
||||||
result = decryptionFunc.call(context, scope, scope, new Object[]{encryptedSig});
|
result = decryptionFunc.call(context, scope, scope, new Object[]{encryptedSig});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new DecryptException(e);
|
throw new DecryptException("could not get decrypt signature", e);
|
||||||
} finally {
|
} finally {
|
||||||
Context.exit();
|
Context.exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package org.schabi.newpipe.extractor.services.youtube;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.Parser;
|
import org.schabi.newpipe.extractor.Parser;
|
||||||
import org.schabi.newpipe.extractor.ParsingException;
|
import org.schabi.newpipe.extractor.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.VideoUrlIdHandler;
|
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
@ -11,7 +11,7 @@ import java.net.URLDecoder;
|
||||||
* Created by Christian Schabesberger on 02.02.16.
|
* Created by Christian Schabesberger on 02.02.16.
|
||||||
*
|
*
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* YoutubeVideoUrlIdHandler.java is part of NewPipe.
|
* YoutubeStreamUrlIdHandler.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
|
||||||
|
@ -27,7 +27,7 @@ import java.net.URLDecoder;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class YoutubeVideoUrlIdHandler implements VideoUrlIdHandler {
|
public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler {
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
@Override
|
@Override
|
||||||
public String getVideoUrl(String videoId) {
|
public String getVideoUrl(String videoId) {
|
125
app/src/main/res/layout/activity_error.xml
Normal file
125
app/src/main/res/layout/activity_error.xml
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ErrorActivity">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scrollView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorSorryView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/sorry_string"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messageWhatHappenedView"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:text="@string/what_happened_headline"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorMessageView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:text="@string/info_labels"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorDeviceHeadlineView"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:text="@string/what_device_headline"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/errorInfoLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorInfoLabelsView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@android:color/black"
|
||||||
|
android:text="@string/info_labels"/>
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorInfosView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorDetailView"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:text="@string/error_details_headline"/>
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/horizontalScrollView"
|
||||||
|
android:layout_gravity="center" >
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:typeface="monospace"/>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorYourComment"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:text="@string/your_comment"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/errorCommentBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/errorReportButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/error_report_button_text" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
9
app/src/main/res/menu/error_menu.xml
Normal file
9
app/src/main/res/menu/error_menu.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/menu_item_share_error"
|
||||||
|
android:title="@string/share"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:icon="@drawable/ic_share_black"/>
|
||||||
|
</menu>
|
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
|
@ -35,5 +35,8 @@
|
||||||
<!-- Paddings & Margins -->
|
<!-- Paddings & Margins -->
|
||||||
<dimen name="video_item_detail_like_margin">6sp</dimen>
|
<dimen name="video_item_detail_like_margin">6sp</dimen>
|
||||||
<dimen name="video_item_detail_play_fab_margin">20dp</dimen>
|
<dimen name="video_item_detail_play_fab_margin">20dp</dimen>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -15,7 +15,7 @@
|
||||||
<string name="download">Download</string>
|
<string name="download">Download</string>
|
||||||
<string name="search">Search</string>
|
<string name="search">Search</string>
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="did_you_mean">Did you mean: </string>
|
<string name="did_you_mean">Did you mean: %1$s ?</string>
|
||||||
<string name="search_page">Search page: </string>
|
<string name="search_page">Search page: </string>
|
||||||
<string name="share_dialog_title">Share with:</string>
|
<string name="share_dialog_title">Share with:</string>
|
||||||
<string name="choose_browser">Choose browser:</string>
|
<string name="choose_browser">Choose browser:</string>
|
||||||
|
@ -84,10 +84,25 @@
|
||||||
<string name="could_not_load_thumbnails">Could not load all Thumbnails</string>
|
<string name="could_not_load_thumbnails">Could not load all Thumbnails</string>
|
||||||
<string name="youtube_signature_decryption_error">Could not decrypt video url signature.</string>
|
<string name="youtube_signature_decryption_error">Could not decrypt video url signature.</string>
|
||||||
<string name="parsing_error">Could not parse website.</string>
|
<string name="parsing_error">Could not parse website.</string>
|
||||||
|
<string name="light_parsing_error">Could not parse website complete.</string>
|
||||||
<string name="content_not_available">Content not available.</string>
|
<string name="content_not_available">Content not available.</string>
|
||||||
<string name="blocked_by_gema">Blocked by GEMA.</string>
|
<string name="blocked_by_gema">Blocked by GEMA.</string>
|
||||||
<string name="could_not_setup_download_menu">Could not setup download menu.</string>
|
<string name="could_not_setup_download_menu">Could not setup download menu.</string>
|
||||||
<string name="live_streams_not_supported">This is a LIVE STREAM. These are not yet supported.</string>
|
<string name="live_streams_not_supported">This is a LIVE STREAM. These are not yet supported.</string>
|
||||||
|
<string name="could_not_get_stream">Could not get any stream.</string>
|
||||||
|
<!-- error activity -->
|
||||||
|
<string name="sorry_string">Sorry that should not happen.</string>
|
||||||
|
<string name="guru_meditation" translatable="false">Guru Meditation.</string>
|
||||||
|
<string name="error_report_button_text">Report error via mail</string>
|
||||||
|
<string name="error_snackbar_message">Sorry some errors occurred.</string>
|
||||||
|
<string name="error_snackbar_action">REPORT</string>
|
||||||
|
<string name="what_device_headline">Info:</string>
|
||||||
|
<string name="what_happened_headline">What happened:</string>
|
||||||
|
<string name="info_labels">What:\\nRequest:\\nContent Lang:\\nService:\\nGMT Time:\\nVersion:\\nOS version:\\nGlob. IP range:</string>
|
||||||
|
<string name="info_searched_lbl">Searched for:</string>
|
||||||
|
<string name="info_requested_stream_lbl">Requested stream:</string>
|
||||||
|
<string name="your_comment">Your comment (in English):</string>
|
||||||
|
<string name="error_details_headline">Details:</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Content descriptions (for better accessibility) -->
|
<!-- Content descriptions (for better accessibility) -->
|
||||||
|
|
Loading…
Reference in a new issue