setup core for search channel support

pull/460/head
Christian Schabesberger 2017-02-13 00:55:05 +01:00
rodzic f29bd0a6e7
commit 91434dd2ac
26 zmienionych plików z 930 dodań i 80 usunięć

Wyświetl plik

@ -92,7 +92,8 @@ public class ChannelActivity extends AppCompatActivity {
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(infoListAdapter);
infoListAdapter.setOnItemSelectedListener(new InfoItemBuilder.OnItemSelectedListener() {
infoListAdapter.setOnStreamItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url) {
Intent detailIntent = new Intent(ChannelActivity.this, VideoItemDetailActivity.class);
@ -172,7 +173,7 @@ public class ChannelActivity extends AppCompatActivity {
}
private void addVideos(final ChannelInfo info) {
infoListAdapter.addStreamItemList(info.related_streams);
infoListAdapter.addInfoItemList(info.related_streams);
}
private void postNewErrorToast(Handler h, final int stringResource) {

Wyświetl plik

@ -28,11 +28,9 @@ import java.util.Vector;
public class InfoItemCollector {
private List<InfoItem> itemList = new Vector<>();
private List<Throwable> errors = new Vector<>();
private UrlIdHandler urlIdHandler;
private int serviceId = -1;
public InfoItemCollector(UrlIdHandler handler, int serviceId) {
urlIdHandler = handler;
public InfoItemCollector(int serviceId) {
this.serviceId = serviceId;
}
@ -60,7 +58,4 @@ public class InfoItemCollector {
protected int getServiceId() {
return serviceId;
}
protected UrlIdHandler getUrlIdHandler() {
return urlIdHandler;
}
}

Wyświetl plik

@ -24,14 +24,18 @@ import org.schabi.newpipe.extractor.InfoItem;
public class ChannelInfoItem implements InfoItem {
public int serviceId = -1;
public String channelName = "";
public String webPageUrl = "";
public int subscriberCount = -1;
public int videoAmount = -1;
public InfoType infoType() {
return InfoType.CHANNEL;
}
public String getTitle() {
return "";
}
public String getLink() {
return "";
}

Wyświetl plik

@ -0,0 +1,61 @@
package org.schabi.newpipe.extractor.channel;
import org.schabi.newpipe.extractor.InfoItemCollector;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.FoundAdException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
/**
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
* ChannelInfoItemCollector.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 ChannelInfoItemCollector extends InfoItemCollector {
public ChannelInfoItemCollector(int serviceId) {
super(serviceId);
}
public void commit(ChannelInfoItemExtractor extractor) throws ParsingException {
try {
ChannelInfoItem resultItem = new ChannelInfoItem();
// importand information
resultItem.channelName = extractor.getChannelName();
resultItem.serviceId = getServiceId();
resultItem.webPageUrl = extractor.getWebPageUrl();
// optional information
try {
resultItem.subscriberCount = extractor.getSubscriberCount();
} catch (Exception e) {
addError(e);
}
try {
resultItem.videoAmount = extractor.getVideoAmount();
} catch (Exception e) {
addError(e);
}
addItem(resultItem);
} catch (Exception e) {
addError(e);
}
}
}

Wyświetl plik

@ -0,0 +1,30 @@
package org.schabi.newpipe.extractor.channel;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
/**
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
* ChannelInfoItemExtractor.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 interface ChannelInfoItemExtractor {
String getChannelName() throws ParsingException;
String getWebPageUrl() throws ParsingException;
int getSubscriberCount() throws ParsingException;
int getVideoAmount() throws ParsingException;
}

Wyświetl plik

@ -2,6 +2,8 @@ package org.schabi.newpipe.extractor.search;
import org.schabi.newpipe.extractor.InfoItemCollector;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemCollector;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
@ -30,10 +32,12 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
public class InfoItemSearchCollector extends InfoItemCollector {
private String suggestion = "";
private StreamInfoItemCollector streamCollector;
private ChannelInfoItemCollector channelCollector;
InfoItemSearchCollector(UrlIdHandler handler, int serviceId) {
super(handler, serviceId);
super(serviceId);
streamCollector = new StreamInfoItemCollector(handler, serviceId);
channelCollector = new ChannelInfoItemCollector(serviceId);
}
public void setSuggestion(String suggestion) {
@ -43,6 +47,7 @@ public class InfoItemSearchCollector extends InfoItemCollector {
public SearchResult getSearchResult() throws ExtractionException {
SearchResult result = new SearchResult();
addFromCollector(channelCollector);
addFromCollector(streamCollector);
result.suggestion = suggestion;
@ -54,4 +59,8 @@ public class InfoItemSearchCollector extends InfoItemCollector {
public void commit(StreamInfoItemExtractor extractor) throws ParsingException {
streamCollector.commit(extractor);
}
public void commit(ChannelInfoItemExtractor extractor) throws ParsingException {
channelCollector.commit(extractor);
}
}

Wyświetl plik

@ -5,6 +5,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
import java.io.IOException;
import java.util.EnumSet;
/**
* Created by Christian Schabesberger on 10.08.15.
@ -27,6 +28,10 @@ import java.io.IOException;
*/
public abstract class SearchEngine {
public enum Filter {
VIDEO, CHANNEL, PLAY_LIST
}
public static class NothingFoundException extends ExtractionException {
public NothingFoundException(String message) {
super(message);
@ -43,6 +48,6 @@ public abstract class SearchEngine {
}
//Result search(String query, int page);
public abstract InfoItemSearchCollector search(
String query, int page, String contentCountry)
String query, int page, String contentCountry, EnumSet<Filter> filter)
throws ExtractionException, IOException;
}

Wyświetl plik

@ -5,6 +5,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
import java.io.IOException;
import java.util.EnumSet;
import java.util.List;
import java.util.Vector;
@ -30,10 +31,12 @@ import java.util.Vector;
public class SearchResult {
public static SearchResult getSearchResult(SearchEngine engine, String query,
int page, String languageCode)
int page, String languageCode, EnumSet<SearchEngine.Filter> filter)
throws ExtractionException, IOException {
SearchResult result = engine.search(query, page, languageCode).getSearchResult();
SearchResult result = engine
.search(query, page, languageCode, filter)
.getSearchResult();
if(result.resultList.isEmpty()) {
if(result.suggestion.isEmpty()) {
throw new ExtractionException("Empty result despite no error");

Wyświetl plik

@ -0,0 +1,49 @@
package org.schabi.newpipe.extractor.services.youtube;
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.jsoup.nodes.Element;
/**
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
* YoutubeChannelInfoItemExtractor.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 YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor {
private Element el;
public YoutubeChannelInfoItemExtractor(Element el) {
this.el = el;
}
public String getChannelName() throws ParsingException {
return "";
}
public String getWebPageUrl() throws ParsingException {
return "";
}
public int getSubscriberCount() throws ParsingException {
return 0;
}
public int getVideoAmount() throws ParsingException {
return 0;
}
}

Wyświetl plik

@ -9,11 +9,10 @@ import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.search.InfoItemSearchCollector;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
import java.net.URLEncoder;
import java.io.IOException;
import java.util.EnumSet;
/**
@ -46,7 +45,10 @@ public class YoutubeSearchEngine extends SearchEngine {
}
@Override
public InfoItemSearchCollector search(String query, int page, String languageCode)
public InfoItemSearchCollector search(String query,
int page,
String languageCode,
EnumSet<Filter> filter)
throws IOException, ExtractionException {
InfoItemSearchCollector collector = getInfoItemSearchCollector();
@ -54,9 +56,13 @@ public class YoutubeSearchEngine extends SearchEngine {
Downloader downloader = NewPipe.getDownloader();
String url = "https://www.youtube.com/results"
+ "?search_query=" + URLEncoder.encode(query, CHARSET_UTF_8)
+ "&page=" + Integer.toString(page + 1)
+ "&filters=" + "video";
+ "?q=" + URLEncoder.encode(query, CHARSET_UTF_8)
+ "&page=" + Integer.toString(page + 1);
if(filter.contains(Filter.VIDEO) && !filter.contains(Filter.CHANNEL)) {
url += "&sp=EgIQAQ%253D%253D";
} else if(!filter.contains(Filter.VIDEO) && filter.contains(Filter.CHANNEL)) {
url += "&sp=EgIQAg%253D%253D";
}
String site;
//String url = builder.build().toString();
@ -94,12 +100,13 @@ public class YoutubeSearchEngine extends SearchEngine {
}
// search message item
} else if ((el = item.select("div[class*=\"search-message\"]").first()) != null) {
//result.errorMessage = el.text();
throw new NothingFoundException(el.text());
// video item type
} else if ((el = item.select("div[class*=\"yt-lockup-video\"").first()) != null) {
} else if ((el = item.select("div[class*=\"yt-lockup-video\"]").first()) != null) {
collector.commit(new YoutubeStreamInfoItemExtractor(el));
} else if((el = item.select("div[class*=\"yt-lockup-channel\"]").first()) != null) {
collector.commit(new YoutubeChannelInfoItemExtractor(el));
} else {
//noinspection ConstantConditions
throw new ExtractionException("unexpected element found:\"" + el + "\"");

Wyświetl plik

@ -31,8 +31,15 @@ import java.util.Vector;
public class StreamInfoItemCollector extends InfoItemCollector {
private UrlIdHandler urlIdHandler;
public StreamInfoItemCollector(UrlIdHandler handler, int serviceId) {
super(handler, serviceId);
super(serviceId);
urlIdHandler = handler;
}
private UrlIdHandler getUrlIdHandler() {
return urlIdHandler;
}
public void commit(StreamInfoItemExtractor extractor) throws ParsingException {

Wyświetl plik

@ -0,0 +1,48 @@
package org.schabi.newpipe.info_list;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* ChannelInfoItemHolder .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 ChannelInfoItemHolder extends InfoItemHolder {
public final CircleImageView itemThumbnailView;
public final TextView itemChannelTitleView;
public final Button itemButton;
ChannelInfoItemHolder(View v) {
super(v);
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
itemButton = (Button) v.findViewById(R.id.item_button);
}
@Override
public InfoItem.InfoType infoType() {
return InfoItem.InfoType.CHANNEL;
}
}

Wyświetl plik

@ -5,6 +5,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
@ -13,6 +14,7 @@ import org.schabi.newpipe.ImageErrorLoadingListener;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.AbstractStreamInfo;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
/**
@ -37,7 +39,8 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
public class InfoItemBuilder {
public interface OnItemSelectedListener {
private static final String TAG = InfoItemBuilder.class.toString();
public interface OnInfoItemSelectedListener {
void selected(String url);
}
@ -46,19 +49,64 @@ public class InfoItemBuilder {
private ImageLoader imageLoader = ImageLoader.getInstance();
private DisplayImageOptions displayImageOptions =
new DisplayImageOptions.Builder().cacheInMemory(true).build();
private OnItemSelectedListener onItemSelectedListener;
private OnInfoItemSelectedListener onStreamInfoItemSelectedListener;
private OnInfoItemSelectedListener onChannelInfoItemSelectedListener;
public InfoItemBuilder(Activity a, View rootView) {
activity = a;
this.rootView = rootView;
}
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
this.onItemSelectedListener = onItemSelectedListener;
public void setOnStreamInfoItemSelectedListener(
OnInfoItemSelectedListener listener) {
this.onStreamInfoItemSelectedListener = listener;
}
public void setOnChannelInfoItemSelectedListener(
OnInfoItemSelectedListener listener) {
this.onChannelInfoItemSelectedListener = listener;
}
public void buildByHolder(InfoItemHolder holder, final InfoItem i) {
final StreamInfoItem info = (StreamInfoItem) i;
switch(i.infoType()) {
case STREAM:
buildStreamInfoItem((StreamInfoItemHolder) holder, (StreamInfoItem) i);
break;
case CHANNEL:
buildChannelInfoItem((ChannelInfoItemHolder) holder, (ChannelInfoItem) i);
break;
case PLAYLIST:
Log.e(TAG, "Not yet implemented");
break;
default:
Log.e(TAG, "Trollolo");
}
}
public View buildView(ViewGroup parent, final InfoItem info) {
View itemView = null;
InfoItemHolder holder = null;
switch(info.infoType()) {
case STREAM:
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.stream_item, parent, false);
holder = new StreamInfoItemHolder(itemView);
break;
case CHANNEL:
itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.channel_item, parent, false);
holder = new ChannelInfoItemHolder(itemView);
break;
case PLAYLIST:
Log.e(TAG, "Not yet implemented");
default:
Log.e(TAG, "Trollolo");
}
buildByHolder(holder, info);
return itemView;
}
private void buildStreamInfoItem(StreamInfoItemHolder holder, final StreamInfoItem info) {
if(info.infoType() != InfoItem.InfoType.STREAM) {
Log.e("InfoItemBuilder", "Info type not yet supported");
}
@ -98,20 +146,23 @@ public class InfoItemBuilder {
holder.itemButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onItemSelectedListener.selected(info.webpage_url);
onStreamInfoItemSelectedListener.selected(info.webpage_url);
}
});
}
public View buildView(ViewGroup parent, final InfoItem info) {
View streamPreviewView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_item, parent, false);
InfoItemHolder holder = new InfoItemHolder(streamPreviewView);
buildByHolder(holder, info);
return streamPreviewView;
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
holder.itemChannelTitleView.setText(info.getTitle());
holder.itemButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onChannelInfoItemSelectedListener.selected(info.getLink());
}
});
}
public static String shortViewCount(Long viewCount){
if(viewCount >= 1000000000){
return Long.toString(viewCount/1000000000)+"B views";

Wyświetl plik

@ -2,14 +2,11 @@ package org.schabi.newpipe.info_list;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
/**
* Created by Christian Schabesberger on 01.08.16.
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* InfoItemHolder.java is part of NewPipe.
@ -28,25 +25,9 @@ import org.schabi.newpipe.R;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class InfoItemHolder extends RecyclerView.ViewHolder {
public final ImageView itemThumbnailView;
public final TextView itemVideoTitleView,
itemUploaderView,
itemDurationView,
itemUploadDateView,
itemViewCountView;
public final Button itemButton;
public abstract class InfoItemHolder extends RecyclerView.ViewHolder {
public InfoItemHolder(View v) {
super(v);
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
itemButton = (Button) v.findViewById(R.id.item_button);
}
public abstract InfoItem.InfoType infoType();
}

Wyświetl plik

@ -2,13 +2,13 @@ package org.schabi.newpipe.info_list;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
import java.util.List;
import java.util.Vector;
@ -34,47 +34,58 @@ import java.util.Vector;
*/
public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
private static final String TAG = InfoListAdapter.class.toString();
private final InfoItemBuilder infoItemBuilder;
private final List<InfoItem> streamList;
private final List<InfoItem> infoItemList;
public InfoListAdapter(Activity a, View rootView) {
infoItemBuilder = new InfoItemBuilder(a, rootView);
streamList = new Vector<>();
infoItemList = new Vector<>();
}
public void setOnItemSelectedListener
(InfoItemBuilder.OnItemSelectedListener onItemSelectedListener) {
infoItemBuilder.setOnItemSelectedListener(onItemSelectedListener);
public void setOnStreamItemSelectedListener
(InfoItemBuilder.OnInfoItemSelectedListener onItemSelectedListener) {
infoItemBuilder.setOnStreamInfoItemSelectedListener(onItemSelectedListener);
}
public void addStreamItemList(List<InfoItem> videos) {
public void addInfoItemList(List<InfoItem> videos) {
if(videos!= null) {
streamList.addAll(videos);
infoItemList.addAll(videos);
notifyDataSetChanged();
}
}
public void clearSteamItemList() {
streamList.clear();
infoItemList.clear();
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return streamList.size();
return infoItemList.size();
}
@Override
public InfoItemHolder onCreateViewHolder(ViewGroup parent, int i) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_item, parent, false);
return new InfoItemHolder(itemView);
switch(infoItemList.get(i).infoType()) {
case STREAM:
return new StreamInfoItemHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.stream_item, parent, false));
case CHANNEL:
return new ChannelInfoItemHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.channel_item, parent, false));
case PLAYLIST:
Log.e(TAG, "Playlist is not yet implemented");
return null;
default:
Log.e(TAG, "Trollolo");
return null;
}
}
@Override
public void onBindViewHolder(InfoItemHolder holder, int i) {
infoItemBuilder.buildByHolder(holder, streamList.get(i));
infoItemBuilder.buildByHolder(holder, infoItemList.get(i));
}
}

Wyświetl plik

@ -0,0 +1,58 @@
package org.schabi.newpipe.info_list;
import android.icu.text.IDNA;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
/**
* Created by Christian Schabesberger on 01.08.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* StreamInfoItemHolder.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 StreamInfoItemHolder extends InfoItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemVideoTitleView,
itemUploaderView,
itemDurationView,
itemUploadDateView,
itemViewCountView;
public final Button itemButton;
public StreamInfoItemHolder(View v) {
super(v);
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
itemButton = (Button) v.findViewById(R.id.item_button);
}
@Override
public InfoItem.InfoType infoType() {
return InfoItem.InfoType.STREAM;
}
}

Wyświetl plik

@ -21,6 +21,7 @@ import android.widget.Toast;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.ErrorActivity;
@ -29,6 +30,8 @@ import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import java.util.EnumSet;
import static android.app.Activity.RESULT_OK;
import static org.schabi.newpipe.ReCaptchaActivity.RECAPTCHA_REQUEST;
@ -166,7 +169,7 @@ public class SearchInfoItemFragment extends Fragment {
sw.setSearchWorkerResultListener(new SearchWorker.SearchWorkerResultListener() {
@Override
public void onResult(SearchResult result) {
infoListAdapter.addStreamItemList(result.resultList);
infoListAdapter.addInfoItemList(result.resultList);
setDoneLoading();
}
@ -213,7 +216,8 @@ public class SearchInfoItemFragment extends Fragment {
infoListAdapter = new InfoListAdapter(getActivity(),
getActivity().findViewById(android.R.id.content));
infoListAdapter.setOnItemSelectedListener(new InfoItemBuilder.OnItemSelectedListener() {
infoListAdapter.setOnStreamItemSelectedListener(
new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url) {
startDetailActivity(url);
@ -298,7 +302,11 @@ public class SearchInfoItemFragment extends Fragment {
private void search(String query, int page) {
isLoading = true;
SearchWorker sw = SearchWorker.getInstance();
sw.search(streamingServiceId, query, page, getActivity());
sw.search(streamingServiceId,
query,
page,
getActivity(),
EnumSet.of(SearchEngine.Filter.CHANNEL));
}
private void setDoneLoading() {

Wyświetl plik

@ -16,6 +16,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import java.io.IOException;
import java.util.EnumSet;
/**
* Created by Christian Schabesberger on 02.08.16.
@ -67,14 +68,21 @@ public class SearchWorker {
public static final String YOUTUBE = "Youtube";
private final String query;
private final int page;
private final EnumSet<SearchEngine.Filter> filter;
final Handler h = new Handler();
private volatile boolean runs = true;
private Activity a = null;
private int serviceId = -1;
public SearchRunnable(int serviceId, String query, int page, Activity activity, int requestId) {
public SearchRunnable(int serviceId,
String query,
int page,
EnumSet<SearchEngine.Filter> filter,
Activity activity,
int requestId) {
this.serviceId = serviceId;
this.query = query;
this.page = page;
this.filter = filter;
this.a = activity;
}
void terminate() {
@ -102,7 +110,7 @@ public class SearchWorker {
String searchLanguage = sp.getString(searchLanguageKey,
a.getString(R.string.default_language_value));
result = SearchResult
.getSearchResult(engine, query, page, searchLanguage);
.getSearchResult(engine, query, page, searchLanguage, filter);
if(runs) {
h.post(new ResultRunnable(result, requestId));
}
@ -180,11 +188,15 @@ public class SearchWorker {
}
public void search(int serviceId, String query, int page, Activity a) {
public void search(int serviceId,
String query,
int page,
Activity a,
EnumSet<SearchEngine.Filter> filter) {
if(runnable != null) {
terminate();
}
runnable = new SearchRunnable(serviceId, query, page, a, requestId);
runnable = new SearchRunnable(serviceId, query, page, filter, a, requestId);
Thread thread = new Thread(runnable);
thread.start();
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.3 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.1 KiB

Wyświetl plik

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlHardcoded">
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/itemThumbnailView"
android:contentDescription="@string/list_thumbnail_view_description"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/buddy_channel_item"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
tools:ignore="RtlHardcoded">
<TextView android:id="@+id/itemChannelTitleView"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<Button
android:id="@+id/item_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>

Wyświetl plik

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlHardcoded">
<ImageView
android:id="@+id/itemThumbnailView"
android:contentDescription="@string/list_thumbnail_view_description"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/dummi_thumbnail_playlist"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
tools:ignore="RtlHardcoded">
<TextView android:id="@+id/itemTitleView"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<Button
android:id="@+id/item_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>

Wyświetl plik

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlHardcoded">
<ImageView android:id="@+id/itemThumbnailView"
android:contentDescription="@string/list_thumbnail_view_description"
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/itemDurationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/itemThumbnailView"
android:layout_alignRight="@id/itemThumbnailView"
android:layout_alignEnd="@id/itemThumbnailView"
android:layout_marginRight="@dimen/video_item_search_duration_margin"
android:layout_marginEnd="@dimen/video_item_search_duration_margin"
android:layout_marginBottom="@dimen/video_item_search_duration_margin"
android:paddingTop="@dimen/video_item_search_duration_vertical_padding"
android:paddingBottom="@dimen/video_item_search_duration_vertical_padding"
android:paddingRight="@dimen/video_item_search_duration_horizontal_padding"
android:paddingLeft="@dimen/video_item_search_duration_horizontal_padding"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_duration_text_size"
android:background="@color/duration_background_color"
android:textColor="@color/duration_text_color"/>
</RelativeLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_toRightOf="@id/itemThumbnailViewContainer"
tools:ignore="RtlHardcoded">
<TextView android:id="@+id/itemVideoTitleView"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"/>
<TextView android:id="@+id/itemUploaderView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
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/itemUploadDateView"
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"/>
<TextView android:id="@+id/itemViewCountView"
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"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<Button
android:id="@+id/item_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"/>
</FrameLayout>

Wyświetl plik

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="200"
height="200"
viewBox="0 0 187.5 187.5"
id="svg2"
version="1.1"
inkscape:version="0.92.0 r"
sodipodi:docname="buddy_channel_item.svg"
inkscape:export-filename="/home/the-scrabi/Projects/NewPipe/app/src/main/res/drawable-nodpi/buddy_channel_item.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.6269531"
inkscape:cx="-8.1138818"
inkscape:cy="101.30232"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-864.86216)">
<rect
style="opacity:0.997;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.6875;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4136"
width="187.5"
height="187.5"
x="0"
y="864.86218" />
<g
id="g4487"
transform="matrix(0.5625,0,0,0.5625,37.5,418.22093)">
<ellipse
ry="75"
rx="100"
cy="1052.3622"
cx="100"
id="path4152"
style="opacity:0.997;fill:#6c6c6c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
r="60"
cy="942.36218"
cx="100"
id="path4154"
style="opacity:0.997;fill:#6c6c6c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.7 KiB

Wyświetl plik

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="454.39999"
height="256"
viewBox="0 0 426.00001 240"
id="svg2"
version="1.1"
inkscape:version="0.92.0 r"
sodipodi:docname="dummi_thumbnail_playlist.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.6106014"
inkscape:cx="305.7725"
inkscape:cy="81.056384"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-812.36212)">
<rect
style="opacity:0.997;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4136"
width="426"
height="240"
x="0"
y="812.36212" />
<g
id="g850"
transform="matrix(0.99796952,0,0,1,32.067586,0)">
<path
inkscape:transform-center-y="-0.54163706"
transform="matrix(0.65046494,0.01393934,-0.01048956,0.86438861,173.20818,83.099197)"
inkscape:transform-center-x="-13.894569"
d="M 122.12368,980.33878 65.844916,1014.7522 2.6244521,1052.3621 0.9609674,986.41661 -1.2793975e-6,912.86109 57.942247,944.39324 Z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="0.93534698"
sodipodi:arg1="-0.018810925"
sodipodi:r2="40.877174"
sodipodi:r1="80.555222"
sodipodi:cy="981.854"
sodipodi:cx="41.58271"
sodipodi:sides="3"
id="path4164"
style="opacity:0.997;fill:#6c6c6c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:type="star" />
<path
inkscape:transform-center-y="-0.54163706"
transform="matrix(0.65046494,0.01393934,-0.01048956,0.86438861,129.96106,83.099439)"
inkscape:transform-center-x="-13.894569"
d="M 122.12368,980.33878 65.844916,1014.7522 2.6244521,1052.3621 0.9609674,986.41661 -1.2793975e-6,912.86109 57.942247,944.39324 Z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="0.93534698"
sodipodi:arg1="-0.018810925"
sodipodi:r2="40.877174"
sodipodi:r1="80.555222"
sodipodi:cy="981.854"
sodipodi:cx="41.58271"
sodipodi:sides="3"
id="path4164-3"
style="opacity:0.997;fill:#6c6c6c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:type="star" />
</g>
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.7 KiB

Wyświetl plik

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="534.39996"
height="336"
viewBox="0 0 500.99998 315"
id="svg2"
version="1.1"
inkscape:version="0.92.0 r"
sodipodi:docname="dummi_thumbnail_playlist_background.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3101865"
inkscape:cx="235.67736"
inkscape:cy="200.81947"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1012"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-737.36212)">
<rect
style="opacity:0.997;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4136-6-5"
width="426.00003"
height="240"
x="18.75"
y="756.11212" />
<rect
style="opacity:0.997;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:2.81250004;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4136-6"
width="426"
height="240"
x="28.125"
y="765.48712" />
</g>
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB