From 4164195fae0713103f95b28ecad06907d82c507a Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 1 Aug 2016 21:50:41 +0200 Subject: [PATCH] show channelvideos --- .../org/schabi/newpipe/ChannelActivity.java | 32 +++- .../newpipe/ImageErrorLoadingListener.java | 65 +++++++ .../newpipe/VideoInfoItemViewCreator.java | 56 ++++-- .../newpipe/VideoItemDetailFragment.java | 69 ++------ .../schabi/newpipe/VideoItemListFragment.java | 3 +- .../org/schabi/newpipe/VideoListAdapter.java | 7 +- .../newpipe/extractor/ChannelExtractor.java | 4 + .../schabi/newpipe/extractor/ChannelInfo.java | 8 + .../schabi/newpipe/extractor/StreamInfo.java | 4 +- .../youtube/YoutubeChannelExtractor.java | 162 +++++++++++++++++- .../YoutubeStreamPreviewInfoExtractor.java | 2 - app/src/main/res/layout/activity_channel.xml | 31 +--- 12 files changed, 334 insertions(+), 109 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/ImageErrorLoadingListener.java diff --git a/app/src/main/java/org/schabi/newpipe/ChannelActivity.java b/app/src/main/java/org/schabi/newpipe/ChannelActivity.java index d7176fcf5..d673536d6 100644 --- a/app/src/main/java/org/schabi/newpipe/ChannelActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ChannelActivity.java @@ -7,12 +7,13 @@ import android.os.Bundle; import android.os.Handler; import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.Toast; @@ -25,9 +26,11 @@ import org.schabi.newpipe.extractor.ChannelInfo; import org.schabi.newpipe.extractor.ExtractionException; import org.schabi.newpipe.extractor.ParsingException; import org.schabi.newpipe.extractor.ServiceList; +import org.schabi.newpipe.extractor.StreamPreviewInfo; import org.schabi.newpipe.extractor.StreamingService; import java.io.IOException; +import java.util.ArrayList; public class ChannelActivity extends AppCompatActivity { @@ -105,6 +108,16 @@ public class ChannelActivity extends AppCompatActivity { updateUi(info); } }); + + // look for non critical errors during extraction + if(info != null && + !info.errors.isEmpty()) { + Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:"); + for (Throwable e : info.errors) { + e.printStackTrace(); + Log.e(TAG, "------"); + } + } } catch(IOException ioe) { postNewErrorToast(h, R.string.network_error); ioe.printStackTrace(); @@ -124,10 +137,12 @@ public class ChannelActivity extends AppCompatActivity { private void updateUi(final ChannelInfo info) { + VideoInfoItemViewCreator viCreator = + new VideoInfoItemViewCreator(LayoutInflater.from(this), this, rootView); CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout); ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar); ImageView channelBanner = (ImageView) findViewById(R.id.channel_banner_image); - View channelContentView = (View) findViewById(R.id.channel_content_view); + View channelContentView = findViewById(R.id.channel_content_view); FloatingActionButton feedButton = (FloatingActionButton) findViewById(R.id.channel_rss_fab); ImageView avatarView = (ImageView) findViewById(R.id.channel_avatar_view); ImageView haloView = (ImageView) findViewById(R.id.channel_avatar_halo); @@ -163,6 +178,19 @@ public class ChannelActivity extends AppCompatActivity { } else { feedButton.setVisibility(View.GONE); } + + initVideos(info, viCreator); + } + + private void initVideos(final ChannelInfo info, VideoInfoItemViewCreator viCreator) { + LinearLayout streamLayout = (LinearLayout) findViewById(R.id.channel_streams_view); + ArrayList streamsList = new ArrayList<>(info.related_streams); + + for(final StreamPreviewInfo streamInfo : streamsList) { + View itemView = viCreator.getViewFromVideoInfoItem(null, streamLayout, streamInfo); + itemView = viCreator.setupView(itemView, streamInfo); + streamLayout.addView(itemView); + } } private void postNewErrorToast(Handler h, final int stringResource) { diff --git a/app/src/main/java/org/schabi/newpipe/ImageErrorLoadingListener.java b/app/src/main/java/org/schabi/newpipe/ImageErrorLoadingListener.java new file mode 100644 index 000000000..b63a7cb98 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ImageErrorLoadingListener.java @@ -0,0 +1,65 @@ +package org.schabi.newpipe; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.view.View; + +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; + +import org.schabi.newpipe.ErrorActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.ServiceList; + +/** + * Created by Christian Schabesberger on 01.08.16. + * + * Copyright (C) Christian Schabesberger 2015 + * VideoInfoItemViewCreator.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class ImageErrorLoadingListener implements ImageLoadingListener { + + private int serviceId = -1; + private Activity activity = null; + private View rootView = null; + + public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) { + this.activity = activity; + this.serviceId= serviceId; + this.rootView = rootView; + } + + @Override + public void onLoadingStarted(String imageUri, View view) {} + + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason) { + ErrorActivity.reportError(activity, + failReason.getCause(), null, rootView, + ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE, + ServiceList.getNameOfService(serviceId), imageUri, + R.string.could_not_load_image)); + } + + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + + } + + @Override + public void onLoadingCancelled(String imageUri, View view) {} +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java b/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java index 4b7c3c332..ae7024be2 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java +++ b/app/src/main/java/org/schabi/newpipe/VideoInfoItemViewCreator.java @@ -1,6 +1,10 @@ package org.schabi.newpipe; +import android.app.Activity; +import android.content.Intent; +import android.content.res.TypedArray; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -33,15 +37,20 @@ import org.schabi.newpipe.extractor.StreamPreviewInfo; */ public class VideoInfoItemViewCreator { + + private View rootView = null; //root view of the activty + private Activity activity = null; private final LayoutInflater inflater; private ImageLoader imageLoader = ImageLoader.getInstance(); private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build(); - public VideoInfoItemViewCreator(LayoutInflater inflater) { + public VideoInfoItemViewCreator(LayoutInflater inflater, Activity a, View rootView) { this.inflater = inflater; + activity = a; + this.rootView = rootView; } - public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, StreamPreviewInfo info) { + public View getViewFromVideoInfoItem(View convertView, ViewGroup parent, final StreamPreviewInfo info) { ViewHolder holder; // generate holder @@ -59,15 +68,7 @@ public class VideoInfoItemViewCreator { holder = (ViewHolder) convertView.getTag(); } - // fill with information - - /* - if(info.thumbnail == null) { - holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail); - } else { - holder.itemThumbnailView.setImageBitmap(info.thumbnail); - } - */ + // fill holder with information holder.itemVideoTitleView.setText(info.title); if(info.uploader != null && !info.uploader.isEmpty()) { holder.itemUploaderView.setText(info.uploader); @@ -100,6 +101,39 @@ public class VideoInfoItemViewCreator { return convertView; } + public View setupView(View convertView, final StreamPreviewInfo info) { + convertView.setClickable(true); + convertView.setFocusable(true); + + + int[] attrs = new int[]{R.attr.selectableItemBackground}; + TypedArray typedArray = activity.obtainStyledAttributes(attrs); + int backgroundResource = typedArray.getResourceId(0, 0); + convertView.setBackgroundResource(backgroundResource); + typedArray.recycle(); + + convertView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + Intent detailIntent = new Intent(activity, VideoItemDetailActivity.class); + detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, info.webpage_url); + detailIntent.putExtra( + VideoItemDetailFragment.STREAMING_SERVICE, info.service_id); + activity.startActivity(detailIntent); + return true; + } + return false; + } + }); + + ImageView rthumb = (ImageView) convertView.findViewById(R.id.itemThumbnailView); + imageLoader.displayImage(info.thumbnail_url, rthumb, + displayImageOptions, new ImageErrorLoadingListener(activity, rootView, info.service_id)); + + return convertView; + } + private class ViewHolder { public ImageView itemThumbnailView; public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView, itemViewCountView; diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java index 4509404d8..1ecd841bf 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java @@ -262,31 +262,13 @@ public class VideoItemDetailFragment extends Fragment { } } - private class ThumbnailLoadingListener implements ImageLoadingListener { - @Override - public void onLoadingStarted(String imageUri, View view) {} - - @Override - public void onLoadingFailed(String imageUri, View view, FailReason failReason) { - if(getContext() != null) { - Toast.makeText(VideoItemDetailFragment.this.getActivity(), - R.string.could_not_load_thumbnails, Toast.LENGTH_LONG).show(); - } - failReason.getCause().printStackTrace(); - } - - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {} - - @Override - public void onLoadingCancelled(String imageUri, View view) {} - } private void updateInfo(final StreamInfo info) { try { Context c = getContext(); VideoInfoItemViewCreator videoItemViewCreator = - new VideoInfoItemViewCreator(LayoutInflater.from(getActivity())); + new VideoInfoItemViewCreator(LayoutInflater.from(getActivity()), + getActivity(), rootView); RelativeLayout textContentLayout = (RelativeLayout) activity.findViewById(R.id.detailTextContentLayout); @@ -422,7 +404,7 @@ public class VideoItemDetailFragment extends Fragment { }); textContentLayout.setVisibility(View.VISIBLE); - if(info.related_videos != null && !info.related_videos.isEmpty()) { + if(info.related_streams != null && !info.related_streams.isEmpty()) { initSimilarVideos(info, videoItemViewCreator); } else { activity.findViewById(R.id.detailSimilarTitle).setVisibility(View.GONE); @@ -487,10 +469,6 @@ public class VideoItemDetailFragment extends Fragment { @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { - Toast.makeText(VideoItemDetailFragment.this.getActivity(), - R.string.could_not_load_thumbnails, Toast.LENGTH_LONG).show(); - failReason.getCause().printStackTrace(); - ErrorActivity.reportError(getActivity(), failReason.getCause(), null, rootView, ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE, @@ -512,11 +490,13 @@ public class VideoItemDetailFragment extends Fragment { } if(info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) { imageLoader.displayImage(info.uploader_thumbnail_url, - uploaderThumb, displayImageOptions, new ThumbnailLoadingListener()); + uploaderThumb, displayImageOptions, + new ImageErrorLoadingListener(activity, rootView, info.service_id)); } if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty() && info.next_video != null) { imageLoader.displayImage(info.next_video.thumbnail_url, - nextVideoThumb, displayImageOptions, new ThumbnailLoadingListener()); + nextVideoThumb, displayImageOptions, + new ImageErrorLoadingListener(activity, rootView, info.service_id)); } } @@ -710,38 +690,15 @@ public class VideoItemDetailFragment extends Fragment { private void initSimilarVideos(final StreamInfo info, VideoInfoItemViewCreator videoItemViewCreator) { LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similarVideosView); - ArrayList similar = new ArrayList<>(info.related_videos); - for (final StreamPreviewInfo item : similar) { - View similarView = videoItemViewCreator + ArrayList similarStreamsList = new ArrayList<>(info.related_streams); + + for (final StreamPreviewInfo item : similarStreamsList) { + View itemView = videoItemViewCreator .getViewFromVideoInfoItem(null, similarLayout, item); - similarView.setClickable(true); - similarView.setFocusable(true); - int[] attrs = new int[]{R.attr.selectableItemBackground}; - TypedArray typedArray = activity.obtainStyledAttributes(attrs); - int backgroundResource = typedArray.getResourceId(0, 0); - similarView.setBackgroundResource(backgroundResource); - typedArray.recycle(); + itemView = videoItemViewCreator.setupView(itemView, item); - similarView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - Intent detailIntent = new Intent(activity, VideoItemDetailActivity.class); - detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, item.webpage_url); - detailIntent.putExtra( - VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId); - startActivity(detailIntent); - return true; - } - return false; - } - }); - - similarLayout.addView(similarView); - ImageView rthumb = (ImageView)similarView.findViewById(R.id.itemThumbnailView); - imageLoader.displayImage(item.thumbnail_url, rthumb, - displayImageOptions, new ThumbnailLoadingListener()); + similarLayout.addView(itemView); } } diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java index 232a2123b..2b9d57658 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java @@ -202,8 +202,9 @@ public class VideoItemListFragment extends ListFragment { setListShown(true); updateList(result.resultList); if(!result.suggestion.isEmpty()) { + Toast.makeText(getActivity(), - String.format(getString(R.string.did_you_mean), result.suggestion), + String.format(getActivity().getString(R.string.did_you_mean), result.suggestion), Toast.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java b/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java index d69b842cf..885d063ab 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/VideoListAdapter.java @@ -1,5 +1,6 @@ package org.schabi.newpipe; +import android.app.Activity; import android.content.Context; import android.support.v4.content.ContextCompat; import android.view.LayoutInflater; @@ -39,12 +40,12 @@ class VideoListAdapter extends BaseAdapter { private Vector videoList = new Vector<>(); private final ListView listView; - public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) { - viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context)); + public VideoListAdapter(Activity activity, VideoItemListFragment videoListFragment) { + viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(activity), activity, null); this.listView = videoListFragment.getListView(); this.listView.setDivider(null); this.listView.setDividerHeight(0); - this.context = context; + this.context = activity; } public void addVideoList(List videos) { diff --git a/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java index 6b4912a37..bf6292bbf 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java @@ -39,11 +39,15 @@ public abstract class ChannelExtractor { public String getUrl() { return url; } public UrlIdHandler getUrlIdHandler() { return urlIdHandler; } public Downloader getDownloader() { return downloader; } + public StreamPreviewInfoCollector getStreamPreviewInfoCollector() { + return previewInfoCollector; + } public abstract String getChannelName() throws ParsingException; public abstract String getAvatarUrl() throws ParsingException; public abstract String getBannerUrl() throws ParsingException; public abstract String getFeedUrl() throws ParsingException; + public abstract StreamPreviewInfoCollector getStreams() throws ParsingException; public int getServiceId() { return serviceId; } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java b/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java index 6f18814d9..0151d895a 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java @@ -55,6 +55,13 @@ public class ChannelInfo { } catch(Exception e) { info.errors.add(e); } + try { + StreamPreviewInfoCollector c = extractor.getStreams(); + info.related_streams = c.getItemList(); + info.errors.addAll(c.getErrors()); + } catch(Exception e) { + info.errors.add(e); + } return info; } @@ -64,6 +71,7 @@ public class ChannelInfo { public String avatar_url = ""; public String banner_url = ""; public String feed_url = ""; + public List related_streams = null; public List errors = new Vector<>(); } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java b/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java index 1c162b97d..9c710114b 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java @@ -253,7 +253,7 @@ public class StreamInfo extends AbstractVideoInfo { try { // get related videos StreamPreviewInfoCollector c = extractor.getRelatedVideos(); - streamInfo.related_videos = c.getItemList(); + streamInfo.related_streams = c.getItemList(); streamInfo.errors.addAll(c.getErrors()); } catch(Exception e) { streamInfo.addException(e); @@ -281,7 +281,7 @@ public class StreamInfo extends AbstractVideoInfo { public int dislike_count = -1; public String average_rating = ""; public StreamPreviewInfo next_video = null; - public List related_videos = null; + public List related_streams = null; //in seconds. some metadata is not passed using a StreamInfo object! public int start_position = 0; diff --git a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java index 4ac26071e..d3029e14c 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelExtractor.java @@ -18,11 +18,14 @@ import java.io.StringReader; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import org.schabi.newpipe.extractor.AbstractVideoInfo; import org.schabi.newpipe.extractor.ChannelExtractor; import org.schabi.newpipe.extractor.Downloader; import org.schabi.newpipe.extractor.ExtractionException; import org.schabi.newpipe.extractor.Parser; import org.schabi.newpipe.extractor.ParsingException; +import org.schabi.newpipe.extractor.StreamPreviewInfoCollector; +import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor; import org.schabi.newpipe.extractor.UrlIdHandler; @@ -56,18 +59,24 @@ public class YoutubeChannelExtractor extends ChannelExtractor { private Downloader downloader; private final Document doc; - private final String siteUrl; + private final String channelUrl; + private String vUrl =""; public YoutubeChannelExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) throws ExtractionException, IOException { super(urlIdHandler, url, dl, serviceId); - siteUrl = urlIdHandler.cleanUrl(url); - Log.d(TAG, siteUrl); + channelUrl = urlIdHandler.cleanUrl(url) ; //+ "/video?veiw=0&flow=list&sort=dd"; downloader = dl; - String pageContent = downloader.download(url); - doc = Jsoup.parse(pageContent, url); + // we first need to get the user url. Otherwise we can't find videos + String channelPageContent = downloader.download(channelUrl); + Document channelDoc = Jsoup.parse(channelPageContent, channelUrl); + String userUrl = getUserUrl(channelDoc); + + vUrl = userUrl + "/videos?veiw=0&flow=list&sort=dd"; + String pageContent = downloader.download(vUrl); + doc = Jsoup.parse(pageContent, vUrl); } @Override @@ -120,6 +129,132 @@ public class YoutubeChannelExtractor extends ChannelExtractor { } } + @Override + public StreamPreviewInfoCollector getStreams() throws ParsingException { + StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector(); + Element ul = doc.select("ul[id=\"browse-items-primary\"]").first(); + + for(final Element li : ul.children()) { + if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) { + collector.commit(new StreamPreviewInfoExtractor() { + @Override + public AbstractVideoInfo.StreamType getStreamType() throws ParsingException { + return AbstractVideoInfo.StreamType.VIDEO_STREAM; + } + + @Override + public String getWebPageUrl() throws ParsingException { + try { + Element el = li.select("div[class=\"feed-item-dismissable\"]").first(); + Element dl = el.select("h3").first().select("a").first(); + return dl.attr("abs:href"); + } catch (Exception e) { + throw new ParsingException("Could not get web page url for the video", e); + } + } + + @Override + public String getTitle() throws ParsingException { + try { + Element el = li.select("div[class=\"feed-item-dismissable\"]").first(); + Element dl = el.select("h3").first().select("a").first(); + return dl.text(); + } catch (Exception e) { + throw new ParsingException("Could not get title", e); + } + } + + @Override + public int getDuration() throws ParsingException { + try { + return YoutubeParsingHelper.parseDurationString( + li.select("span[class=\"video-time\"]").first().text()); + } catch(Exception e) { + if(isLiveStream(li)) { + // -1 for no duration + return -1; + } else { + throw new ParsingException("Could not get Duration: " + getTitle(), e); + } + } + } + + @Override + public String getUploader() throws ParsingException { + return getChannelName(); + } + + @Override + public String getUploadDate() throws ParsingException { + try { + return li.select("div[class=\"yt-lockup-meta\"]").first() + .select("li").first() + .text(); + } catch(Exception e) { + throw new ParsingException("Could not get uplaod date", e); + } + } + + @Override + public long getViewCount() throws ParsingException { + String output; + String input; + try { + input = li.select("div[class=\"yt-lockup-meta\"]").first() + .select("li").get(1) + .text(); + } catch (IndexOutOfBoundsException e) { + if(isLiveStream(li)) { + // -1 for no view count + return -1; + } else { + throw new ParsingException( + "Could not parse yt-lockup-meta although available: " + getTitle(), e); + } + } + + output = Parser.matchGroup1("([0-9,\\. ]*)", input) + .replace(" ", "") + .replace(".", "") + .replace(",", ""); + + try { + return Long.parseLong(output); + } catch (NumberFormatException e) { + // if this happens the video probably has no views + if(!input.isEmpty()) { + return 0; + } else { + throw new ParsingException("Could not handle input: " + input, e); + } + } + } + + @Override + public String getThumbnailUrl() throws ParsingException { + try { + String url; + Element te = li.select("span[class=\"yt-thumb-clip\"]").first() + .select("img").first(); + url = te.attr("abs:src"); + // Sometimes youtube sends links to gif files which somehow seem to not exist + // anymore. Items with such gif also offer a secondary image source. So we are going + // to use that if we've caught such an item. + if (url.contains(".gif")) { + url = te.attr("abs:data-thumb"); + } + return url; + } catch (Exception e) { + throw new ParsingException("Could not get thumbnail url", e); + } + } + }); + } + } + + return collector; + } + @Override public String getFeedUrl() throws ParsingException { try { @@ -129,8 +264,21 @@ public class YoutubeChannelExtractor extends ChannelExtractor { } } - private String getUserUrl() throws ParsingException { - return doc.select("span[class=\"qualified-channel-title-text\"]").first() + private String getUserUrl(Document d) throws ParsingException { + return d.select("span[class=\"qualified-channel-title-text\"]").first() .select("a").first().attr("abs:href"); } + + private boolean isLiveStream(Element item) { + Element bla = item.select("span[class*=\"yt-badge-live\"]").first(); + + if(bla == null) { + // sometimes livestreams dont have badges but sill are live streams + // if video time is not available we most likly have an offline livestream + if(item.select("span[class*=\"video-time\"]").first() == null) { + return true; + } + } + return bla != null; + } } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamPreviewInfoExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamPreviewInfoExtractor.java index a23bf904c..371cc96ff 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamPreviewInfoExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamPreviewInfoExtractor.java @@ -66,8 +66,6 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra } else { throw new ParsingException("Could not get Duration: " + getTitle(), e); } - - } } diff --git a/app/src/main/res/layout/activity_channel.xml b/app/src/main/res/layout/activity_channel.xml index db9139185..3e061a76a 100644 --- a/app/src/main/res/layout/activity_channel.xml +++ b/app/src/main/res/layout/activity_channel.xml @@ -94,32 +94,13 @@ android:layout_gravity="fill_vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/channel_content_view" - android:visibility="gone"> + android:visibility="visible"> - - - + android:layout_height="match_parent" + android:id="@+id/channel_streams_view"> +