git channel item running

This commit is contained in:
Christian Schabesberger 2017-02-15 12:59:36 +01:00
parent 91434dd2ac
commit 3f0078f38a
21 changed files with 233 additions and 40 deletions

View File

@ -1,18 +1,24 @@
package org.schabi.newpipe;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
@ -34,6 +40,9 @@ import org.schabi.newpipe.report.ErrorActivity;
import java.io.IOException;
import java.util.Objects;
import static android.os.Build.VERSION.SDK;
import static android.os.Build.VERSION.SDK_INT;
/**
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* ChannelActivity.java is part of NewPipe.
@ -87,15 +96,17 @@ public class ChannelActivity extends AppCompatActivity {
channelUrl = i.getStringExtra(CHANNEL_URL);
serviceId = i.getIntExtra(SERVICE_ID, -1);
setTranslucentStatusBar(getWindow());
infoListAdapter = new InfoListAdapter(this, rootView);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.channel_streams_view);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(infoListAdapter);
infoListAdapter.setOnStreamItemSelectedListener(
infoListAdapter.setOnStreamInfoItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url) {
public void selected(String url, int serviceId) {
Intent detailIntent = new Intent(ChannelActivity.this, VideoItemDetailActivity.class);
detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, url);
detailIntent.putExtra(
@ -245,9 +256,13 @@ public class ChannelActivity extends AppCompatActivity {
});
pe.printStackTrace();
} catch(ExtractionException ex) {
String name = "none";
if(service != null) {
name = service.getServiceInfo().name;
}
ErrorActivity.reportError(h, ChannelActivity.this, ex, VideoItemDetailFragment.class, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_CHANNEL,
service.getServiceInfo().name, channelUrl, R.string.parsing_error));
name, channelUrl, R.string.parsing_error));
h.post(new Runnable() {
@Override
public void run() {
@ -273,4 +288,28 @@ public class ChannelActivity extends AppCompatActivity {
channelExtractorThread.start();
}
// fix transparent statusbar fuckup (fuck google why can't they just leave something that worked
// as it is, and everyone gets happy)
public static void setTranslucentStatusBar(Window window) {
if (window == null) return;
int sdkInt = Build.VERSION.SDK_INT;
if (sdkInt >= Build.VERSION_CODES.LOLLIPOP) {
setTranslucentStatusBarLollipop(window);
} else if (sdkInt >= Build.VERSION_CODES.KITKAT) {
setTranslucentStatusBarKiKat(window);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static void setTranslucentStatusBarLollipop(Window window) {
window.setStatusBarColor(
ContextCompat.getColor(window.getContext(), android.R.color.transparent));
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private static void setTranslucentStatusBarKiKat(Window window) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}

View File

@ -543,7 +543,7 @@ public class VideoItemDetailFragment extends Fragment {
infoItemBuilder.setOnStreamInfoItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url) {
public void selected(String url, int serviceId) {
openStreamUrl(url);
}
});

View File

@ -26,17 +26,19 @@ public class ChannelInfoItem implements InfoItem {
public int serviceId = -1;
public String channelName = "";
public String thumbnailUrl = "";
public String webPageUrl = "";
public int subscriberCount = -1;
public String description = "";
public long subscriberCount = -1;
public int videoAmount = -1;
public InfoType infoType() {
return InfoType.CHANNEL;
}
public String getTitle() {
return "";
return channelName;
}
public String getLink() {
return "";
return webPageUrl;
}
}

View File

@ -52,6 +52,16 @@ public class ChannelInfoItemCollector extends InfoItemCollector {
} catch (Exception e) {
addError(e);
}
try {
resultItem.thumbnailUrl = extractor.getThumbnailUrl();
} catch (Exception e) {
addError(e);
}
try {
resultItem.description = extractor.getDescription();
} catch (Exception e) {
addError(e);
}
addItem(resultItem);
} catch (Exception e) {

View File

@ -23,8 +23,10 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
*/
public interface ChannelInfoItemExtractor {
String getThumbnailUrl() throws ParsingException;
String getChannelName() throws ParsingException;
String getWebPageUrl() throws ParsingException;
int getSubscriberCount() throws ParsingException;
String getDescription() throws ParsingException;
long getSubscriberCount() throws ParsingException;
int getVideoAmount() throws ParsingException;
}

View File

@ -39,7 +39,9 @@ public class SearchResult {
.getSearchResult();
if(result.resultList.isEmpty()) {
if(result.suggestion.isEmpty()) {
throw new ExtractionException("Empty result despite no error");
if(result.errors.isEmpty()) {
throw new ExtractionException("Empty result despite no error");
}
} else {
// This is used as a fallback. Do not relay on it !!!
throw new SearchEngine.NothingFoundException(result.suggestion);

View File

@ -136,8 +136,9 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
if(!isAjaxPage) {
Element el = doc.select("div[id=\"gh-banner\"]").first().select("style").first();
String cssContent = el.html();
String url = "https:" + Parser.matchGroup1("url\\(([^)]+)\\)", cssContent);
if (url.contains("s.ytimg.com")) {
String url = Parser.matchGroup1("url\\(([^)]+)\\)", cssContent);
if (url.contains("s.ytimg.com") || url.contains("default_banner")) {
bannerUrl = null;
} else {
bannerUrl = url;

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe.extractor.services.youtube;
import org.schabi.newpipe.extractor.Parser;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.jsoup.nodes.Element;
@ -31,19 +32,48 @@ public class YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor
this.el = el;
}
public String getThumbnailUrl() throws ParsingException {
Element img = el.select("span[class*=\"yt-thumb-simple\"]").first()
.select("img").first();
String url = img.attr("abs:src");
if(url.contains("gif")) {
url = img.attr("abs:data-thumb");
}
return url;
}
public String getChannelName() throws ParsingException {
return "";
return el.select("a[class*=\"yt-uix-tile-link\"]").first()
.text();
}
public String getWebPageUrl() throws ParsingException {
return "";
return el.select("a[class*=\"yt-uix-tile-link\"]").first()
.attr("abs:href");
}
public int getSubscriberCount() throws ParsingException {
return 0;
public long getSubscriberCount() throws ParsingException {
return Long.parseLong(el.select("span[class*=\"yt-subscriber-count\"]").first()
.text().replaceAll("\\D+",""));
}
public int getVideoAmount() throws ParsingException {
return 0;
Element metaEl = el.select("ul[class*=\"yt-lockup-meta-info\"]").first();
if(metaEl == null) {
return 0;
} else {
return Integer.parseInt(metaEl.text().replaceAll("\\D+",""));
}
}
public String getDescription() throws ParsingException {
Element desEl = el.select("div[class*=\"yt-lockup-description\"]").first();
if(desEl == null) {
return "";
} else {
return desEl.text();
}
}
}

View File

@ -109,7 +109,7 @@ public class YoutubeSearchEngine extends SearchEngine {
collector.commit(new YoutubeChannelInfoItemExtractor(el));
} else {
//noinspection ConstantConditions
throw new ExtractionException("unexpected element found:\"" + el + "\"");
throw new ExtractionException("unexpected element found: \"" + el + "\"");
}
}

View File

@ -32,12 +32,18 @@ import de.hdodenhof.circleimageview.CircleImageView;
public class ChannelInfoItemHolder extends InfoItemHolder {
public final CircleImageView itemThumbnailView;
public final TextView itemChannelTitleView;
public final TextView itemSubscriberCountView;
public final TextView itemVideoCountView;
public final TextView itemChannelDescriptionView;
public final Button itemButton;
ChannelInfoItemHolder(View v) {
super(v);
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
itemSubscriberCountView = (TextView) v.findViewById(R.id.itemSubscriberCountView);
itemVideoCountView = (TextView) v.findViewById(R.id.itemVideoCountView);
itemChannelDescriptionView = (TextView) v.findViewById(R.id.itemChannelDescriptionView);
itemButton = (Button) v.findViewById(R.id.item_button);
}

View File

@ -39,9 +39,17 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
public class InfoItemBuilder {
final String viewsS;
final String videosS;
final String subsS;
final String thousand;
final String million;
final String billion;
private static final String TAG = InfoItemBuilder.class.toString();
public interface OnInfoItemSelectedListener {
void selected(String url);
void selected(String url, int serviceId);
}
private Activity activity = null;
@ -55,6 +63,12 @@ public class InfoItemBuilder {
public InfoItemBuilder(Activity a, View rootView) {
activity = a;
this.rootView = rootView;
viewsS = a.getString(R.string.views);
videosS = a.getString(R.string.videos);
subsS = a.getString(R.string.subscriber);
thousand = a.getString(R.string.short_thousand);
million = a.getString(R.string.short_million);
billion = a.getString(R.string.short_billion);
}
public void setOnStreamInfoItemSelectedListener(
@ -146,32 +160,55 @@ public class InfoItemBuilder {
holder.itemButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onStreamInfoItemSelectedListener.selected(info.webpage_url);
onStreamInfoItemSelectedListener.selected(info.webpage_url, info.service_id);
}
});
}
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
holder.itemChannelTitleView.setText(info.getTitle());
holder.itemSubscriberCountView.setText(shortSubscriber(info.subscriberCount) + "");
holder.itemVideoCountView.setText(info.videoAmount + " " + videosS);
holder.itemChannelDescriptionView.setText(info.description);
holder.itemThumbnailView.setImageResource(R.drawable.buddy_channel_item);
if(info.thumbnailUrl != null && !info.thumbnailUrl.isEmpty()) {
imageLoader.displayImage(info.thumbnailUrl,
holder.itemThumbnailView,
displayImageOptions,
new ImageErrorLoadingListener(activity, rootView, info.serviceId));
}
holder.itemButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onChannelInfoItemSelectedListener.selected(info.getLink());
onChannelInfoItemSelectedListener.selected(info.getLink(), info.serviceId);
}
});
}
public static String shortViewCount(Long viewCount){
public String shortViewCount(Long viewCount){
if(viewCount >= 1000000000){
return Long.toString(viewCount/1000000000)+"B views";
return Long.toString(viewCount/1000000000)+ billion + " " + viewsS;
}else if(viewCount>=1000000){
return Long.toString(viewCount/1000000)+"M views";
return Long.toString(viewCount/1000000)+ million + " " + viewsS;
}else if(viewCount>=1000){
return Long.toString(viewCount/1000)+"K views";
return Long.toString(viewCount/1000)+ thousand + " " + viewsS;
}else {
return Long.toString(viewCount)+" views";
return Long.toString(viewCount)+ " " + viewsS;
}
}
public String shortSubscriber(Long viewCount){
if(viewCount >= 1000000000){
return Long.toString(viewCount/1000000000)+ billion + " " + subsS;
}else if(viewCount>=1000000){
return Long.toString(viewCount/1000000)+ million + " " + subsS;
}else if(viewCount>=1000){
return Long.toString(viewCount/1000)+ thousand + " " + subsS;
}else {
return Long.toString(viewCount)+ " " + subsS;
}
}

View File

@ -44,9 +44,14 @@ public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
infoItemList = new Vector<>();
}
public void setOnStreamItemSelectedListener
(InfoItemBuilder.OnInfoItemSelectedListener onItemSelectedListener) {
infoItemBuilder.setOnStreamInfoItemSelectedListener(onItemSelectedListener);
public void setOnStreamInfoItemSelectedListener
(InfoItemBuilder.OnInfoItemSelectedListener listener) {
infoItemBuilder.setOnStreamInfoItemSelectedListener(listener);
}
public void setOnChannelInfoItemSelectedListener
(InfoItemBuilder.OnInfoItemSelectedListener listener) {
infoItemBuilder.setOnChannelInfoItemSelectedListener(listener);
}
public void addInfoItemList(List<InfoItem> videos) {

View File

@ -19,6 +19,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.schabi.newpipe.ChannelActivity;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.search.SearchEngine;
@ -216,13 +217,19 @@ public class SearchInfoItemFragment extends Fragment {
infoListAdapter = new InfoListAdapter(getActivity(),
getActivity().findViewById(android.R.id.content));
infoListAdapter.setOnStreamItemSelectedListener(
infoListAdapter.setOnStreamInfoItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url) {
public void selected(String url, int serviceId) {
startDetailActivity(url);
}
});
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url, int serviceId) {
startChannelActivity(url, serviceId);
}
});
recyclerView.setAdapter(infoListAdapter);
recyclerView.clearOnScrollListeners();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@ -254,6 +261,13 @@ public class SearchInfoItemFragment extends Fragment {
getActivity().startActivity(i);
}
private void startChannelActivity(String url, int serviceId) {
Intent i = new Intent(getActivity(), ChannelActivity.class);
i.putExtra(ChannelActivity.CHANNEL_URL, url);
i.putExtra(ChannelActivity.SERVICE_ID, serviceId);
startActivity(i);
}
@Override
public void onStart() {
super.onStart();

View File

@ -117,6 +117,7 @@ public class SearchWorker {
// look for errors during extraction
// soft errors:
View rootView = a.findViewById(android.R.id.content);
if(result != null &&
!result.errors.isEmpty()) {
Log.e(TAG, "OCCURRED ERRORS DURING SEARCH EXTRACTION:");
@ -125,11 +126,17 @@ public class SearchWorker {
Log.e(TAG, "------");
}
View rootView = a.findViewById(android.R.id.content);
ErrorActivity.reportError(h, a, result.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.light_parsing_error));
if(result.resultList.isEmpty()&& !result.errors.isEmpty()) {
// if it compleatly failes dont show snackbar, instead show error directlry
ErrorActivity.reportError(h, a, result.errors, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.parsing_error));
} else {
// if it partly show snackbar
ErrorActivity.reportError(h, a, result.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.light_parsing_error));
}
}
// hard errors:
} catch (ReCaptchaException e) {

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/item_main_layout"
<FrameLayout android:id="@+id/item_main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
@ -46,13 +46,35 @@
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"/>
android:textSize="@dimen/channel_item_detail_title_text_size"/>
<TextView android:id="@+id/itemChannelDescriptionView"
android:layout_weight="2"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/itemSubscriberCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
android:text="1000 subs"/>
<TextView android:id="@+id/itemVideoCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"
android:text="1000 vids"/>
</LinearLayout>

View File

@ -138,6 +138,12 @@
<string name="switch_mode">Zwischen Liste und Gitter umschalten</string>
<string name="videos">Videos</string>
<string name="subscriber">Abonenten</string>
<string name="views">Aufrufe</string>
<string name="short_thousand">Tsd.</string>
<string name="short_million">Mio.</string>
<string name="short_billion">Mrd.</string>
<string name="msg_url">Download-URL</string>
<string name="msg_name">Dateiname</string>

View File

@ -3,6 +3,7 @@
<!-- Video Item Search View Dimensions-->
<!-- Text Size -->
<dimen name="channel_item_detail_title_text_size">33sp</dimen>
<dimen name="video_item_search_title_text_size">22sp</dimen>
<dimen name="video_item_search_duration_text_size">16sp</dimen>
<dimen name="video_item_search_uploader_text_size">18sp</dimen>

View File

@ -3,6 +3,7 @@
<!-- Video Item Search View Dimensions-->
<!-- Text Size -->
<dimen name="channel_item_detail_title_text_size">21sp</dimen>
<dimen name="video_item_search_title_text_size">14sp</dimen>
<dimen name="video_item_search_duration_text_size">11sp</dimen>
<dimen name="video_item_search_uploader_text_size">12sp</dimen>

View File

@ -3,6 +3,7 @@
<!-- Video Item Detail View Dimensions-->
<!-- Text Size -->
<dimen name="channel_item_detail_title_text_size">30sp</dimen>
<dimen name="video_item_detail_title_text_size">20sp</dimen>
<dimen name="video_item_detail_views_text_size">16sp</dimen>
<dimen name="video_item_detail_likes_text_size">14sp</dimen>

View File

@ -2,6 +2,7 @@
<resources>
<!-- Video Item Search View Dimensions-->
<!-- Text Size -->
<dimen name="channel_item_detail_title_text_size">21sp</dimen>
<dimen name="video_item_search_title_text_size">14sp</dimen>
<dimen name="video_item_search_duration_text_size">11sp</dimen>
<dimen name="video_item_search_uploader_text_size">12sp</dimen>

View File

@ -147,6 +147,12 @@
<string name="storage_permission_denied">Permission to access storage was denied</string>
<string name="use_exoplayer_title">Use ExoPlayer</string>
<string name="use_exoplayer_summary">Experimental</string>
<string name="videos">videos</string>
<string name="subscriber">subscriber</string>
<string name="views">views</string>
<string name="short_thousand">T</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<!-- Missions -->
<string name="start">Start</string>