small hacky error handling refactoring, and add link to c3s

This commit is contained in:
Christian Schabesberger 2015-12-28 00:32:38 +01:00
parent 8760792426
commit bd6cc22e63
8 changed files with 199 additions and 135 deletions

View file

@ -16,6 +16,8 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import org.schabi.newpipe.services.VideoInfo;
/** /**
* Created by Christian Schabesberger on 18.08.15. * Created by Christian Schabesberger on 18.08.15.
* *

View file

@ -33,7 +33,7 @@ class VideoInfoItemViewCreator {
this.inflater = inflater; this.inflater = inflater;
} }
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) { public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) {
ViewHolder holder; ViewHolder holder;
if(convertView == null) { if(convertView == null) {
convertView = inflater.inflate(R.layout.video_item, parent, false); convertView = inflater.inflate(R.layout.video_item, parent, false);

View file

@ -8,6 +8,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Point; import android.graphics.Point;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -31,6 +32,7 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast;
import java.net.URL; import java.net.URL;
import java.text.DateFormat; import java.text.DateFormat;
@ -45,6 +47,7 @@ import java.util.Vector;
import org.schabi.newpipe.services.VideoExtractor; import org.schabi.newpipe.services.VideoExtractor;
import org.schabi.newpipe.services.ServiceList; import org.schabi.newpipe.services.ServiceList;
import org.schabi.newpipe.services.StreamingService; import org.schabi.newpipe.services.StreamingService;
import org.schabi.newpipe.services.VideoInfo;
/** /**
@ -114,7 +117,7 @@ public class VideoItemDetailFragment extends Fragment {
this.videoExtractor = service.getExtractorInstance(videoUrl); this.videoExtractor = service.getExtractorInstance(videoUrl);
VideoInfo videoInfo = videoExtractor.getVideoInfo(); VideoInfo videoInfo = videoExtractor.getVideoInfo();
h.post(new VideoResultReturnedRunnable(videoInfo)); h.post(new VideoResultReturnedRunnable(videoInfo));
if (videoInfo.videoAvailableStatus == VideoInfo.VIDEO_AVAILABLE) { if (videoInfo.errorCode == VideoInfo.NO_ERROR) {
h.post(new SetThumbnailRunnable( h.post(new SetThumbnailRunnable(
BitmapFactory.decodeStream( BitmapFactory.decodeStream(
new URL(videoInfo.thumbnail_url) new URL(videoInfo.thumbnail_url)
@ -224,22 +227,28 @@ public class VideoItemDetailFragment extends Fragment {
FrameLayout nextVideoFrame = (FrameLayout) activity.findViewById(R.id.detailNextVideoFrame); FrameLayout nextVideoFrame = (FrameLayout) activity.findViewById(R.id.detailNextVideoFrame);
RelativeLayout nextVideoRootFrame = RelativeLayout nextVideoRootFrame =
(RelativeLayout) activity.findViewById(R.id.detailNextVideoRootLayout); (RelativeLayout) activity.findViewById(R.id.detailNextVideoRootLayout);
Button backgroundButton = (Button)
activity.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton);
progressBar.setVisibility(View.GONE);
switch (info.errorCode) {
case VideoInfo.NO_ERROR: {
View nextVideoView = videoItemViewCreator View nextVideoView = videoItemViewCreator
.getViewByVideoInfoItem(null, nextVideoFrame, info.nextVideo); .getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo);
nextVideoFrame.addView(nextVideoView); nextVideoFrame.addView(nextVideoView);
Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton); Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton);
Button similarVideosButton = (Button) activity.findViewById(R.id.detailShowSimilarButton); Button similarVideosButton = (Button) activity.findViewById(R.id.detailShowSimilarButton);
textContentLayout.setVisibility(View.VISIBLE); textContentLayout.setVisibility(View.VISIBLE);
playVideoButton.setVisibility(View.VISIBLE); playVideoButton.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE); if (!showNextVideoItem) {
if(!showNextVideoItem) {
nextVideoRootFrame.setVisibility(View.GONE); nextVideoRootFrame.setVisibility(View.GONE);
similarVideosButton.setVisibility(View.GONE); similarVideosButton.setVisibility(View.GONE);
} }
switch (info.videoAvailableStatus) {
case VideoInfo.VIDEO_AVAILABLE: {
videoTitleView.setText(info.title); videoTitleView.setText(info.title);
uploaderView.setText(info.uploader); uploaderView.setText(info.uploader);
actionBarHandler.setChannelName(info.uploader); actionBarHandler.setChannelName(info.uploader);
@ -287,7 +296,6 @@ public class VideoItemDetailFragment extends Fragment {
streamList[i] = streamsToUse.get(i); streamList[i] = streamsToUse.get(i);
} }
actionBarHandler.setStreams(streamList, info.audioStreams); actionBarHandler.setStreams(streamList, info.audioStreams);
}
nextVideoButton.setOnClickListener(new View.OnClickListener() { nextVideoButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -303,14 +311,26 @@ public class VideoItemDetailFragment extends Fragment {
startActivity(detailIntent); startActivity(detailIntent);
} }
}); });
}
break; break;
case VideoInfo.VIDEO_UNAVAILABLE_GEMA: case VideoInfo.ERROR_BLOCKED_BY_GEMA:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource( thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.gruese_die_gema_unangebracht)); getResources(), R.drawable.gruese_die_gema_unangebracht));
backgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(activity.getString(R.string.c3sUrl)));
activity.startActivity(intent);
}
});
break; break;
case VideoInfo.VIDEO_UNAVAILABLE: case VideoInfo.ERROR_NO_SPECIFIED_ERROR:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource( thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.not_available_monkey)); getResources(), R.drawable.not_available_monkey));
Toast.makeText(activity, info.errorMessage, Toast.LENGTH_LONG)
.show();
break; break;
default: default:
Log.e(TAG, "Video Available Status not known."); Log.e(TAG, "Video Available Status not known.");

View file

@ -96,7 +96,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.getViewByVideoInfoItem(convertView, parent, videoList.get(position)); convertView = viewCreator.getViewFromVideoInfoItem(convertView, parent, videoList.get(position));
if(listView.isItemChecked(position)) { if(listView.isItemChecked(position)) {
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube)); convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube));

View file

@ -20,8 +20,6 @@ package org.schabi.newpipe.services;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>. * along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/ */
import org.schabi.newpipe.VideoInfo;
/**Scrapes information from a video streaming service (eg, YouTube).*/ /**Scrapes information from a video streaming service (eg, YouTube).*/
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
@ -46,55 +44,61 @@ public abstract class VideoExtractor {
videoInfo.webpage_url = pageUrl; videoInfo.webpage_url = pageUrl;
} }
if(videoInfo.title.isEmpty()) { if(getErrorCode() == VideoInfo.NO_ERROR) {
if (videoInfo.title.isEmpty()) {
videoInfo.title = getTitle(); videoInfo.title = getTitle();
} }
if(videoInfo.duration < 1) { if (videoInfo.duration < 1) {
videoInfo.duration = getLength(); videoInfo.duration = getLength();
} }
if(videoInfo.uploader.isEmpty()) { if (videoInfo.uploader.isEmpty()) {
videoInfo.uploader = getUploader(); videoInfo.uploader = getUploader();
} }
if(videoInfo.description.isEmpty()) { if (videoInfo.description.isEmpty()) {
videoInfo.description = getDescription(); videoInfo.description = getDescription();
} }
if(videoInfo.view_count == -1) { if (videoInfo.view_count == -1) {
videoInfo.view_count = getViews(); videoInfo.view_count = getViews();
} }
if(videoInfo.upload_date.isEmpty()) { if (videoInfo.upload_date.isEmpty()) {
videoInfo.upload_date = getUploadDate(); videoInfo.upload_date = getUploadDate();
} }
if(videoInfo.thumbnail_url.isEmpty()) { if (videoInfo.thumbnail_url.isEmpty()) {
videoInfo.thumbnail_url = getThumbnailUrl(); videoInfo.thumbnail_url = getThumbnailUrl();
} }
if(videoInfo.id.isEmpty()) { if (videoInfo.id.isEmpty()) {
videoInfo.id = getVideoId(pageUrl); videoInfo.id = getVideoId(pageUrl);
} }
/** Load and extract audio*/ /** Load and extract audio*/
if(videoInfo.audioStreams == null) { if (videoInfo.audioStreams == null) {
videoInfo.audioStreams = getAudioStreams(); videoInfo.audioStreams = getAudioStreams();
} }
/** Extract video stream url*/ /** Extract video stream url*/
if(videoInfo.videoStreams == null) { if (videoInfo.videoStreams == null) {
videoInfo.videoStreams = getVideoStreams(); videoInfo.videoStreams = getVideoStreams();
} }
if(videoInfo.uploader_thumbnail_url.isEmpty()) { if (videoInfo.uploader_thumbnail_url.isEmpty()) {
videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl(); videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl();
} }
if(videoInfo.startPosition < 0) { if (videoInfo.startPosition < 0) {
videoInfo.startPosition = getTimeStamp(); videoInfo.startPosition = getTimeStamp();
} }
} else {
videoInfo.errorCode = getErrorCode();
videoInfo.errorMessage = getErrorMessage();
}
//Bitmap thumbnail = null; //Bitmap thumbnail = null;
//Bitmap uploader_thumbnail = null; //Bitmap uploader_thumbnail = null;
@ -102,6 +106,9 @@ public abstract class VideoExtractor {
return videoInfo; return videoInfo;
} }
protected abstract int getErrorCode();
protected abstract String getErrorMessage();
protected abstract String getVideoUrl(String videoId); protected abstract String getVideoUrl(String videoId);
protected abstract String getVideoId(String siteUrl); protected abstract String getVideoId(String siteUrl);
protected abstract int getTimeStamp(); protected abstract int getTimeStamp();

View file

@ -1,5 +1,6 @@
package org.schabi.newpipe; package org.schabi.newpipe.services;
import org.schabi.newpipe.VideoPreviewInfo;
import org.schabi.newpipe.services.AbstractVideoInfo; import org.schabi.newpipe.services.AbstractVideoInfo;
import java.util.List; import java.util.List;
@ -28,11 +29,20 @@ import java.util.List;
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
public class VideoInfo extends AbstractVideoInfo { public class VideoInfo extends AbstractVideoInfo {
// If a video could not be parsed, this predefined error codes
// will be returned AND can be parsed by the frontend of the app.
// Error codes:
public final static int NO_ERROR = 0x0;
public final static int ERROR_NO_SPECIFIED_ERROR = 0x1;
// GEMA a german music colecting society.
public final static int ERROR_BLOCKED_BY_GEMA = 0x2;
public String uploader_thumbnail_url = ""; public String uploader_thumbnail_url = "";
public String description = ""; public String description = "";
public VideoStream[] videoStreams = null; public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null; public AudioStream[] audioStreams = null;
public int videoAvailableStatus = VIDEO_AVAILABLE; public int errorCode = NO_ERROR;
public String errorMessage = "";
public int duration = -1; public int duration = -1;
/*YouTube-specific fields /*YouTube-specific fields
@ -45,11 +55,6 @@ public class VideoInfo extends AbstractVideoInfo {
public List<VideoPreviewInfo> relatedVideos = null; public List<VideoPreviewInfo> relatedVideos = null;
public int startPosition = -1;//in seconds. some metadata is not passed using a VideoInfo object! public int startPosition = -1;//in seconds. some metadata is not passed using a VideoInfo object!
public static final int VIDEO_AVAILABLE = 0x00;
public static final int VIDEO_UNAVAILABLE = 0x01;
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
public VideoInfo() {} public VideoInfo() {}

View file

@ -15,7 +15,7 @@ import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.Downloader; import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.services.VideoExtractor; import org.schabi.newpipe.services.VideoExtractor;
import org.schabi.newpipe.MediaFormat; import org.schabi.newpipe.MediaFormat;
import org.schabi.newpipe.VideoInfo; import org.schabi.newpipe.services.VideoInfo;
import org.schabi.newpipe.VideoPreviewInfo; import org.schabi.newpipe.VideoPreviewInfo;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
@ -53,6 +53,8 @@ public class YoutubeVideoExtractor extends VideoExtractor {
private final Document doc; private final Document doc;
private JSONObject jsonObj; private JSONObject jsonObj;
private JSONObject playerArgs; private JSONObject playerArgs;
private int errorCode = VideoInfo.NO_ERROR;
private String errorMessage = "";
// static values // static values
private static final String DECRYPTION_FUNC_NAME="decrypt"; private static final String DECRYPTION_FUNC_NAME="decrypt";
@ -60,7 +62,6 @@ public class YoutubeVideoExtractor extends VideoExtractor {
// cached values // cached values
private static volatile String decryptionCode = ""; private static volatile String decryptionCode = "";
public YoutubeVideoExtractor(String pageUrl) { public YoutubeVideoExtractor(String pageUrl) {
super(pageUrl);//most common videoInfo fields are now set in our superclass, for all services super(pageUrl);//most common videoInfo fields are now set in our superclass, for all services
String pageContents = Downloader.download(cleanUrl(pageUrl)); String pageContents = Downloader.download(cleanUrl(pageUrl));
@ -69,12 +70,18 @@ public class YoutubeVideoExtractor extends VideoExtractor {
//attempt to load the youtube js player JSON arguments //attempt to load the youtube js player JSON arguments
try { try {
String jsonString = matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", pageContents); String jsonString = matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", pageContents);
//todo: implement this by try and catch. TESTING THE STRING AGAINST EMPTY IS CONSIDERED POOR STYLE !!!
if(jsonString.isEmpty()) {
errorCode = findErrorReason(doc);
return;
}
jsonObj = new JSONObject(jsonString); jsonObj = new JSONObject(jsonString);
playerArgs = jsonObj.getJSONObject("args"); playerArgs = jsonObj.getJSONObject("args");
} catch (Exception e) {//if this fails, the video is most likely not available. } catch (Exception e) {//if this fails, the video is most likely not available.
// Determining why is done later. // Determining why is done later.
videoInfo.videoAvailableStatus = VideoInfo.VIDEO_UNAVAILABLE; videoInfo.errorCode = VideoInfo.ERROR_NO_SPECIFIED_ERROR;
Log.e(TAG, "Could not load JSON data for Youtube video \""+pageUrl+"\". This most likely means the video is unavailable"); Log.e(TAG, "Could not load JSON data for Youtube video \""+pageUrl+"\". This most likely means the video is unavailable");
} }
@ -367,16 +374,19 @@ public class YoutubeVideoExtractor extends VideoExtractor {
@Override @Override
public VideoInfo getVideoInfo() { public VideoInfo getVideoInfo() {
//todo: @medovax i like your work, but what the fuck:
videoInfo = super.getVideoInfo(); videoInfo = super.getVideoInfo();
if(errorCode == VideoInfo.NO_ERROR) {
//todo: replace this with a call to getVideoId, if possible //todo: replace this with a call to getVideoId, if possible
videoInfo.id = matchGroup1("v=([0-9a-zA-Z_-]{11})", pageUrl); videoInfo.id = matchGroup1("v=([0-9a-zA-Z_-]{11})", pageUrl);
if(videoInfo.audioStreams == null if (videoInfo.audioStreams == null
|| videoInfo.audioStreams.length == 0) { || videoInfo.audioStreams.length == 0) {
Log.e(TAG, "uninitialised audio streams!"); Log.e(TAG, "uninitialised audio streams!");
} }
if(videoInfo.videoStreams == null if (videoInfo.videoStreams == null
|| videoInfo.videoStreams.length == 0) { || videoInfo.videoStreams.length == 0) {
Log.e(TAG, "uninitialised video streams!"); Log.e(TAG, "uninitialised video streams!");
} }
@ -394,12 +404,14 @@ public class YoutubeVideoExtractor extends VideoExtractor {
// extracting information from html page // extracting information from html page
//--------------------------------------- //---------------------------------------
/* Code does not work here anymore.
// Determine what went wrong when the Video is not available // Determine what went wrong when the Video is not available
if(videoInfo.videoAvailableStatus == VideoInfo.VIDEO_UNAVAILABLE) { if(videoInfo.errorCode == VideoInfo.ERROR_NO_SPECIFIED_ERROR) {
if(doc.select("h1[id=\"unavailable-message\"]").first().text().contains("GEMA")) { if(doc.select("h1[id=\"unavailable-message\"]").first().text().contains("GEMA")) {
videoInfo.videoAvailableStatus = VideoInfo.VIDEO_UNAVAILABLE_GEMA; videoInfo.videoAvailableStatus = VideoInfo.VIDEO_UNAVAILABLE_GEMA;
} }
} }
*/
String likesString = ""; String likesString = "";
String dislikesString = ""; String dislikesString = "";
@ -413,10 +425,10 @@ public class YoutubeVideoExtractor extends VideoExtractor {
.select("span.yt-uix-button-content").first().text(); .select("span.yt-uix-button-content").first().text();
videoInfo.dislike_count = Integer.parseInt(dislikesString.replaceAll("[^\\d]", "")); videoInfo.dislike_count = Integer.parseInt(dislikesString.replaceAll("[^\\d]", ""));
} catch(NumberFormatException nfe) { } catch (NumberFormatException nfe) {
Log.e(TAG, "failed to parse likesString \""+likesString+"\" and dislikesString \""+ Log.e(TAG, "failed to parse likesString \"" + likesString + "\" and dislikesString \"" +
dislikesString+"\" as integers"); dislikesString + "\" as integers");
} catch(Exception e) { } catch (Exception e) {
// if it fails we know that the video does not offer dislikes. // if it fails we know that the video does not offer dislikes.
e.printStackTrace(); e.printStackTrace();
videoInfo.like_count = 0; videoInfo.like_count = 0;
@ -429,18 +441,28 @@ public class YoutubeVideoExtractor extends VideoExtractor {
// related videos // related videos
Vector<VideoPreviewInfo> relatedVideos = new Vector<>(); Vector<VideoPreviewInfo> 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) {
relatedVideos.add(extractVideoPreviewInfo(li)); relatedVideos.add(extractVideoPreviewInfo(li));
} }
} }
//todo: replace conversion //todo: replace conversion
videoInfo.relatedVideos = relatedVideos; videoInfo.relatedVideos = relatedVideos;
//videoInfo.relatedVideos = relatedVideos.toArray(new VideoPreviewInfo[relatedVideos.size()]); //videoInfo.relatedVideos = relatedVideos.toArray(new VideoPreviewInfo[relatedVideos.size()]);
}
return videoInfo; return videoInfo;
} }
@Override
public int getErrorCode() {
return errorCode;
}
@Override
public String getErrorMessage() {
return errorMessage;
}
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) { private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
if(!dashManifest.contains("/signature/")) { if(!dashManifest.contains("/signature/")) {
@ -610,6 +632,13 @@ public class YoutubeVideoExtractor extends VideoExtractor {
new Exception("failed to find pattern \""+pattern+"\"").printStackTrace(); new Exception("failed to find pattern \""+pattern+"\"").printStackTrace();
return ""; return "";
} }
}
private int findErrorReason(Document doc) {
errorMessage = doc.select("h1[id=\"unavailable-message\"]").first().text();
if(errorMessage.contains("GEMA")) {
return VideoInfo.ERROR_BLOCKED_BY_GEMA;
}
return VideoInfo.ERROR_NO_SPECIFIED_ERROR;
} }
} }

View file

@ -52,6 +52,7 @@
<string name="settingsCategoryEtcTitle">ETC</string> <string name="settingsCategoryEtcTitle">ETC</string>
<string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string> <string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string>
<string name="backgroundPlayerStartPlayingToast">Playing in background</string> <string name="backgroundPlayerStartPlayingToast">Playing in background</string>
<string name="c3sUrl" translatable="false">https://www.c3s.cc/</string>
<!-- Content descriptions (for better accessibility) --> <!-- Content descriptions (for better accessibility) -->
<string name="itemThumbnailViewDescription">Video preview thumbnail</string> <string name="itemThumbnailViewDescription">Video preview thumbnail</string>