diff --git a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
index 23ae0724d..b1967fd0b 100644
--- a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
@@ -16,6 +16,8 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
+import org.schabi.newpipe.services.VideoInfo;
+
/**
* Created by Christian Schabesberger on 18.08.15.
*
diff --git a/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java b/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java
index c925a40d3..b7eaa4285 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java
+++ b/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java
@@ -33,7 +33,7 @@ class VideoInfoItemViewCreator {
this.inflater = inflater;
}
- public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) {
+ public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) {
ViewHolder holder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.video_item, parent, false);
diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
index d1e3d10be..0173c07de 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
@@ -8,6 +8,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -31,6 +32,7 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.view.MenuItem;
+import android.widget.Toast;
import java.net.URL;
import java.text.DateFormat;
@@ -45,6 +47,7 @@ import java.util.Vector;
import org.schabi.newpipe.services.VideoExtractor;
import org.schabi.newpipe.services.ServiceList;
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);
VideoInfo videoInfo = videoExtractor.getVideoInfo();
h.post(new VideoResultReturnedRunnable(videoInfo));
- if (videoInfo.videoAvailableStatus == VideoInfo.VIDEO_AVAILABLE) {
+ if (videoInfo.errorCode == VideoInfo.NO_ERROR) {
h.post(new SetThumbnailRunnable(
BitmapFactory.decodeStream(
new URL(videoInfo.thumbnail_url)
@@ -224,22 +227,28 @@ public class VideoItemDetailFragment extends Fragment {
FrameLayout nextVideoFrame = (FrameLayout) activity.findViewById(R.id.detailNextVideoFrame);
RelativeLayout nextVideoRootFrame =
(RelativeLayout) activity.findViewById(R.id.detailNextVideoRootLayout);
- View nextVideoView = videoItemViewCreator
- .getViewByVideoInfoItem(null, nextVideoFrame, info.nextVideo);
- nextVideoFrame.addView(nextVideoView);
- Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton);
- Button similarVideosButton = (Button) activity.findViewById(R.id.detailShowSimilarButton);
+ Button backgroundButton = (Button)
+ activity.findViewById(R.id.detailVideoThumbnailWindowBackgroundButton);
- textContentLayout.setVisibility(View.VISIBLE);
- playVideoButton.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
- if(!showNextVideoItem) {
- nextVideoRootFrame.setVisibility(View.GONE);
- similarVideosButton.setVisibility(View.GONE);
- }
- switch (info.videoAvailableStatus) {
- case VideoInfo.VIDEO_AVAILABLE: {
+ switch (info.errorCode) {
+ case VideoInfo.NO_ERROR: {
+ View nextVideoView = videoItemViewCreator
+ .getViewFromVideoInfoItem(null, nextVideoFrame, info.nextVideo);
+ nextVideoFrame.addView(nextVideoView);
+
+
+ Button nextVideoButton = (Button) activity.findViewById(R.id.detailNextVideoButton);
+ Button similarVideosButton = (Button) activity.findViewById(R.id.detailShowSimilarButton);
+
+ textContentLayout.setVisibility(View.VISIBLE);
+ playVideoButton.setVisibility(View.VISIBLE);
+ if (!showNextVideoItem) {
+ nextVideoRootFrame.setVisibility(View.GONE);
+ similarVideosButton.setVisibility(View.GONE);
+ }
+
videoTitleView.setText(info.title);
uploaderView.setText(info.uploader);
actionBarHandler.setChannelName(info.uploader);
@@ -287,30 +296,41 @@ public class VideoItemDetailFragment extends Fragment {
streamList[i] = streamsToUse.get(i);
}
actionBarHandler.setStreams(streamList, info.audioStreams);
- }
- nextVideoButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent detailIntent =
- new Intent(getActivity(), VideoItemDetailActivity.class);
+ nextVideoButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent detailIntent =
+ new Intent(getActivity(), VideoItemDetailActivity.class);
/*detailIntent.putExtra(
VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */
- detailIntent.putExtra(
- VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
- //todo: make id dynamic the following line is crap
- detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
- startActivity(detailIntent);
- }
- });
+ detailIntent.putExtra(
+ VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
+ //todo: make id dynamic the following line is crap
+ detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
+ startActivity(detailIntent);
+ }
+ });
+ }
break;
- case VideoInfo.VIDEO_UNAVAILABLE_GEMA:
+ case VideoInfo.ERROR_BLOCKED_BY_GEMA:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
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;
- case VideoInfo.VIDEO_UNAVAILABLE:
+ case VideoInfo.ERROR_NO_SPECIFIED_ERROR:
thumbnailView.setImageBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.not_available_monkey));
+ Toast.makeText(activity, info.errorMessage, Toast.LENGTH_LONG)
+ .show();
break;
default:
Log.e(TAG, "Video Available Status not known.");
diff --git a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java b/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java
index 4e35a5a20..e85e74e22 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java
+++ b/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java
@@ -96,7 +96,7 @@ class VideoListAdapter extends BaseAdapter {
@Override
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)) {
convertView.setBackgroundColor(ContextCompat.getColor(context,R.color.primaryColorYoutube));
diff --git a/app/src/main/java/org/schabi/newpipe/services/VideoExtractor.java b/app/src/main/java/org/schabi/newpipe/services/VideoExtractor.java
index ff2b736d4..9da25fc92 100644
--- a/app/src/main/java/org/schabi/newpipe/services/VideoExtractor.java
+++ b/app/src/main/java/org/schabi/newpipe/services/VideoExtractor.java
@@ -20,8 +20,6 @@ package org.schabi.newpipe.services;
* along with NewPipe. If not, see .
*/
-import org.schabi.newpipe.VideoInfo;
-
/**Scrapes information from a video streaming service (eg, YouTube).*/
@SuppressWarnings("ALL")
@@ -46,54 +44,60 @@ public abstract class VideoExtractor {
videoInfo.webpage_url = pageUrl;
}
- if(videoInfo.title.isEmpty()) {
- videoInfo.title = getTitle();
- }
+ if(getErrorCode() == VideoInfo.NO_ERROR) {
- if(videoInfo.duration < 1) {
- videoInfo.duration = getLength();
- }
+ if (videoInfo.title.isEmpty()) {
+ videoInfo.title = getTitle();
+ }
+
+ if (videoInfo.duration < 1) {
+ videoInfo.duration = getLength();
+ }
- if(videoInfo.uploader.isEmpty()) {
- videoInfo.uploader = getUploader();
- }
+ if (videoInfo.uploader.isEmpty()) {
+ videoInfo.uploader = getUploader();
+ }
- if(videoInfo.description.isEmpty()) {
- videoInfo.description = getDescription();
- }
+ if (videoInfo.description.isEmpty()) {
+ videoInfo.description = getDescription();
+ }
- if(videoInfo.view_count == -1) {
- videoInfo.view_count = getViews();
- }
+ if (videoInfo.view_count == -1) {
+ videoInfo.view_count = getViews();
+ }
- if(videoInfo.upload_date.isEmpty()) {
- videoInfo.upload_date = getUploadDate();
- }
+ if (videoInfo.upload_date.isEmpty()) {
+ videoInfo.upload_date = getUploadDate();
+ }
- if(videoInfo.thumbnail_url.isEmpty()) {
- videoInfo.thumbnail_url = getThumbnailUrl();
- }
+ if (videoInfo.thumbnail_url.isEmpty()) {
+ videoInfo.thumbnail_url = getThumbnailUrl();
+ }
- if(videoInfo.id.isEmpty()) {
- videoInfo.id = getVideoId(pageUrl);
- }
+ if (videoInfo.id.isEmpty()) {
+ videoInfo.id = getVideoId(pageUrl);
+ }
- /** Load and extract audio*/
- if(videoInfo.audioStreams == null) {
- videoInfo.audioStreams = getAudioStreams();
- }
- /** Extract video stream url*/
- if(videoInfo.videoStreams == null) {
- videoInfo.videoStreams = getVideoStreams();
- }
+ /** Load and extract audio*/
+ if (videoInfo.audioStreams == null) {
+ videoInfo.audioStreams = getAudioStreams();
+ }
+ /** Extract video stream url*/
+ if (videoInfo.videoStreams == null) {
+ videoInfo.videoStreams = getVideoStreams();
+ }
- if(videoInfo.uploader_thumbnail_url.isEmpty()) {
- videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl();
- }
+ if (videoInfo.uploader_thumbnail_url.isEmpty()) {
+ videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl();
+ }
- if(videoInfo.startPosition < 0) {
- videoInfo.startPosition = getTimeStamp();
+ if (videoInfo.startPosition < 0) {
+ videoInfo.startPosition = getTimeStamp();
+ }
+ } else {
+ videoInfo.errorCode = getErrorCode();
+ videoInfo.errorMessage = getErrorMessage();
}
//Bitmap thumbnail = null;
@@ -102,6 +106,9 @@ public abstract class VideoExtractor {
return videoInfo;
}
+
+ protected abstract int getErrorCode();
+ protected abstract String getErrorMessage();
protected abstract String getVideoUrl(String videoId);
protected abstract String getVideoId(String siteUrl);
protected abstract int getTimeStamp();
diff --git a/app/src/main/java/org/schabi/newpipe/VideoInfo.java b/app/src/main/java/org/schabi/newpipe/services/VideoInfo.java
similarity index 86%
rename from app/src/main/java/org/schabi/newpipe/VideoInfo.java
rename to app/src/main/java/org/schabi/newpipe/services/VideoInfo.java
index 89e576aae..dcf871d8c 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoInfo.java
+++ b/app/src/main/java/org/schabi/newpipe/services/VideoInfo.java
@@ -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 java.util.List;
@@ -28,11 +29,20 @@ import java.util.List;
@SuppressWarnings("ALL")
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 description = "";
public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null;
- public int videoAvailableStatus = VIDEO_AVAILABLE;
+ public int errorCode = NO_ERROR;
+ public String errorMessage = "";
public int duration = -1;
/*YouTube-specific fields
@@ -45,11 +55,6 @@ public class VideoInfo extends AbstractVideoInfo {
public List relatedVideos = null;
public int startPosition = -1;//in seconds. some metadata is not passed using a VideoInfo object!
- public static final int VIDEO_AVAILABLE = 0x00;
- public static final int VIDEO_UNAVAILABLE = 0x01;
- public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
-
-
public VideoInfo() {}
diff --git a/app/src/main/java/org/schabi/newpipe/services/youtube/YoutubeVideoExtractor.java b/app/src/main/java/org/schabi/newpipe/services/youtube/YoutubeVideoExtractor.java
index bb8338909..659b0b7e4 100644
--- a/app/src/main/java/org/schabi/newpipe/services/youtube/YoutubeVideoExtractor.java
+++ b/app/src/main/java/org/schabi/newpipe/services/youtube/YoutubeVideoExtractor.java
@@ -15,7 +15,7 @@ import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.services.VideoExtractor;
import org.schabi.newpipe.MediaFormat;
-import org.schabi.newpipe.VideoInfo;
+import org.schabi.newpipe.services.VideoInfo;
import org.schabi.newpipe.VideoPreviewInfo;
import org.xmlpull.v1.XmlPullParser;
@@ -53,6 +53,8 @@ public class YoutubeVideoExtractor extends VideoExtractor {
private final Document doc;
private JSONObject jsonObj;
private JSONObject playerArgs;
+ private int errorCode = VideoInfo.NO_ERROR;
+ private String errorMessage = "";
// static values
private static final String DECRYPTION_FUNC_NAME="decrypt";
@@ -60,7 +62,6 @@ public class YoutubeVideoExtractor extends VideoExtractor {
// cached values
private static volatile String decryptionCode = "";
-
public YoutubeVideoExtractor(String pageUrl) {
super(pageUrl);//most common videoInfo fields are now set in our superclass, for all services
String pageContents = Downloader.download(cleanUrl(pageUrl));
@@ -69,12 +70,18 @@ public class YoutubeVideoExtractor extends VideoExtractor {
//attempt to load the youtube js player JSON arguments
try {
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);
playerArgs = jsonObj.getJSONObject("args");
} catch (Exception e) {//if this fails, the video is most likely not available.
// 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");
}
@@ -367,80 +374,95 @@ public class YoutubeVideoExtractor extends VideoExtractor {
@Override
public VideoInfo getVideoInfo() {
+ //todo: @medovax i like your work, but what the fuck:
videoInfo = super.getVideoInfo();
- //todo: replace this with a call to getVideoId, if possible
- videoInfo.id = matchGroup1("v=([0-9a-zA-Z_-]{11})", pageUrl);
- if(videoInfo.audioStreams == null
- || videoInfo.audioStreams.length == 0) {
- Log.e(TAG, "uninitialised audio streams!");
- }
+ if(errorCode == VideoInfo.NO_ERROR) {
+ //todo: replace this with a call to getVideoId, if possible
+ videoInfo.id = matchGroup1("v=([0-9a-zA-Z_-]{11})", pageUrl);
- if(videoInfo.videoStreams == null
- || videoInfo.videoStreams.length == 0) {
- Log.e(TAG, "uninitialised video streams!");
- }
+ if (videoInfo.audioStreams == null
+ || videoInfo.audioStreams.length == 0) {
+ Log.e(TAG, "uninitialised audio streams!");
+ }
- videoInfo.age_limit = 0;
+ if (videoInfo.videoStreams == null
+ || videoInfo.videoStreams.length == 0) {
+ Log.e(TAG, "uninitialised video streams!");
+ }
- //average rating
- try {
- videoInfo.average_rating = playerArgs.getString("avg_rating");
- } catch (JSONException e) {
- e.printStackTrace();
- }
+ videoInfo.age_limit = 0;
- //---------------------------------------
- // extracting information from html page
- //---------------------------------------
+ //average rating
+ try {
+ videoInfo.average_rating = playerArgs.getString("avg_rating");
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ //---------------------------------------
+ // extracting information from html page
+ //---------------------------------------
+
+ /* Code does not work here anymore.
// 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")) {
videoInfo.videoAvailableStatus = VideoInfo.VIDEO_UNAVAILABLE_GEMA;
}
}
+ */
- String likesString = "";
- String dislikesString = "";
- try {
- // likes
- likesString = doc.select("button.like-button-renderer-like-button").first()
- .select("span.yt-uix-button-content").first().text();
- videoInfo.like_count = Integer.parseInt(likesString.replaceAll("[^\\d]", ""));
- // dislikes
- dislikesString = doc.select("button.like-button-renderer-dislike-button").first()
- .select("span.yt-uix-button-content").first().text();
+ String likesString = "";
+ String dislikesString = "";
+ try {
+ // likes
+ likesString = doc.select("button.like-button-renderer-like-button").first()
+ .select("span.yt-uix-button-content").first().text();
+ videoInfo.like_count = Integer.parseInt(likesString.replaceAll("[^\\d]", ""));
+ // dislikes
+ dislikesString = doc.select("button.like-button-renderer-dislike-button").first()
+ .select("span.yt-uix-button-content").first().text();
- videoInfo.dislike_count = Integer.parseInt(dislikesString.replaceAll("[^\\d]", ""));
- } catch(NumberFormatException nfe) {
- Log.e(TAG, "failed to parse likesString \""+likesString+"\" and dislikesString \""+
- dislikesString+"\" as integers");
- } catch(Exception e) {
- // if it fails we know that the video does not offer dislikes.
- e.printStackTrace();
- videoInfo.like_count = 0;
- videoInfo.dislike_count = 0;
- }
-
- // next video
- videoInfo.nextVideo = extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
- .select("li").first());
-
- // related videos
- Vector relatedVideos = new Vector<>();
- for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
- // first check if we have a playlist. If so leave them out
- if(li.select("a[class*=\"content-link\"]").first() != null) {
- relatedVideos.add(extractVideoPreviewInfo(li));
+ videoInfo.dislike_count = Integer.parseInt(dislikesString.replaceAll("[^\\d]", ""));
+ } catch (NumberFormatException nfe) {
+ Log.e(TAG, "failed to parse likesString \"" + likesString + "\" and dislikesString \"" +
+ dislikesString + "\" as integers");
+ } catch (Exception e) {
+ // if it fails we know that the video does not offer dislikes.
+ e.printStackTrace();
+ videoInfo.like_count = 0;
+ videoInfo.dislike_count = 0;
}
+
+ // next video
+ videoInfo.nextVideo = extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
+ .select("li").first());
+
+ // related videos
+ Vector relatedVideos = new Vector<>();
+ for (Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
+ // first check if we have a playlist. If so leave them out
+ if (li.select("a[class*=\"content-link\"]").first() != null) {
+ relatedVideos.add(extractVideoPreviewInfo(li));
+ }
+ }
+ //todo: replace conversion
+ videoInfo.relatedVideos = relatedVideos;
+ //videoInfo.relatedVideos = relatedVideos.toArray(new VideoPreviewInfo[relatedVideos.size()]);
}
- //todo: replace conversion
- videoInfo.relatedVideos = relatedVideos;
- //videoInfo.relatedVideos = relatedVideos.toArray(new VideoPreviewInfo[relatedVideos.size()]);
return videoInfo;
}
+ @Override
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return errorMessage;
+ }
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
if(!dashManifest.contains("/signature/")) {
@@ -610,6 +632,13 @@ public class YoutubeVideoExtractor extends VideoExtractor {
new Exception("failed to find pattern \""+pattern+"\"").printStackTrace();
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;
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fa767bdf4..177280277 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,6 +52,7 @@
ETC
%1$s - NewPipe
Playing in background
+ https://www.c3s.cc/
Video preview thumbnail