mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2024-11-22 11:02:35 +01:00
get channel banner
This commit is contained in:
parent
9a0f61e60b
commit
6beb36f92f
@ -45,4 +45,5 @@ dependencies {
|
||||
compile 'com.google.code.gson:gson:2.4'
|
||||
compile 'com.nononsenseapps:filepicker:2.0.5'
|
||||
testCompile 'junit:junit:4.12'
|
||||
//compile 'net.sourceforge.cssparser:cssparser:0.9.20'
|
||||
}
|
||||
|
@ -79,6 +79,10 @@ public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase {
|
||||
assertTrue(extractor.getUploadDate().length() > 0);
|
||||
}
|
||||
|
||||
public void testGetChannelUrl() throws ParsingException {
|
||||
assertTrue(extractor.getChannelUrl().length() > 0);
|
||||
}
|
||||
|
||||
public void testGetThumbnailUrl() throws ParsingException {
|
||||
assertTrue(extractor.getThumbnailUrl(),
|
||||
extractor.getThumbnailUrl().contains(HTTPS));
|
||||
|
@ -43,7 +43,7 @@ public class ActivityCommunicator {
|
||||
public volatile Bitmap backgroundPlayerThumbnail;
|
||||
|
||||
// Sent from any activity to ErrorActivity.
|
||||
public volatile List<Exception> errorList;
|
||||
public volatile List<Throwable> errorList;
|
||||
public volatile Class returnActivity;
|
||||
public volatile ErrorActivity.ErrorInfo errorInfo;
|
||||
}
|
||||
|
@ -1,28 +1,170 @@
|
||||
package org.schabi.newpipe;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
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.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
|
||||
import org.schabi.newpipe.extractor.ChannelExtractor;
|
||||
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.StreamingService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ChannelActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = ChannelActivity.class.toString();
|
||||
private View rootView = null;
|
||||
|
||||
class FailedThumbnailListener implements ImageLoadingListener {
|
||||
|
||||
int serviceId = -1;
|
||||
|
||||
public FailedThumbnailListener(int serviceId) {
|
||||
this.serviceId= serviceId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoadingStarted(String imageUri, View view) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
ErrorActivity.reportError(ChannelActivity.this,
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
// intent const
|
||||
public static final String CHANNEL_URL = "channel_url";
|
||||
public static final String SERVICE_ID = "service_id";
|
||||
|
||||
private int serviceId = -1;
|
||||
private String channelUrl = "";
|
||||
|
||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_channel);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
rootView = findViewById(R.id.rootView);
|
||||
setSupportActionBar(toolbar);
|
||||
Intent i = getIntent();
|
||||
channelUrl = i.getStringExtra(CHANNEL_URL);
|
||||
serviceId = i.getIntExtra(SERVICE_ID, -1);
|
||||
|
||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
// start processing
|
||||
Thread channelExtractorThread = new Thread(new Runnable() {
|
||||
Handler h = new Handler();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
StreamingService service = ServiceList.getService(serviceId);
|
||||
ChannelExtractor extractor = service.getChannelExtractorInstance(
|
||||
channelUrl, new Downloader());
|
||||
|
||||
final ChannelInfo info = ChannelInfo.getInfo(extractor, new Downloader());
|
||||
|
||||
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUi(info);
|
||||
}
|
||||
});
|
||||
} catch(IOException ioe) {
|
||||
postNewErrorToast(h, R.string.network_error);
|
||||
ioe.printStackTrace();
|
||||
} catch(ParsingException pe) {
|
||||
pe.printStackTrace();
|
||||
} catch(ExtractionException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
channelExtractorThread.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void updateUi(final ChannelInfo info) {
|
||||
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);
|
||||
FloatingActionButton feedButton = (FloatingActionButton) findViewById(R.id.channel_rss_fab);
|
||||
|
||||
progressBar.setVisibility(View.GONE);
|
||||
channelContentView.setVisibility(View.VISIBLE);
|
||||
|
||||
if(info.channel_name != null && !info.channel_name.isEmpty()) {
|
||||
ctl.setTitle(info.channel_name);
|
||||
}
|
||||
|
||||
if(info.banner_url != null && !info.banner_url.isEmpty()) {
|
||||
imageLoader.displayImage(info.banner_url, channelBanner,
|
||||
new FailedThumbnailListener(info.service_id));
|
||||
}
|
||||
|
||||
if(info.feed_url != null && !info.feed_url.isEmpty()) {
|
||||
feedButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show();
|
||||
Log.d(TAG, info.feed_url);
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(info.feed_url + ".rss"));
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
feedButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void postNewErrorToast(Handler h, final int stringResource) {
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(ChannelActivity.this,
|
||||
stringResource, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -79,16 +79,19 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
public static final int GET_SUGGESTIONS = 2;
|
||||
public static final int SOMETHING_ELSE = 3;
|
||||
public static final int USER_REPORT = 4;
|
||||
public static final int LOAD_IMAGE = 5;
|
||||
public static final String SEARCHED_STRING = "searched";
|
||||
public static final String REQUESTED_STREAM_STRING = "requested stream";
|
||||
public static final String GET_SUGGESTIONS_STRING = "get suggestions";
|
||||
public static final String SOMETHING_ELSE_STRING = "something";
|
||||
public static final String USER_REPORT_STRING = "user report";
|
||||
public static final String LOAD_IMAGE_STRING = "load image";
|
||||
|
||||
|
||||
public static final String ERROR_EMAIL_ADDRESS = "crashreport@newpipe.schabi.org";
|
||||
public static final String ERROR_EMAIL_SUBJECT = "Exception in NewPipe " + BuildConfig.VERSION_NAME;
|
||||
|
||||
private List<Exception> errorList;
|
||||
private List<Throwable> errorList;
|
||||
private ErrorInfo errorInfo;
|
||||
private Class returnActivity;
|
||||
private String currentTimeStamp;
|
||||
@ -102,7 +105,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
private TextView infoView;
|
||||
private TextView errorMessageView;
|
||||
|
||||
public static void reportError(final Context context, final List<Exception> el,
|
||||
public static void reportError(final Context context, final List<Throwable> el,
|
||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||
|
||||
if (rootView != null) {
|
||||
@ -129,9 +132,9 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public static void reportError(final Context context, final Exception e,
|
||||
public static void reportError(final Context context, final Throwable e,
|
||||
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
|
||||
List<Exception> el = null;
|
||||
List<Throwable> el = null;
|
||||
if(e != null) {
|
||||
el = new Vector<>();
|
||||
el.add(e);
|
||||
@ -140,10 +143,10 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// async call
|
||||
public static void reportError(Handler handler, final Context context, final Exception e,
|
||||
public static void reportError(Handler handler, final Context context, final Throwable e,
|
||||
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
||||
|
||||
List<Exception> el = null;
|
||||
List<Throwable> el = null;
|
||||
if(e != null) {
|
||||
el = new Vector<>();
|
||||
el.add(e);
|
||||
@ -152,7 +155,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// async call
|
||||
public static void reportError(Handler handler, final Context context, final List<Exception> el,
|
||||
public static void reportError(Handler handler, final Context context, final List<Throwable> el,
|
||||
final Class returnAcitivty, final View rootView, final ErrorInfo errorInfo) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
@ -171,7 +174,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.error_report_title);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Error turing exception handling");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -252,10 +255,10 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
return sw.getBuffer().toString();
|
||||
}
|
||||
|
||||
private String formErrorText(List<Exception> el) {
|
||||
private String formErrorText(List<Throwable> el) {
|
||||
String text = "";
|
||||
if(el != null) {
|
||||
for (Exception e : el) {
|
||||
for (Throwable e : el) {
|
||||
text += "-------------------------------------\n"
|
||||
+ getStackTrace(e);
|
||||
}
|
||||
@ -313,7 +316,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
|
||||
JSONArray exceptionArray = new JSONArray();
|
||||
if(errorList != null) {
|
||||
for (Exception e : errorList) {
|
||||
for (Throwable e : errorList) {
|
||||
exceptionArray.put(getStackTrace(e));
|
||||
}
|
||||
}
|
||||
@ -322,7 +325,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
errorObject.put("user_comment", userCommentBox.getText().toString());
|
||||
|
||||
return errorObject.toString(3);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Error while erroring: Could not build json");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -390,7 +393,7 @@ public class ErrorActivity extends AppCompatActivity {
|
||||
|
||||
ipRange = Parser.matchGroup1("([0-9]*\\.[0-9]*\\.)[0-9]*\\.[0-9]*", ip)
|
||||
+ "0.0";
|
||||
} catch(Exception e) {
|
||||
} catch(Throwable e) {
|
||||
Log.d(TAG, "Error while error: could not get iprange");
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
@ -116,6 +116,8 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
private DisplayImageOptions displayImageOptions =
|
||||
new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||
|
||||
private View rootView = null;
|
||||
|
||||
|
||||
public interface OnInvokeCreateOptionsMenuListener {
|
||||
void createOptionsMenu();
|
||||
@ -150,7 +152,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
if(streamInfo != null &&
|
||||
!streamInfo.errors.isEmpty()) {
|
||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
||||
for (Exception e : streamInfo.errors) {
|
||||
for (Throwable e : streamInfo.errors) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "------");
|
||||
}
|
||||
@ -449,13 +451,19 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
if(info.channel_url != null && info.channel_url != "") {
|
||||
channelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(activity, ChannelActivity.class);
|
||||
i.putExtra(ChannelActivity.CHANNEL_URL, info.channel_url);
|
||||
i.putExtra(ChannelActivity.SERVICE_ID, info.service_id);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
channelButton.setVisibility(Button.GONE);
|
||||
}
|
||||
|
||||
} catch (java.lang.NullPointerException e) {
|
||||
Log.w(TAG, "updateInfo(): Fragment closed before thread ended work... or else");
|
||||
@ -463,7 +471,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void initThumbnailViews(StreamInfo info, View nextVideoFrame) {
|
||||
private void initThumbnailViews(final StreamInfo info, View nextVideoFrame) {
|
||||
ImageView videoThumbnailView = (ImageView) activity.findViewById(R.id.detailThumbnailView);
|
||||
ImageView uploaderThumb
|
||||
= (ImageView) activity.findViewById(R.id.detailUploaderThumbnailView);
|
||||
@ -482,6 +490,12 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
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,
|
||||
ServiceList.getNameOfService(info.service_id), imageUri,
|
||||
R.string.could_not_load_thumbnails));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -797,7 +811,7 @@ public class VideoItemDetailFragment extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_videoitem_detail, container, false);
|
||||
rootView = inflater.inflate(R.layout.fragment_videoitem_detail, container, false);
|
||||
progressBar = (ProgressBar) rootView.findViewById(R.id.detailProgressBar);
|
||||
|
||||
actionBarHandler = new ActionBarHandler(activity);
|
||||
|
@ -360,7 +360,7 @@ public class VideoItemListActivity extends AppCompatActivity
|
||||
return true;
|
||||
}
|
||||
case R.id.action_report_error: {
|
||||
ErrorActivity.reportError(VideoItemListActivity.this, new Vector<Exception>(),
|
||||
ErrorActivity.reportError(VideoItemListActivity.this, new Vector<Throwable>(),
|
||||
null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT,
|
||||
ServiceList.getNameOfService(currentStreamingServiceId),
|
||||
|
@ -121,7 +121,7 @@ public class VideoItemListFragment extends ListFragment {
|
||||
if(result != null &&
|
||||
!result.errors.isEmpty()) {
|
||||
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
|
||||
for(Exception e : result.errors) {
|
||||
for(Throwable e : result.errors) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "------");
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ public class MainActivity extends AppCompatActivity implements AdapterView.OnIte
|
||||
return true;
|
||||
}
|
||||
case R.id.action_report_error: {
|
||||
ErrorActivity.reportError(MainActivity.this, new Vector<Exception>(),
|
||||
ErrorActivity.reportError(MainActivity.this, new Vector<Throwable>(),
|
||||
null, null,
|
||||
ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT,
|
||||
null,
|
||||
|
@ -43,4 +43,8 @@ public abstract class ChannelExtractor {
|
||||
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 int getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
package org.schabi.newpipe.extractor;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 31.07.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* ChannelInfo.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 ChannelInfo {
|
||||
|
||||
|
||||
public void addException(Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
public static ChannelInfo getInfo(ChannelExtractor extractor, Downloader dl)
|
||||
throws ParsingException {
|
||||
ChannelInfo info = new ChannelInfo();
|
||||
|
||||
// importand data
|
||||
info.service_id = extractor.getServiceId();
|
||||
info.channel_name = extractor.getChannelName();
|
||||
|
||||
try {
|
||||
info.avatar_url = extractor.getAvatarUrl();
|
||||
} catch (Exception e) {
|
||||
info.errors.add(e);
|
||||
}
|
||||
try {
|
||||
info.banner_url = extractor.getBannerUrl();
|
||||
} catch (Exception e) {
|
||||
info.errors.add(e);
|
||||
}
|
||||
try {
|
||||
info.feed_url = extractor.getFeedUrl();
|
||||
} catch(Exception e) {
|
||||
info.errors.add(e);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public int service_id = -1;
|
||||
public String channel_name = "";
|
||||
public String avatar_url = "";
|
||||
public String banner_url = "";
|
||||
public String feed_url = "";
|
||||
|
||||
public List<Throwable> errors = new Vector<>();
|
||||
}
|
@ -43,5 +43,5 @@ public class SearchResult {
|
||||
|
||||
public String suggestion = "";
|
||||
public List<StreamPreviewInfo> resultList = new Vector<>();
|
||||
public List<Exception> errors = new Vector<>();
|
||||
public List<Throwable> errors = new Vector<>();
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ public abstract class StreamExtractor {
|
||||
public abstract String getTitle() throws ParsingException;
|
||||
public abstract String getDescription() throws ParsingException;
|
||||
public abstract String getUploader() throws ParsingException;
|
||||
public abstract String getChannelUrl() throws ParsingException;
|
||||
public abstract int getLength() throws ParsingException;
|
||||
public abstract long getViewCount() throws ParsingException;
|
||||
public abstract String getUploadDate() throws ParsingException;
|
||||
|
@ -188,6 +188,11 @@ public class StreamInfo extends AbstractVideoInfo {
|
||||
} catch(Exception e) {
|
||||
streamInfo.addException(e);
|
||||
}
|
||||
try {
|
||||
streamInfo.channel_url = extractor.getChannelUrl();
|
||||
} catch(Exception e) {
|
||||
streamInfo.addException(e);
|
||||
}
|
||||
try {
|
||||
streamInfo.description = extractor.getDescription();
|
||||
} catch(Exception e) {
|
||||
@ -258,6 +263,7 @@ public class StreamInfo extends AbstractVideoInfo {
|
||||
}
|
||||
|
||||
public String uploader_thumbnail_url = "";
|
||||
public String channel_url = "";
|
||||
public String description = "";
|
||||
|
||||
public List<VideoStream> video_streams = null;
|
||||
@ -279,5 +285,5 @@ public class StreamInfo extends AbstractVideoInfo {
|
||||
//in seconds. some metadata is not passed using a StreamInfo object!
|
||||
public int start_position = 0;
|
||||
|
||||
public List<Exception> errors = new Vector<>();
|
||||
public List<Throwable> errors = new Vector<>();
|
||||
}
|
@ -27,7 +27,7 @@ import java.util.Vector;
|
||||
|
||||
public class StreamPreviewInfoCollector {
|
||||
private List<StreamPreviewInfo> itemList = new Vector<>();
|
||||
private List<Exception> errors = new Vector<>();
|
||||
private List<Throwable> errors = new Vector<>();
|
||||
private UrlIdHandler urlIdHandler;
|
||||
private int serviceId = -1;
|
||||
|
||||
@ -40,7 +40,7 @@ public class StreamPreviewInfoCollector {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
public List<Exception> getErrors() {
|
||||
public List<Throwable> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,30 @@ package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/*
|
||||
import com.steadystate.css.dom.CSSStyleDeclarationImpl;
|
||||
import com.steadystate.css.dom.CSSStyleSheetImpl;
|
||||
import com.steadystate.css.parser.CSSOMParser;
|
||||
import com.steadystate.css.parser.SACParserCSS3;
|
||||
import org.w3c.css.sac.CSSParseException;
|
||||
import org.w3c.css.sac.InputSource;
|
||||
import org.w3c.dom.css.CSSRule;
|
||||
import org.w3c.dom.css.CSSRuleList;
|
||||
import org.w3c.dom.css.CSSStyleSheet;
|
||||
import java.io.StringReader;
|
||||
*/
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
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.UrlIdHandler;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@ -36,6 +52,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
|
||||
private static final String TAG = YoutubeChannelExtractor.class.toString();
|
||||
|
||||
// private CSSOMParser cssParser = new CSSOMParser(new SACParserCSS3());
|
||||
|
||||
private Downloader downloader;
|
||||
private final Document doc;
|
||||
private final String siteUrl;
|
||||
@ -45,17 +63,21 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
throws ExtractionException, IOException {
|
||||
super(urlIdHandler, url, dl, serviceId);
|
||||
|
||||
siteUrl = url;
|
||||
siteUrl = urlIdHandler.cleanUrl(url);
|
||||
Log.d(TAG, siteUrl);
|
||||
downloader = dl;
|
||||
String pageContent = downloader.download(url);
|
||||
doc = Jsoup.parse(pageContent, url);
|
||||
|
||||
Log.d(TAG, pageContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelName() throws ParsingException {
|
||||
return getUrlIdHandler().getId(siteUrl);
|
||||
try {
|
||||
return doc.select("span[class=\"qualified-channel-title-text\"]").first()
|
||||
.select("a").first().text();
|
||||
} catch(Exception e) {
|
||||
throw new ParsingException("Could not get channel name");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,6 +92,41 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
|
||||
@Override
|
||||
public String getBannerUrl() throws ParsingException {
|
||||
return "https://yt3.ggpht.com/-oF0YbeAGkaA/VBgrKvEGY1I/AAAAAAAACdw/nx02iZSseFw/w2120-fcrop64=1,00005a57ffffa5a8-nd-c0xffffffff-rj-k-no/Channel-Art-Template-%2528Photoshop%2529.png";
|
||||
String cssContent = "";
|
||||
try {
|
||||
Element el = doc.select("div[id=\"gh-banner\"]").first().select("style").first();
|
||||
cssContent = el.html();
|
||||
// todo: parse this using a css parser
|
||||
/*
|
||||
CSSStyleSheet sheet = cssParser.parseStyleSheet(
|
||||
new org.w3c.css.sac.InputSource(
|
||||
new StringReader(cssContent)), null, null);
|
||||
CSSRuleList rules = sheet.getCssRules();
|
||||
for (int i = 0; i < rules.getLength(); i++) {
|
||||
final CSSRule rule = rules.item(i);
|
||||
System.out.println(rule.getCssText());
|
||||
}
|
||||
*/
|
||||
String url = "https:" + Parser.matchGroup1("url\\((.*)\\)", cssContent);
|
||||
if(url.contains("s.ytimg.com")) {
|
||||
return null;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
/* } catch(CSSParseException csse) {
|
||||
throw new ParsingException("Could not parse css: " + cssContent); */
|
||||
} catch(Exception e) {
|
||||
throw new ParsingException("Could not get Banner", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeedUrl() throws ParsingException {
|
||||
return siteUrl + "/feed";
|
||||
}
|
||||
|
||||
private String getUserUrl() throws ParsingException {
|
||||
return doc.select("span[class=\"qualified-channel-title-text\"]").first()
|
||||
.select("a").first().attr("abs:href");
|
||||
}
|
||||
}
|
||||
|
@ -27,15 +27,11 @@ import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
public class YoutubeChannelUrlIdHandler implements UrlIdHandler {
|
||||
|
||||
public String getUrl(String channelId) {
|
||||
return "https://www.youtube.com/user/" + channelId + "/videos";
|
||||
return "https://www.youtube.com/" + channelId;
|
||||
}
|
||||
|
||||
public String getId(String siteUrl) throws ParsingException {
|
||||
try {
|
||||
return Parser.matchGroup1("/user/(.*)", siteUrl);
|
||||
} catch(Exception e) {
|
||||
throw new ParsingException("Could not get channel/user id", e);
|
||||
}
|
||||
return Parser.matchGroup1("/(user/[A-Za-z0-9_-]*|channel/[A-Za-z0-9_-]*)", siteUrl);
|
||||
}
|
||||
|
||||
public String cleanUrl(String siteUrl) throws ParsingException {
|
||||
@ -45,6 +41,7 @@ public class YoutubeChannelUrlIdHandler implements UrlIdHandler {
|
||||
public boolean acceptUrl(String videoUrl) {
|
||||
return (videoUrl.contains("youtube") ||
|
||||
videoUrl.contains("youtu.be")) &&
|
||||
videoUrl.contains("/user/");
|
||||
( videoUrl.contains("/user/") ||
|
||||
videoUrl.contains("/channel/"));
|
||||
}
|
||||
}
|
||||
|
@ -685,6 +685,16 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
return pageUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChannelUrl() throws ParsingException {
|
||||
try {
|
||||
return doc.select("div[class=\"yt-user-info\"]").first().children()
|
||||
.select("a").first().attr("abs:href");
|
||||
} catch(Exception e) {
|
||||
throw new ParsingException("Could not get channel link", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamInfo.StreamType getStreamType() throws ParsingException {
|
||||
//todo: if implementing livestream support this value should be generated dynamically
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 417 B |
BIN
app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 286 B |
BIN
app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 528 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 761 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1012 B |
@ -5,25 +5,36 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:id="@+id/rootView"
|
||||
tools:context="org.schabi.newpipe.ChannelActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
android:id="@+id/channel_app_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/app_bar_height"
|
||||
android:fitsSystemWindows="true"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.design.widget.CollapsingToolbarLayout
|
||||
android:id="@+id/toolbar_layout"
|
||||
android:id="@+id/channel_toolbar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="?attr/colorPrimary"
|
||||
app:contentScrim="@color/light_youtube_primary_color"
|
||||
app:statusBarScrim="@color/light_youtube_dark_color"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/channel_banner_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:background="@color/light_youtube_dark_color"
|
||||
app:layout_collapseMode="parallax" />
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:id="@+id/cannel_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_collapseMode="pin"
|
||||
@ -32,15 +43,58 @@
|
||||
</android.support.design.widget.CollapsingToolbarLayout>
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_channel" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:id="@+id/channel_rss_fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:src="@android:drawable/ic_dialog_email"
|
||||
app:layout_anchor="@id/app_bar"
|
||||
android:src="@drawable/ic_rss_feed_white_24dp"
|
||||
app:layout_anchor="@id/channel_app_bar"
|
||||
app:layout_anchorGravity="bottom|end" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/channel_loading">
|
||||
<ProgressBar android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:indeterminate="true"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="fill_vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:id="@+id/channel_content_view"
|
||||
android:visibility="gone">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
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.design.widget.CoordinatorLayout>
|
||||
|
@ -96,6 +96,7 @@
|
||||
<string name="could_not_setup_download_menu">Could not setup download menu.</string>
|
||||
<string name="live_streams_not_supported">This is a LIVE STREAM. These are not yet supported.</string>
|
||||
<string name="could_not_get_stream">Could not get any stream.</string>
|
||||
<string name="could_not_load_image">Could not load Image</string>
|
||||
<!-- error activity -->
|
||||
<string name="sorry_string">Sorry that should not happen.</string>
|
||||
<string name="guru_meditation" translatable="false">Guru Meditation.</string>
|
||||
|
Loading…
Reference in New Issue
Block a user