diff --git a/app/build.gradle b/app/build.gradle index cf45527ff..39c38eb38 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } diff --git a/app/src/androidTest/java/org/schabi/newpipe/extractor/youtube/YoutubeStreamExtractorDefaultTest.java b/app/src/androidTest/java/org/schabi/newpipe/extractor/youtube/YoutubeStreamExtractorDefaultTest.java index 9428a3605..cc163dbb9 100644 --- a/app/src/androidTest/java/org/schabi/newpipe/extractor/youtube/YoutubeStreamExtractorDefaultTest.java +++ b/app/src/androidTest/java/org/schabi/newpipe/extractor/youtube/YoutubeStreamExtractorDefaultTest.java @@ -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)); diff --git a/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java index ea76b46f2..a097e087c 100644 --- a/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java +++ b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java @@ -43,7 +43,7 @@ public class ActivityCommunicator { public volatile Bitmap backgroundPlayerThumbnail; // Sent from any activity to ErrorActivity. - public volatile List errorList; + public volatile List errorList; public volatile Class returnActivity; public volatile ErrorActivity.ErrorInfo errorInfo; } diff --git a/app/src/main/java/org/schabi/newpipe/ChannelActivity.java b/app/src/main/java/org/schabi/newpipe/ChannelActivity.java index 3d5d204a9..eee862317 100644 --- a/app/src/main/java/org/schabi/newpipe/ChannelActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ChannelActivity.java @@ -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); + + // start processing + Thread channelExtractorThread = new Thread(new Runnable() { + Handler h = new Handler(); - FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); - fab.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(); + 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) { + 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(); } }); } + + } diff --git a/app/src/main/java/org/schabi/newpipe/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/ErrorActivity.java index ec43730dd..5661f30bd 100644 --- a/app/src/main/java/org/schabi/newpipe/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ErrorActivity.java @@ -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 errorList; + private List 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 el, + public static void reportError(final Context context, final List 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 el = null; + List 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 el = null; + List 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 el, + public static void reportError(Handler handler, final Context context, final List 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 el) { + private String formErrorText(List 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 { diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java index a0c3f3c26..4509404d8 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java @@ -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 { } }); - channelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent i = new Intent(activity, ChannelActivity.class); - startActivity(i); - } - }); + 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); diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java b/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java index 6efb529a9..71c10cdaf 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemListActivity.java @@ -360,7 +360,7 @@ public class VideoItemListActivity extends AppCompatActivity return true; } case R.id.action_report_error: { - ErrorActivity.reportError(VideoItemListActivity.this, new Vector(), + ErrorActivity.reportError(VideoItemListActivity.this, new Vector(), null, null, ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, ServiceList.getNameOfService(currentStreamingServiceId), diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java index 7c7d0588c..232a2123b 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemListFragment.java @@ -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, "------"); } diff --git a/app/src/main/java/org/schabi/newpipe/download/MainActivity.java b/app/src/main/java/org/schabi/newpipe/download/MainActivity.java index e6c583287..953289cb6 100644 --- a/app/src/main/java/org/schabi/newpipe/download/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/MainActivity.java @@ -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(), + ErrorActivity.reportError(MainActivity.this, new Vector(), null, null, ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT, null, 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 77459b933..6b4912a37 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/ChannelExtractor.java @@ -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; + } } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java b/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java new file mode 100644 index 000000000..6f18814d9 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/extractor/ChannelInfo.java @@ -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 + * 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 . + */ + +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 errors = new Vector<>(); +} diff --git a/app/src/main/java/org/schabi/newpipe/extractor/SearchResult.java b/app/src/main/java/org/schabi/newpipe/extractor/SearchResult.java index 36150d68b..8feccad0c 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/SearchResult.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/SearchResult.java @@ -43,5 +43,5 @@ public class SearchResult { public String suggestion = ""; public List resultList = new Vector<>(); - public List errors = new Vector<>(); + public List errors = new Vector<>(); } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/StreamExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/StreamExtractor.java index 4b6e8df1c..c3e8c6fcb 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/StreamExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/StreamExtractor.java @@ -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; 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 815663c00..1c162b97d 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/StreamInfo.java @@ -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 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 errors = new Vector<>(); + public List errors = new Vector<>(); } \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/extractor/StreamPreviewInfoCollector.java b/app/src/main/java/org/schabi/newpipe/extractor/StreamPreviewInfoCollector.java index 6fbb24b38..676e0988c 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/StreamPreviewInfoCollector.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/StreamPreviewInfoCollector.java @@ -27,7 +27,7 @@ import java.util.Vector; public class StreamPreviewInfoCollector { private List itemList = new Vector<>(); - private List errors = new Vector<>(); + private List errors = new Vector<>(); private UrlIdHandler urlIdHandler; private int serviceId = -1; @@ -40,7 +40,7 @@ public class StreamPreviewInfoCollector { return itemList; } - public List getErrors() { + public List getErrors() { return errors; } 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 c30384711..bc83bb9b4 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 @@ -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"); } } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java index fc3a65118..5f6f85ae6 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeChannelUrlIdHandler.java @@ -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/")); } } diff --git a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java index b5e25acf5..aac94b355 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractor.java @@ -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 diff --git a/app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png new file mode 100644 index 000000000..c966a6e0a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_rss_feed_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png new file mode 100644 index 000000000..067b10752 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_rss_feed_white_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png new file mode 100644 index 000000000..650627286 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_rss_feed_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png new file mode 100644 index 000000000..2267aad8f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_rss_feed_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png new file mode 100644 index 000000000..209688dc5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_rss_feed_white_24dp.png differ diff --git a/app/src/main/res/layout/activity_channel.xml b/app/src/main/res/layout/activity_channel.xml index 110b64a21..f6da0cb71 100644 --- a/app/src/main/res/layout/activity_channel.xml +++ b/app/src/main/res/layout/activity_channel.xml @@ -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"> + + - - + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4db613260..3d1a0fa35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -96,6 +96,7 @@ Could not setup download menu. This is a LIVE STREAM. These are not yet supported. Could not get any stream. + Could not load Image Sorry that should not happen. Guru Meditation.