mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	Merge branch 'advancedErrorHandling'
This commit is contained in:
		| @@ -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) --> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Christian Schabesberger
					Christian Schabesberger