show channelvideos
This commit is contained in:
parent
c03b106118
commit
4164195fae
|
@ -7,12 +7,13 @@ import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.design.widget.CollapsingToolbarLayout;
|
import android.support.design.widget.CollapsingToolbarLayout;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Toast;
|
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.ExtractionException;
|
||||||
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.StreamPreviewInfo;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ChannelActivity extends AppCompatActivity {
|
public class ChannelActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@ -105,6 +108,16 @@ public class ChannelActivity extends AppCompatActivity {
|
||||||
updateUi(info);
|
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) {
|
} catch(IOException ioe) {
|
||||||
postNewErrorToast(h, R.string.network_error);
|
postNewErrorToast(h, R.string.network_error);
|
||||||
ioe.printStackTrace();
|
ioe.printStackTrace();
|
||||||
|
@ -124,10 +137,12 @@ public class ChannelActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
|
||||||
private void updateUi(final ChannelInfo info) {
|
private void updateUi(final ChannelInfo info) {
|
||||||
|
VideoInfoItemViewCreator viCreator =
|
||||||
|
new VideoInfoItemViewCreator(LayoutInflater.from(this), this, rootView);
|
||||||
CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout);
|
CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout);
|
||||||
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||||
ImageView channelBanner = (ImageView) findViewById(R.id.channel_banner_image);
|
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);
|
FloatingActionButton feedButton = (FloatingActionButton) findViewById(R.id.channel_rss_fab);
|
||||||
ImageView avatarView = (ImageView) findViewById(R.id.channel_avatar_view);
|
ImageView avatarView = (ImageView) findViewById(R.id.channel_avatar_view);
|
||||||
ImageView haloView = (ImageView) findViewById(R.id.channel_avatar_halo);
|
ImageView haloView = (ImageView) findViewById(R.id.channel_avatar_halo);
|
||||||
|
@ -163,6 +178,19 @@ public class ChannelActivity extends AppCompatActivity {
|
||||||
} else {
|
} else {
|
||||||
feedButton.setVisibility(View.GONE);
|
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<StreamPreviewInfo> 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) {
|
private void postNewErrorToast(Handler h, final int stringResource) {
|
||||||
|
|
|
@ -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 <chris.schabesberger@mailbox.org>
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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) {}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
@ -33,15 +37,20 @@ import org.schabi.newpipe.extractor.StreamPreviewInfo;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class VideoInfoItemViewCreator {
|
public class VideoInfoItemViewCreator {
|
||||||
|
|
||||||
|
private View rootView = null; //root view of the activty
|
||||||
|
private Activity activity = null;
|
||||||
private final LayoutInflater inflater;
|
private final LayoutInflater inflater;
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||||
|
|
||||||
public VideoInfoItemViewCreator(LayoutInflater inflater) {
|
public VideoInfoItemViewCreator(LayoutInflater inflater, Activity a, View rootView) {
|
||||||
this.inflater = inflater;
|
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;
|
ViewHolder holder;
|
||||||
|
|
||||||
// generate holder
|
// generate holder
|
||||||
|
@ -59,15 +68,7 @@ public class VideoInfoItemViewCreator {
|
||||||
holder = (ViewHolder) convertView.getTag();
|
holder = (ViewHolder) convertView.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill with information
|
// fill holder with information
|
||||||
|
|
||||||
/*
|
|
||||||
if(info.thumbnail == null) {
|
|
||||||
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
|
|
||||||
} else {
|
|
||||||
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
holder.itemVideoTitleView.setText(info.title);
|
holder.itemVideoTitleView.setText(info.title);
|
||||||
if(info.uploader != null && !info.uploader.isEmpty()) {
|
if(info.uploader != null && !info.uploader.isEmpty()) {
|
||||||
holder.itemUploaderView.setText(info.uploader);
|
holder.itemUploaderView.setText(info.uploader);
|
||||||
|
@ -100,6 +101,39 @@ public class VideoInfoItemViewCreator {
|
||||||
return convertView;
|
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 {
|
private class ViewHolder {
|
||||||
public ImageView itemThumbnailView;
|
public ImageView itemThumbnailView;
|
||||||
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView, itemViewCountView;
|
public TextView itemVideoTitleView, itemUploaderView, itemDurationView, itemUploadDateView, itemViewCountView;
|
||||||
|
|
|
@ -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) {
|
private void updateInfo(final StreamInfo info) {
|
||||||
try {
|
try {
|
||||||
Context c = getContext();
|
Context c = getContext();
|
||||||
VideoInfoItemViewCreator videoItemViewCreator =
|
VideoInfoItemViewCreator videoItemViewCreator =
|
||||||
new VideoInfoItemViewCreator(LayoutInflater.from(getActivity()));
|
new VideoInfoItemViewCreator(LayoutInflater.from(getActivity()),
|
||||||
|
getActivity(), rootView);
|
||||||
|
|
||||||
RelativeLayout textContentLayout =
|
RelativeLayout textContentLayout =
|
||||||
(RelativeLayout) activity.findViewById(R.id.detailTextContentLayout);
|
(RelativeLayout) activity.findViewById(R.id.detailTextContentLayout);
|
||||||
|
@ -422,7 +404,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
});
|
});
|
||||||
textContentLayout.setVisibility(View.VISIBLE);
|
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);
|
initSimilarVideos(info, videoItemViewCreator);
|
||||||
} else {
|
} else {
|
||||||
activity.findViewById(R.id.detailSimilarTitle).setVisibility(View.GONE);
|
activity.findViewById(R.id.detailSimilarTitle).setVisibility(View.GONE);
|
||||||
|
@ -487,10 +469,6 @@ public class VideoItemDetailFragment extends Fragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
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(),
|
ErrorActivity.reportError(getActivity(),
|
||||||
failReason.getCause(), null, rootView,
|
failReason.getCause(), null, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
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()) {
|
if(info.uploader_thumbnail_url != null && !info.uploader_thumbnail_url.isEmpty()) {
|
||||||
imageLoader.displayImage(info.uploader_thumbnail_url,
|
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) {
|
if(info.thumbnail_url != null && !info.thumbnail_url.isEmpty() && info.next_video != null) {
|
||||||
imageLoader.displayImage(info.next_video.thumbnail_url,
|
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) {
|
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<StreamPreviewInfo> similar = new ArrayList<>(info.related_videos);
|
ArrayList<StreamPreviewInfo> similarStreamsList = new ArrayList<>(info.related_streams);
|
||||||
for (final StreamPreviewInfo item : similar) {
|
|
||||||
View similarView = videoItemViewCreator
|
for (final StreamPreviewInfo item : similarStreamsList) {
|
||||||
|
View itemView = videoItemViewCreator
|
||||||
.getViewFromVideoInfoItem(null, similarLayout, item);
|
.getViewFromVideoInfoItem(null, similarLayout, item);
|
||||||
|
|
||||||
similarView.setClickable(true);
|
itemView = videoItemViewCreator.setupView(itemView, item);
|
||||||
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();
|
|
||||||
|
|
||||||
similarView.setOnTouchListener(new View.OnTouchListener() {
|
similarLayout.addView(itemView);
|
||||||
@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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,8 +202,9 @@ public class VideoItemListFragment extends ListFragment {
|
||||||
setListShown(true);
|
setListShown(true);
|
||||||
updateList(result.resultList);
|
updateList(result.resultList);
|
||||||
if(!result.suggestion.isEmpty()) {
|
if(!result.suggestion.isEmpty()) {
|
||||||
|
|
||||||
Toast.makeText(getActivity(),
|
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();
|
Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -39,12 +40,12 @@ class VideoListAdapter extends BaseAdapter {
|
||||||
private Vector<StreamPreviewInfo> 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(Activity activity, VideoItemListFragment videoListFragment) {
|
||||||
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
|
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(activity), activity, null);
|
||||||
this.listView = videoListFragment.getListView();
|
this.listView = videoListFragment.getListView();
|
||||||
this.listView.setDivider(null);
|
this.listView.setDivider(null);
|
||||||
this.listView.setDividerHeight(0);
|
this.listView.setDividerHeight(0);
|
||||||
this.context = context;
|
this.context = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVideoList(List<StreamPreviewInfo> videos) {
|
public void addVideoList(List<StreamPreviewInfo> videos) {
|
||||||
|
|
|
@ -39,11 +39,15 @@ public abstract class ChannelExtractor {
|
||||||
public String getUrl() { return url; }
|
public String getUrl() { return url; }
|
||||||
public UrlIdHandler getUrlIdHandler() { return urlIdHandler; }
|
public UrlIdHandler getUrlIdHandler() { return urlIdHandler; }
|
||||||
public Downloader getDownloader() { return downloader; }
|
public Downloader getDownloader() { return downloader; }
|
||||||
|
public StreamPreviewInfoCollector getStreamPreviewInfoCollector() {
|
||||||
|
return previewInfoCollector;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract String getChannelName() throws ParsingException;
|
public abstract String getChannelName() throws ParsingException;
|
||||||
public abstract String getAvatarUrl() throws ParsingException;
|
public abstract String getAvatarUrl() throws ParsingException;
|
||||||
public abstract String getBannerUrl() throws ParsingException;
|
public abstract String getBannerUrl() throws ParsingException;
|
||||||
public abstract String getFeedUrl() throws ParsingException;
|
public abstract String getFeedUrl() throws ParsingException;
|
||||||
|
public abstract StreamPreviewInfoCollector getStreams() throws ParsingException;
|
||||||
public int getServiceId() {
|
public int getServiceId() {
|
||||||
return serviceId;
|
return serviceId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,13 @@ public class ChannelInfo {
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
info.errors.add(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;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +71,7 @@ public class ChannelInfo {
|
||||||
public String avatar_url = "";
|
public String avatar_url = "";
|
||||||
public String banner_url = "";
|
public String banner_url = "";
|
||||||
public String feed_url = "";
|
public String feed_url = "";
|
||||||
|
public List<StreamPreviewInfo> related_streams = null;
|
||||||
|
|
||||||
public List<Throwable> errors = new Vector<>();
|
public List<Throwable> errors = new Vector<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,7 @@ public class StreamInfo extends AbstractVideoInfo {
|
||||||
try {
|
try {
|
||||||
// get related videos
|
// get related videos
|
||||||
StreamPreviewInfoCollector c = extractor.getRelatedVideos();
|
StreamPreviewInfoCollector c = extractor.getRelatedVideos();
|
||||||
streamInfo.related_videos = c.getItemList();
|
streamInfo.related_streams = c.getItemList();
|
||||||
streamInfo.errors.addAll(c.getErrors());
|
streamInfo.errors.addAll(c.getErrors());
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
streamInfo.addException(e);
|
streamInfo.addException(e);
|
||||||
|
@ -281,7 +281,7 @@ public class StreamInfo extends AbstractVideoInfo {
|
||||||
public int dislike_count = -1;
|
public int dislike_count = -1;
|
||||||
public String average_rating = "";
|
public String average_rating = "";
|
||||||
public StreamPreviewInfo next_video = null;
|
public StreamPreviewInfo next_video = null;
|
||||||
public List<StreamPreviewInfo> related_videos = null;
|
public List<StreamPreviewInfo> related_streams = null;
|
||||||
//in seconds. some metadata is not passed using a StreamInfo object!
|
//in seconds. some metadata is not passed using a StreamInfo object!
|
||||||
public int start_position = 0;
|
public int start_position = 0;
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,14 @@ import java.io.StringReader;
|
||||||
import org.jsoup.Jsoup;
|
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.AbstractVideoInfo;
|
||||||
import org.schabi.newpipe.extractor.ChannelExtractor;
|
import org.schabi.newpipe.extractor.ChannelExtractor;
|
||||||
import org.schabi.newpipe.extractor.Downloader;
|
import org.schabi.newpipe.extractor.Downloader;
|
||||||
import org.schabi.newpipe.extractor.ExtractionException;
|
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.StreamPreviewInfoCollector;
|
||||||
|
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
|
||||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,18 +59,24 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||||
|
|
||||||
private Downloader downloader;
|
private Downloader downloader;
|
||||||
private final Document doc;
|
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)
|
public YoutubeChannelExtractor(UrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
super(urlIdHandler, url, dl, serviceId);
|
super(urlIdHandler, url, dl, serviceId);
|
||||||
|
|
||||||
siteUrl = urlIdHandler.cleanUrl(url);
|
channelUrl = urlIdHandler.cleanUrl(url) ; //+ "/video?veiw=0&flow=list&sort=dd";
|
||||||
Log.d(TAG, siteUrl);
|
|
||||||
downloader = dl;
|
downloader = dl;
|
||||||
String pageContent = downloader.download(url);
|
// we first need to get the user url. Otherwise we can't find videos
|
||||||
doc = Jsoup.parse(pageContent, url);
|
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
|
@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
|
@Override
|
||||||
public String getFeedUrl() throws ParsingException {
|
public String getFeedUrl() throws ParsingException {
|
||||||
try {
|
try {
|
||||||
|
@ -129,8 +264,21 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUserUrl() throws ParsingException {
|
private String getUserUrl(Document d) throws ParsingException {
|
||||||
return doc.select("span[class=\"qualified-channel-title-text\"]").first()
|
return d.select("span[class=\"qualified-channel-title-text\"]").first()
|
||||||
.select("a").first().attr("abs:href");
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,6 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra
|
||||||
} else {
|
} else {
|
||||||
throw new ParsingException("Could not get Duration: " + getTitle(), e);
|
throw new ParsingException("Could not get Duration: " + getTitle(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,32 +94,13 @@
|
||||||
android:layout_gravity="fill_vertical"
|
android:layout_gravity="fill_vertical"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
android:id="@+id/channel_content_view"
|
android:id="@+id/channel_content_view"
|
||||||
android:visibility="gone">
|
android:visibility="visible">
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
<TextView
|
android:id="@+id/channel_streams_view">
|
||||||
android:layout_width="match_parent"
|
</LinearLayout>
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Lorem ipsum dolor sit amet, sed in vocent nonumes maluisset. No dicant munere apeirian qui, iisque invenire ius in, sea duis illud dolor ex. Ei cum dolorem conclusionemque, in persius feugait efficiendi est, duo cu nonumy graeco ponderum. Efficiendi interpretaris ei pro. Ea salutandi suavitate qualisque qui.
|
|
||||||
|
|
||||||
Cum ad sumo modus, duo et libris posidonium reprehendunt. Ex nam ornatus delectus. Eum quidam repudiandae ne. Te vidit senserit eos, has justo copiosae probatus ne. Ea vim nisl aeterno fabulas, per an legimus laoreet, natum abhorreant sit no. Epicurei cotidieque cu eum, novum tantas at has, ad eam malis reprimique intellegam.
|
|
||||||
|
|
||||||
Posse doming consulatu no mel. Eu tale iudico his. Nibh nostrum ea mei, te amet esse consequat mea. Corrumpit iracundia an eam, omnesque suavitate erroribus mel ei. Ea suavitate urbanitas nec, mei te dolore menandri theophrastus, ad quaeque delicata vix.
|
|
||||||
|
|
||||||
Has sale mucius menandri eu, pri veniam partem deterruisset ei. In nihil nominavi accusata mea, te sit quis veniam. Nam dissentiet conclusionemque id. Ex novum verterem usu.
|
|
||||||
|
|
||||||
Eum fastidii consulatu cu. In nobis iuvaret usu, paulo tincidunt no usu, minim corpora his te. Nam graeco delenit omittam cu. Et sed ipsum fastidii, mea cu altera ullamcorper. Sea id altera menandri deseruisse, munere audire utroque cu pri. Nec solet facilis id. Mucius delectus eu vis, has in augue veniam.
|
|
||||||
Lorem ipsum dolor sit amet, sed in vocent nonumes maluisset. No dicant munere apeirian qui, iisque invenire ius in, sea duis illud dolor ex. Ei cum dolorem conclusionemque, in persius feugait efficiendi est, duo cu nonumy graeco ponderum. Efficiendi interpretaris ei pro. Ea salutandi suavitate qualisque qui.
|
|
||||||
|
|
||||||
Cum ad sumo modus, duo et libris posidonium reprehendunt. Ex nam ornatus delectus. Eum quidam repudiandae ne. Te vidit senserit eos, has justo copiosae probatus ne. Ea vim nisl aeterno fabulas, per an legimus laoreet, natum abhorreant sit no. Epicurei cotidieque cu eum, novum tantas at has, ad eam malis reprimique intellegam.
|
|
||||||
|
|
||||||
Posse doming consulatu no mel. Eu tale iudico his. Nibh nostrum ea mei, te amet esse consequat mea. Corrumpit iracundia an eam, omnesque suavitate erroribus mel ei. Ea suavitate urbanitas nec, mei te dolore menandri theophrastus, ad quaeque delicata vix.
|
|
||||||
|
|
||||||
Has sale mucius menandri eu, pri veniam partem deterruisset ei. In nihil nominavi accusata mea, te sit quis veniam. Nam dissentiet conclusionemque id. Ex novum verterem usu.
|
|
||||||
|
|
||||||
Eum fastidii consulatu cu. In nobis iuvaret usu, paulo tincidunt no usu, minim corpora his te. Nam graeco delenit omittam cu. Et sed ipsum fastidii, mea cu altera ullamcorper. Sea id altera menandri deseruisse, munere audire utroque cu pri. Nec solet facilis id. Mucius delectus eu vis, has in augue veniam."/>
|
|
||||||
</RelativeLayout>
|
|
||||||
</android.support.v4.widget.NestedScrollView>
|
</android.support.v4.widget.NestedScrollView>
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
Loading…
Reference in New Issue