Merge remote-tracking branch 'origin/master'
							
								
								
									
										5
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -13,6 +13,9 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras | ||||
| * Check if this issue/feature is already fixed/implemented in the repository | ||||
| * If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself | ||||
|  | ||||
| ## Bugfixing | ||||
| * If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.  | ||||
|  | ||||
| ## Translation | ||||
|  | ||||
| * NewPipe can be translated on [weblate](https://hosted.weblate.org/projects/newpipe/strings/) | ||||
| @@ -31,6 +34,6 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras | ||||
|  | ||||
| ## Communication | ||||
|  | ||||
| * I hereby declare our Slack channel as dead!!! There are no plans on building a new chat, but if there is interest on creating one and keeping it alive, I'd be pleased to create one again.  | ||||
| * WE DO NOW HAVE A MAILING LIST: [newpipe@list.schabi.org](https://list.schabi.org/cgi-bin/mailman/listinfo/newpipe). | ||||
| * If you want to get in contact with me or one of our other contributors you can send me an email at tnp(at)schabi.org | ||||
| * Feel free to post suggestions, changes, ideas etc! | ||||
|   | ||||
| @@ -42,6 +42,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only | ||||
| * Search YouTube in a specific language | ||||
| * Watch age restricted material | ||||
| * Display general information about channels | ||||
| * Search channels | ||||
|  | ||||
| ### Coming Features | ||||
|  | ||||
| @@ -49,7 +50,6 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only | ||||
| * Bookmarks | ||||
| * View history | ||||
| * Search history | ||||
| * Search channels | ||||
| * Subscribe to channels | ||||
| * Watch videos from a channel | ||||
| * Search/Watch Playlists | ||||
|   | ||||
| @@ -45,7 +45,8 @@ dependencies { | ||||
|     compile 'com.google.android.exoplayer:exoplayer:r1.5.5' | ||||
|     compile 'com.google.code.gson:gson:2.4' | ||||
|     compile 'com.nononsenseapps:filepicker:3.0.0' | ||||
|     compile 'ch.acra:acra:4.9.0' | ||||
|     testCompile 'junit:junit:4.12' | ||||
|     testCompile 'org.mockito:mockito-core:1.10.19' | ||||
|     compile 'ch.acra:acra:4.9.0' | ||||
|     testCompile 'org.json:json:20160810' | ||||
| } | ||||
|   | ||||
| @@ -1,52 +0,0 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
|  | ||||
| import android.test.AndroidTestCase; | ||||
|  | ||||
|  | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamExtractor; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 11.03.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeStreamExtractorLiveStreamTest.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 YoutubeStreamExtractorLiveStreamTest extends AndroidTestCase { | ||||
|  | ||||
|     private StreamExtractor extractor; | ||||
|  | ||||
|     public void setUp() throws IOException, ExtractionException { | ||||
|         //todo: make the extractor not throw over a livestream | ||||
|         /* | ||||
|  | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getExtractorInstance("https://www.youtube.com/watch?v=J0s6NjqdjLE", Downloader.getInstance()); | ||||
|                 */ | ||||
|     } | ||||
|  | ||||
|     public void testStreamType() throws ParsingException { | ||||
|         assertTrue(true); | ||||
|         // assertTrue(extractor.getStreamType() == AbstractVideoInfo.StreamType.LIVE_STREAM); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -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,14 +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.setOnItemSelectedListener(new InfoItemBuilder.OnItemSelectedListener() { | ||||
|         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( | ||||
| @@ -172,7 +184,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) { | ||||
| @@ -244,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() { | ||||
| @@ -272,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); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,10 +5,14 @@ import android.media.AudioManager; | ||||
| import android.support.v4.app.Fragment; | ||||
| import android.support.v4.app.NavUtils; | ||||
| import android.os.Bundle; | ||||
| import android.util.Log; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.widget.AdapterView; | ||||
|  | ||||
| import org.schabi.newpipe.download.DownloadActivity; | ||||
| import org.schabi.newpipe.settings.SettingsActivity; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
|  | ||||
| @@ -33,8 +37,8 @@ import org.schabi.newpipe.util.PermissionHelper; | ||||
|  */ | ||||
|  | ||||
| public class MainActivity extends ThemableActivity { | ||||
|  | ||||
|     private Fragment mainFragment = null; | ||||
|     private static final String TAG = MainActivity.class.toString(); | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
| @@ -58,7 +62,7 @@ public class MainActivity extends ThemableActivity { | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         int id = item.getItemId(); | ||||
|  | ||||
|         switch(id) { | ||||
|         switch (id) { | ||||
|             case android.R.id.home: { | ||||
|                 Intent intent = new Intent(this, MainActivity.class); | ||||
|                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); | ||||
| @@ -71,10 +75,10 @@ public class MainActivity extends ThemableActivity { | ||||
|                 return true; | ||||
|             } | ||||
|             case R.id.action_show_downloads: { | ||||
|                 if(!PermissionHelper.checkStoragePermissions(this)) { | ||||
|                 if (!PermissionHelper.checkStoragePermissions(this)) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 Intent intent = new Intent(this, org.schabi.newpipe.download.DownloadActivity.class); | ||||
|                 Intent intent = new Intent(this, DownloadActivity.class); | ||||
|                 startActivity(intent); | ||||
|                 return true; | ||||
|             } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package org.schabi.newpipe; | ||||
|  | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| @@ -16,4 +15,14 @@ public class ThemableActivity extends AppCompatActivity { | ||||
|                 setTheme(R.style.DarkTheme); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onResume() { | ||||
|         super.onResume(); | ||||
|         if (PreferenceManager.getDefaultSharedPreferences(this) | ||||
|                 .getString("theme", getResources().getString(R.string.light_theme_title)). | ||||
|                         equals(getResources().getString(R.string.dark_theme_title)))  { | ||||
|             setTheme(R.style.DarkTheme); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -46,11 +46,12 @@ import org.schabi.newpipe.Localization; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.ReCaptchaActivity; | ||||
| import org.schabi.newpipe.download.DownloadDialog; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.MediaFormat; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.stream_info.AudioStream; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream_info.VideoStream; | ||||
| import org.schabi.newpipe.info_list.InfoItemBuilder; | ||||
| import org.schabi.newpipe.player.BackgroundPlayer; | ||||
| @@ -536,12 +537,13 @@ public class VideoItemDetailFragment extends Fragment { | ||||
|  | ||||
|     private void initSimilarVideos(final StreamInfo info) { | ||||
|         LinearLayout similarLayout = (LinearLayout) activity.findViewById(R.id.similar_streams_view); | ||||
|         for (final StreamPreviewInfo item : info.related_streams) { | ||||
|         for (final InfoItem item : info.related_streams) { | ||||
|             similarLayout.addView(infoItemBuilder.buildView(similarLayout, item)); | ||||
|         } | ||||
|         infoItemBuilder.setOnItemSelectedListener(new InfoItemBuilder.OnItemSelectedListener() { | ||||
|         infoItemBuilder.setOnStreamInfoItemSelectedListener( | ||||
|                 new InfoItemBuilder.OnInfoItemSelectedListener() { | ||||
|             @Override | ||||
|             public void selected(String url) { | ||||
|             public void selected(String url, int serviceId) { | ||||
|                 openStreamUrl(url); | ||||
|             } | ||||
|         }); | ||||
|   | ||||
| @@ -18,9 +18,9 @@ package org.schabi.newpipe.extractor; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /**Common properties between StreamInfo and StreamPreviewInfo.*/ | ||||
| /**Common properties between StreamInfo and StreamInfoItem.*/ | ||||
| public abstract class AbstractStreamInfo { | ||||
|     public static enum StreamType { | ||||
|     public enum StreamType { | ||||
|         NONE,   // placeholder to check if stream type was checked or not | ||||
|         VIDEO_STREAM, | ||||
|         AUDIO_STREAM, | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/src/main/java/org/schabi/newpipe/extractor/InfoItem.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,33 @@ | ||||
| package org.schabi.newpipe.extractor; | ||||
|  | ||||
| /** | ||||
|  * Created by the-scrabi on 11.02.17. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org> | ||||
|  * InfoItem.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 InfoItem { | ||||
|     public enum InfoType { | ||||
|         STREAM, | ||||
|         PLAYLIST, | ||||
|         CHANNEL | ||||
|     } | ||||
|  | ||||
|     InfoType infoType(); | ||||
|     String getTitle(); | ||||
|     String getLink(); | ||||
| } | ||||
| @@ -0,0 +1,61 @@ | ||||
| package org.schabi.newpipe.extractor; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 12.02.17. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org> | ||||
|  * InfoItemCollector.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 InfoItemCollector { | ||||
|     private List<InfoItem> itemList = new Vector<>(); | ||||
|     private List<Throwable> errors = new Vector<>(); | ||||
|     private int serviceId = -1; | ||||
|  | ||||
|     public InfoItemCollector(int serviceId) { | ||||
|         this.serviceId = serviceId; | ||||
|     } | ||||
|  | ||||
|     public List<InfoItem> getItemList() { | ||||
|         return itemList; | ||||
|     } | ||||
|     public List<Throwable> getErrors() { | ||||
|         return errors; | ||||
|     } | ||||
|     protected void addFromCollector(InfoItemCollector otherC) throws ExtractionException { | ||||
|         if(serviceId != otherC.serviceId) { | ||||
|             throw new ExtractionException("Service Id does not equal: " | ||||
|                     + NewPipe.getNameOfService(serviceId) | ||||
|                     + " and " + NewPipe.getNameOfService(otherC.serviceId)); | ||||
|         } | ||||
|         errors.addAll(otherC.errors); | ||||
|         itemList.addAll(otherC.itemList); | ||||
|     } | ||||
|     protected void addError(Exception e) { | ||||
|         errors.add(e); | ||||
|     } | ||||
|     protected void addItem(InfoItem item) { | ||||
|         itemList.add(item); | ||||
|     } | ||||
|     protected int getServiceId() { | ||||
|         return serviceId; | ||||
|     } | ||||
| } | ||||
| @@ -3,7 +3,6 @@ package org.schabi.newpipe.extractor; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelExtractor; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | ||||
| import org.schabi.newpipe.extractor.search.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamExtractor; | ||||
|  | ||||
| import java.io.IOException; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package org.schabi.newpipe.extractor.search; | ||||
| package org.schabi.newpipe.extractor; | ||||
| 
 | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| 
 | ||||
| @@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.channel; | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| @@ -31,7 +31,7 @@ public abstract class ChannelExtractor { | ||||
|     private int serviceId; | ||||
|     private String url; | ||||
|     private UrlIdHandler urlIdHandler; | ||||
|     private StreamPreviewInfoCollector previewInfoCollector; | ||||
|     private StreamInfoItemCollector previewInfoCollector; | ||||
|     private int page = -1; | ||||
|  | ||||
|     public ChannelExtractor(UrlIdHandler urlIdHandler, String url, int page, int serviceId) | ||||
| @@ -40,12 +40,12 @@ public abstract class ChannelExtractor { | ||||
|         this.page = page; | ||||
|         this.serviceId = serviceId; | ||||
|         this.urlIdHandler = urlIdHandler; | ||||
|         previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId); | ||||
|         previewInfoCollector = new StreamInfoItemCollector(urlIdHandler, serviceId); | ||||
|     } | ||||
|  | ||||
|     public String getUrl() { return url; } | ||||
|     public UrlIdHandler getUrlIdHandler() { return urlIdHandler; } | ||||
|     public StreamPreviewInfoCollector getStreamPreviewInfoCollector() { | ||||
|     public StreamInfoItemCollector getStreamPreviewInfoCollector() { | ||||
|         return previewInfoCollector; | ||||
|     } | ||||
|  | ||||
| @@ -53,7 +53,7 @@ public abstract class ChannelExtractor { | ||||
|     public abstract String getAvatarUrl() throws ParsingException; | ||||
|     public abstract String getBannerUrl() throws ParsingException; | ||||
|     public abstract String getFeedUrl() throws ParsingException; | ||||
|     public abstract StreamPreviewInfoCollector getStreams() throws ParsingException; | ||||
|     public abstract StreamInfoItemCollector getStreams() throws ParsingException; | ||||
|     public abstract boolean hasNextPage() throws ParsingException; | ||||
|     public int getServiceId() { | ||||
|         return serviceId; | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| package org.schabi.newpipe.extractor.channel; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
| @@ -59,7 +60,7 @@ public class ChannelInfo { | ||||
|             info.errors.add(e); | ||||
|         } | ||||
|         try { | ||||
|             StreamPreviewInfoCollector c = extractor.getStreams(); | ||||
|             StreamInfoItemCollector c = extractor.getStreams(); | ||||
|             info.related_streams = c.getItemList(); | ||||
|             info.errors.addAll(c.getErrors()); | ||||
|         } catch(Exception e) { | ||||
| @@ -74,7 +75,7 @@ public class ChannelInfo { | ||||
|     public String avatar_url = ""; | ||||
|     public String banner_url = ""; | ||||
|     public String feed_url = ""; | ||||
|     public List<StreamPreviewInfo> related_streams = null; | ||||
|     public List<InfoItem> related_streams = null; | ||||
|     public boolean hasNextPage = false; | ||||
|  | ||||
|     public List<Throwable> errors = new Vector<>(); | ||||
|   | ||||
| @@ -0,0 +1,44 @@ | ||||
| package org.schabi.newpipe.extractor.channel; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 11.02.17. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org> | ||||
|  * ChannelInfoItem.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 ChannelInfoItem implements InfoItem { | ||||
|  | ||||
|     public int serviceId = -1; | ||||
|     public String channelName = ""; | ||||
|     public String thumbnailUrl = ""; | ||||
|     public String webPageUrl = ""; | ||||
|     public String description = ""; | ||||
|     public long subscriberCount = -1; | ||||
|     public int videoAmount = -1; | ||||
|  | ||||
|     public InfoType infoType() { | ||||
|         return InfoType.CHANNEL; | ||||
|     } | ||||
|     public String getTitle() { | ||||
|         return channelName; | ||||
|     } | ||||
|     public String getLink() { | ||||
|         return webPageUrl; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,74 @@ | ||||
| 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 ChannelInfoItem extract(ChannelInfoItemExtractor extractor) throws ParsingException { | ||||
|         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); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.thumbnailUrl = extractor.getThumbnailUrl(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.description = extractor.getDescription(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         return resultItem; | ||||
|     } | ||||
|  | ||||
|     public void commit(ChannelInfoItemExtractor extractor) throws ParsingException { | ||||
|         try { | ||||
|             addItem(extract(extractor)); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| 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 getThumbnailUrl() throws ParsingException; | ||||
|     String getChannelName() throws ParsingException; | ||||
|     String getWebPageUrl() throws ParsingException; | ||||
|     String getDescription() throws ParsingException; | ||||
|     long getSubscriberCount() throws ParsingException; | ||||
|     int getVideoAmount() throws ParsingException; | ||||
| } | ||||
| @@ -0,0 +1,79 @@ | ||||
| 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.FoundAdException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector; | ||||
| 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> | ||||
|  * InfoItemSearchCollector.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 InfoItemSearchCollector extends InfoItemCollector { | ||||
|     private String suggestion = ""; | ||||
|     private StreamInfoItemCollector streamCollector; | ||||
|     private ChannelInfoItemCollector channelCollector; | ||||
|  | ||||
|     SearchResult result = new SearchResult(); | ||||
|  | ||||
|     InfoItemSearchCollector(UrlIdHandler handler, int serviceId) { | ||||
|         super(serviceId); | ||||
|         streamCollector = new StreamInfoItemCollector(handler, serviceId); | ||||
|         channelCollector = new ChannelInfoItemCollector(serviceId); | ||||
|     } | ||||
|  | ||||
|     public void setSuggestion(String suggestion) { | ||||
|         this.suggestion = suggestion; | ||||
|     } | ||||
|  | ||||
|     public SearchResult getSearchResult() throws ExtractionException { | ||||
|  | ||||
|         addFromCollector(channelCollector); | ||||
|         addFromCollector(streamCollector); | ||||
|  | ||||
|         result.suggestion = suggestion; | ||||
|         result.errors = getErrors(); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void commit(StreamInfoItemExtractor extractor) { | ||||
|         try { | ||||
|             result.resultList.add(streamCollector.extract(extractor)); | ||||
|         } catch(FoundAdException ae) { | ||||
|             System.err.println("Found add"); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void commit(ChannelInfoItemExtractor extractor) { | ||||
|         try { | ||||
|             result.resultList.add(channelCollector.extract(extractor)); | ||||
|         } catch(FoundAdException ae) { | ||||
|             System.err.println("Found add"); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,8 +2,10 @@ package org.schabi.newpipe.extractor.search; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| 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. | ||||
| @@ -26,24 +28,26 @@ import java.io.IOException; | ||||
|  */ | ||||
|  | ||||
| public abstract class SearchEngine { | ||||
|     public enum Filter { | ||||
|         STREAM, CHANNEL, PLAY_LIST | ||||
|     } | ||||
|  | ||||
|     public static class NothingFoundException extends ExtractionException { | ||||
|         public NothingFoundException(String message) { | ||||
|             super(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private StreamPreviewInfoSearchCollector collector; | ||||
|     private InfoItemSearchCollector collector; | ||||
|  | ||||
|     public SearchEngine(UrlIdHandler urlIdHandler, int serviceId) { | ||||
|         collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId); | ||||
|         collector = new InfoItemSearchCollector(urlIdHandler, serviceId); | ||||
|     } | ||||
|  | ||||
|     protected StreamPreviewInfoSearchCollector getStreamPreviewInfoSearchCollector() { | ||||
|     protected  InfoItemSearchCollector getInfoItemSearchCollector() { | ||||
|         return collector; | ||||
|     } | ||||
|  | ||||
|     //Result search(String query, int page); | ||||
|     public abstract StreamPreviewInfoSearchCollector search( | ||||
|             String query, int page, String contentCountry) | ||||
|     public abstract InfoItemSearchCollector search( | ||||
|             String query, int page, String contentCountry, EnumSet<Filter> filter) | ||||
|             throws ExtractionException, IOException; | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| package org.schabi.newpipe.extractor.search; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.EnumSet; | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
|  | ||||
| @@ -29,13 +31,17 @@ 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()) { | ||||
|                 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); | ||||
| @@ -45,6 +51,6 @@ public class SearchResult { | ||||
|     } | ||||
|  | ||||
|     public String suggestion = ""; | ||||
|     public List<StreamPreviewInfo> resultList = new Vector<>(); | ||||
|     public List<InfoItem> resultList = new Vector<>(); | ||||
|     public List<Throwable> errors = new Vector<>(); | ||||
| } | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| package org.schabi.newpipe.extractor.search; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 11.05.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * StreamPreviewInfoSearchCollector.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 StreamPreviewInfoSearchCollector extends StreamPreviewInfoCollector { | ||||
|  | ||||
|     private String suggestion = ""; | ||||
|  | ||||
|     public StreamPreviewInfoSearchCollector(UrlIdHandler handler, int serviceId) { | ||||
|         super(handler, serviceId); | ||||
|     } | ||||
|  | ||||
|     public void setSuggestion(String suggestion) { | ||||
|         this.suggestion = suggestion; | ||||
|     } | ||||
|  | ||||
|     public SearchResult getSearchResult() { | ||||
|         SearchResult result = new SearchResult(); | ||||
|         result.suggestion = suggestion; | ||||
|         result.errors = getErrors(); | ||||
|         result.resultList = getItemList(); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -15,8 +15,8 @@ import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelExtractor; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor; | ||||
|  | ||||
|  | ||||
| import java.io.IOException; | ||||
| @@ -137,7 +137,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor { | ||||
|                 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")) { | ||||
|  | ||||
|                 if (url.contains("s.ytimg.com") || url.contains("default_banner")) { | ||||
|                     bannerUrl = null; | ||||
|                 } else { | ||||
|                     bannerUrl = url; | ||||
| @@ -150,8 +151,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StreamPreviewInfoCollector getStreams() throws ParsingException { | ||||
|         StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector(); | ||||
|     public StreamInfoItemCollector getStreams() throws ParsingException { | ||||
|         StreamInfoItemCollector collector = getStreamPreviewInfoCollector(); | ||||
|         Element ul = null; | ||||
|         if(isAjaxPage) { | ||||
|             ul = doc.select("body").first(); | ||||
| @@ -161,7 +162,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor { | ||||
|  | ||||
|         for(final Element li : ul.children()) { | ||||
|             if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) { | ||||
|                 collector.commit(new StreamPreviewInfoExtractor() { | ||||
|                 collector.commit(new StreamInfoItemExtractor() { | ||||
|                     @Override | ||||
|                     public AbstractStreamInfo.StreamType getStreamType() throws ParsingException { | ||||
|                         return AbstractStreamInfo.StreamType.VIDEO_STREAM; | ||||
|   | ||||
| @@ -0,0 +1,83 @@ | ||||
| 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; | ||||
|  | ||||
| /** | ||||
|  * 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 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 el.select("a[class*=\"yt-uix-tile-link\"]").first() | ||||
|                 .text(); | ||||
|     } | ||||
|  | ||||
|     public String getWebPageUrl() throws ParsingException { | ||||
|         return el.select("a[class*=\"yt-uix-tile-link\"]").first() | ||||
|                 .attr("abs:href"); | ||||
|     } | ||||
|  | ||||
|     public long getSubscriberCount() throws ParsingException { | ||||
|         Element subsEl = el.select("span[class*=\"yt-subscriber-count\"]").first(); | ||||
|         if(subsEl == null) { | ||||
|             return 0; | ||||
|         } else { | ||||
|             return Integer.parseInt(subsEl.text().replaceAll("\\D+","")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public int getVideoAmount() throws ParsingException { | ||||
|         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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -7,12 +7,12 @@ import org.schabi.newpipe.extractor.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| 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.search.StreamPreviewInfoSearchCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor; | ||||
|  | ||||
| import java.net.URLEncoder; | ||||
| import java.io.IOException; | ||||
| import java.util.EnumSet; | ||||
|  | ||||
|  | ||||
| /** | ||||
| @@ -45,16 +45,24 @@ public class YoutubeSearchEngine extends SearchEngine { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StreamPreviewInfoSearchCollector search(String query, int page, String languageCode) | ||||
|     public InfoItemSearchCollector search(String query, | ||||
|                                           int page, | ||||
|                                           String languageCode, | ||||
|                                           EnumSet<Filter> filter) | ||||
|             throws IOException, ExtractionException { | ||||
|         StreamPreviewInfoSearchCollector collector = getStreamPreviewInfoSearchCollector(); | ||||
|         InfoItemSearchCollector collector = getInfoItemSearchCollector(); | ||||
|  | ||||
|  | ||||
|         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.STREAM) && !filter.contains(Filter.CHANNEL)) { | ||||
|             url += "&sp=EgIQAQ%253D%253D"; | ||||
|         } else if(!filter.contains(Filter.STREAM) && filter.contains(Filter.CHANNEL)) { | ||||
|             url += "&sp=EgIQAg%253D%253D"; | ||||
|         } | ||||
|  | ||||
|         String site; | ||||
|         //String url = builder.build().toString(); | ||||
| @@ -92,22 +100,20 @@ 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) { | ||||
|                 collector.commit(extractPreviewInfo(el)); | ||||
|             } 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 | ||||
|                 collector.addError(new Exception("unexpected element found:\"" + el + "\"")); | ||||
|                 // noinspection ConstantConditions | ||||
|                 // simply ignore not known items | ||||
|                 // throw new ExtractionException("unexpected element found: \"" + item + "\""); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return collector; | ||||
|     } | ||||
|  | ||||
|     private StreamPreviewInfoExtractor extractPreviewInfo(final Element item) { | ||||
|         return new YoutubeStreamPreviewInfoExtractor(item); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelExtractor; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | ||||
| import org.schabi.newpipe.extractor.search.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamExtractor; | ||||
|  | ||||
| import java.io.IOException; | ||||
|   | ||||
| @@ -20,8 +20,8 @@ import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.MediaFormat; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.VideoStream; | ||||
|  | ||||
| import java.io.IOException; | ||||
| @@ -657,7 +657,7 @@ public class YoutubeStreamExtractor extends StreamExtractor { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StreamPreviewInfoExtractor getNextVideo() throws ParsingException { | ||||
|     public StreamInfoItemExtractor getNextVideo() throws ParsingException { | ||||
|         try { | ||||
|             return extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first() | ||||
|                     .select("li").first()); | ||||
| @@ -667,9 +667,9 @@ public class YoutubeStreamExtractor extends StreamExtractor { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StreamPreviewInfoCollector getRelatedVideos() throws ParsingException { | ||||
|     public StreamInfoItemCollector getRelatedVideos() throws ParsingException { | ||||
|         try { | ||||
|             StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector(); | ||||
|             StreamInfoItemCollector collector = getStreamPreviewInfoCollector(); | ||||
|             Element ul = doc.select("ul[id=\"watch-related\"]").first(); | ||||
|             if(ul != null) { | ||||
|                 for (Element li : ul.children()) { | ||||
| @@ -707,10 +707,10 @@ public class YoutubeStreamExtractor extends StreamExtractor { | ||||
|     } | ||||
|  | ||||
|     /**Provides information about links to other videos on the video page, such as related videos. | ||||
|      * This is encapsulated in a StreamPreviewInfo object, | ||||
|      * This is encapsulated in a StreamInfoItem object, | ||||
|      * which is a subset of the fields in a full StreamInfo.*/ | ||||
|     private StreamPreviewInfoExtractor extractVideoPreviewInfo(final Element li) { | ||||
|         return new StreamPreviewInfoExtractor() { | ||||
|     private StreamInfoItemExtractor extractVideoPreviewInfo(final Element li) { | ||||
|         return new StreamInfoItemExtractor() { | ||||
|             @Override | ||||
|             public AbstractStreamInfo.StreamType getStreamType() throws ParsingException { | ||||
|                 return null; | ||||
|   | ||||
| @@ -4,11 +4,11 @@ import org.jsoup.nodes.Element; | ||||
| import org.schabi.newpipe.extractor.AbstractStreamInfo; | ||||
| import org.schabi.newpipe.extractor.Parser; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor; | ||||
| 
 | ||||
| /** | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeStreamPreviewInfoExtractor.java is part of NewPipe. | ||||
|  * YoutubeStreamInfoItemExtractor.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 | ||||
| @@ -24,11 +24,11 @@ import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtractor { | ||||
| public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor { | ||||
| 
 | ||||
|     private final Element item; | ||||
| 
 | ||||
|     public YoutubeStreamPreviewInfoExtractor(Element item) { | ||||
|     public YoutubeStreamInfoItemExtractor(Element item) { | ||||
|         this.item = item; | ||||
|     } | ||||
| 
 | ||||
| @@ -83,9 +83,12 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra | ||||
|     @Override | ||||
|     public String getUploadDate() throws ParsingException { | ||||
|         try { | ||||
|             return item.select("div[class=\"yt-lockup-meta\"]").first() | ||||
|                     .select("li").first() | ||||
|                     .text(); | ||||
|             Element div = item.select("div[class=\"yt-lockup-meta\"]").first(); | ||||
|             if(div == null) { | ||||
|                 return null; | ||||
|             } else { | ||||
|                 return div.select("li").first().text(); | ||||
|             } | ||||
|         } catch(Exception e) { | ||||
|             throw new ParsingException("Could not get uplaod date", e); | ||||
|         } | ||||
| @@ -96,9 +99,13 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra | ||||
|         String output; | ||||
|         String input; | ||||
|         try { | ||||
|             input = item.select("div[class=\"yt-lockup-meta\"]").first() | ||||
|                     .select("li").get(1) | ||||
|             Element div = item.select("div[class=\"yt-lockup-meta\"]").first(); | ||||
|             if(div == null) { | ||||
|                 return -1; | ||||
|             } else { | ||||
|                 input = div.select("li").get(1) | ||||
|                         .text(); | ||||
|             } | ||||
|         } catch (IndexOutOfBoundsException e) { | ||||
|             if(isLiveStream(item)) { | ||||
|                 // -1 for no view count | ||||
| @@ -4,7 +4,7 @@ import org.schabi.newpipe.extractor.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.search.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.SuggestionExtractor; | ||||
| import org.w3c.dom.Node; | ||||
| import org.w3c.dom.NodeList; | ||||
| import org.xml.sax.InputSource; | ||||
| @@ -15,7 +15,6 @@ import java.io.IOException; | ||||
| import java.net.URLEncoder; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
|  | ||||
| import javax.xml.parsers.DocumentBuilder; | ||||
| import javax.xml.parsers.DocumentBuilderFactory; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ public abstract class StreamExtractor { | ||||
|     private int serviceId; | ||||
|     private String url; | ||||
|     private UrlIdHandler urlIdHandler; | ||||
|     private StreamPreviewInfoCollector previewInfoCollector; | ||||
|     private StreamInfoItemCollector previewInfoCollector; | ||||
|  | ||||
|     public class ExtractorInitException extends ExtractionException { | ||||
|         public ExtractorInitException(String message) { | ||||
| @@ -61,10 +61,10 @@ public abstract class StreamExtractor { | ||||
|     public StreamExtractor(UrlIdHandler urlIdHandler, String url, int serviceId) { | ||||
|         this.serviceId = serviceId; | ||||
|         this.urlIdHandler = urlIdHandler; | ||||
|         previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId); | ||||
|         previewInfoCollector = new StreamInfoItemCollector(urlIdHandler, serviceId); | ||||
|     } | ||||
|  | ||||
|     protected StreamPreviewInfoCollector getStreamPreviewInfoCollector() { | ||||
|     protected StreamInfoItemCollector getStreamPreviewInfoCollector() { | ||||
|         return previewInfoCollector; | ||||
|     } | ||||
|  | ||||
| @@ -94,8 +94,8 @@ public abstract class StreamExtractor { | ||||
|     public abstract String getAverageRating() throws ParsingException; | ||||
|     public abstract int getLikeCount() throws ParsingException; | ||||
|     public abstract int getDislikeCount() throws ParsingException; | ||||
|     public abstract StreamPreviewInfoExtractor getNextVideo() throws ParsingException; | ||||
|     public abstract StreamPreviewInfoCollector getRelatedVideos() throws ParsingException; | ||||
|     public abstract StreamInfoItemExtractor getNextVideo() throws ParsingException; | ||||
|     public abstract StreamInfoItemCollector getRelatedVideos() throws ParsingException; | ||||
|     public abstract String getPageUrl(); | ||||
|     public abstract StreamInfo.StreamType getStreamType() throws ParsingException; | ||||
|     public int getServiceId() { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package org.schabi.newpipe.extractor.stream_info; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.AbstractStreamInfo; | ||||
| import org.schabi.newpipe.extractor.DashMpdParser; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
|  | ||||
| @@ -55,14 +56,14 @@ public class StreamInfo extends AbstractStreamInfo { | ||||
|         this.view_count = avi.view_count; | ||||
|  | ||||
|         //todo: better than this | ||||
|         if(avi instanceof StreamPreviewInfo) { | ||||
|         if(avi instanceof StreamInfoItem) { | ||||
|             //shitty String to convert code | ||||
|             /* | ||||
|             String dur = ((StreamPreviewInfo)avi).duration; | ||||
|             String dur = ((StreamInfoItem)avi).duration; | ||||
|             int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":"))); | ||||
|             int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length())); | ||||
|             */ | ||||
|             this.duration = ((StreamPreviewInfo)avi).duration; | ||||
|             this.duration = ((StreamInfoItem)avi).duration; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -241,12 +242,12 @@ public class StreamInfo extends AbstractStreamInfo { | ||||
|             // get next video | ||||
|             if(streamInfo.next_video != null) | ||||
|             { | ||||
|                 StreamPreviewInfoCollector c = new StreamPreviewInfoCollector( | ||||
|                 StreamInfoItemCollector c = new StreamInfoItemCollector( | ||||
|                         extractor.getUrlIdHandler(), extractor.getServiceId()); | ||||
|                 StreamPreviewInfoExtractor nextVideo = extractor.getNextVideo(); | ||||
|                 StreamInfoItemExtractor nextVideo = extractor.getNextVideo(); | ||||
|                 c.commit(nextVideo); | ||||
|                 if(c.getItemList().size() != 0) { | ||||
|                     streamInfo.next_video = c.getItemList().get(0); | ||||
|                     streamInfo.next_video = (StreamInfoItem) c.getItemList().get(0); | ||||
|                 } | ||||
|                 streamInfo.errors.addAll(c.getErrors()); | ||||
|             } | ||||
| @@ -256,7 +257,7 @@ public class StreamInfo extends AbstractStreamInfo { | ||||
|         } | ||||
|         try { | ||||
|             // get related videos | ||||
|             StreamPreviewInfoCollector c = extractor.getRelatedVideos(); | ||||
|             StreamInfoItemCollector c = extractor.getRelatedVideos(); | ||||
|             streamInfo.related_streams = c.getItemList(); | ||||
|             streamInfo.errors.addAll(c.getErrors()); | ||||
|         } catch(Exception e) { | ||||
| @@ -284,8 +285,8 @@ public class StreamInfo extends AbstractStreamInfo { | ||||
|     public int like_count = -1; | ||||
|     public int dislike_count = -1; | ||||
|     public String average_rating = ""; | ||||
|     public StreamPreviewInfo next_video = null; | ||||
|     public List<StreamPreviewInfo> related_streams = null; | ||||
|     public StreamInfoItem next_video = null; | ||||
|     public List<InfoItem> related_streams = null; | ||||
|     //in seconds. some metadata is not passed using a StreamInfo object! | ||||
|     public int start_position = 0; | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ package org.schabi.newpipe.extractor.stream_info; | ||||
|  * Created by Christian Schabesberger on 26.08.15. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * StreamPreviewInfo.java is part of NewPipe. | ||||
|  * StreamInfoItem.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 | ||||
| @@ -21,8 +21,21 @@ package org.schabi.newpipe.extractor.stream_info; | ||||
|  */ | ||||
| 
 | ||||
| import org.schabi.newpipe.extractor.AbstractStreamInfo; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| 
 | ||||
| /**Info object for previews of unopened videos, eg search results, related videos*/ | ||||
| public class StreamPreviewInfo extends AbstractStreamInfo { | ||||
| public class StreamInfoItem extends AbstractStreamInfo implements InfoItem { | ||||
|     public int duration; | ||||
| 
 | ||||
|     public InfoType infoType() { | ||||
|         return InfoType.STREAM; | ||||
|     } | ||||
| 
 | ||||
|     public String getTitle() { | ||||
|         return title; | ||||
|     } | ||||
| 
 | ||||
|     public String getLink() { | ||||
|         return webpage_url; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,99 @@ | ||||
| package org.schabi.newpipe.extractor.stream_info; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.InfoItemCollector; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.exceptions.FoundAdException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 28.02.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * StreamInfoItemCollector.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 StreamInfoItemCollector extends InfoItemCollector { | ||||
|  | ||||
|     private UrlIdHandler urlIdHandler; | ||||
|  | ||||
|     public StreamInfoItemCollector(UrlIdHandler handler, int serviceId) { | ||||
|         super(serviceId); | ||||
|         urlIdHandler = handler; | ||||
|     } | ||||
|  | ||||
|     private UrlIdHandler getUrlIdHandler() { | ||||
|         return urlIdHandler; | ||||
|     } | ||||
|  | ||||
|     public StreamInfoItem extract(StreamInfoItemExtractor extractor) throws Exception { | ||||
|  | ||||
|         StreamInfoItem resultItem = new StreamInfoItem(); | ||||
|         // importand information | ||||
|         resultItem.service_id = getServiceId(); | ||||
|         resultItem.webpage_url = extractor.getWebPageUrl(); | ||||
|         if (getUrlIdHandler() == null) { | ||||
|             throw new ParsingException("Error: UrlIdHandler not set"); | ||||
|         } else if (!resultItem.webpage_url.isEmpty()) { | ||||
|             resultItem.id = NewPipe.getService(getServiceId()) | ||||
|                     .getUrlIdHandlerInstance() | ||||
|                     .getId(resultItem.webpage_url); | ||||
|         } | ||||
|         resultItem.title = extractor.getTitle(); | ||||
|         resultItem.stream_type = extractor.getStreamType(); | ||||
|  | ||||
|         // optional information | ||||
|         try { | ||||
|             resultItem.duration = extractor.getDuration(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.uploader = extractor.getUploader(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.upload_date = extractor.getUploadDate(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.view_count = extractor.getViewCount(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         try { | ||||
|             resultItem.thumbnail_url = extractor.getThumbnailUrl(); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|         return resultItem; | ||||
|     } | ||||
|  | ||||
|     public void commit(StreamInfoItemExtractor extractor) throws ParsingException { | ||||
|         try { | ||||
|             addItem(extract(extractor)); | ||||
|         } catch(FoundAdException ae) { | ||||
|             System.out.println("AD_WARNING: " + ae.getMessage()); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
|  * Created by Christian Schabesberger on 28.02.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * StreamPreviewInfoExtractor.java is part of NewPipe. | ||||
|  * StreamInfoItemExtractor.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 | ||||
| @@ -23,7 +23,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public interface StreamPreviewInfoExtractor { | ||||
| public interface StreamInfoItemExtractor { | ||||
|     AbstractStreamInfo.StreamType getStreamType() throws ParsingException; | ||||
|     String getWebPageUrl() throws ParsingException; | ||||
|     String getTitle() throws ParsingException; | ||||
| @@ -1,102 +0,0 @@ | ||||
| package org.schabi.newpipe.extractor.stream_info; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.UrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.exceptions.FoundAdException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 28.02.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org> | ||||
|  * StreamPreviewInfoCollector.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 StreamPreviewInfoCollector { | ||||
|     private List<StreamPreviewInfo> itemList = new Vector<>(); | ||||
|     private List<Throwable> errors = new Vector<>(); | ||||
|     private UrlIdHandler urlIdHandler; | ||||
|     private int serviceId = -1; | ||||
|  | ||||
|     public StreamPreviewInfoCollector(UrlIdHandler handler, int serviceId) { | ||||
|         urlIdHandler = handler; | ||||
|         this.serviceId = serviceId; | ||||
|     } | ||||
|  | ||||
|     public List<StreamPreviewInfo> getItemList() { | ||||
|         return itemList; | ||||
|     } | ||||
|  | ||||
|     public List<Throwable> getErrors() { | ||||
|         return errors; | ||||
|     } | ||||
|  | ||||
|     public void addError(Exception e) { | ||||
|         errors.add(e); | ||||
|     } | ||||
|  | ||||
|     public void commit(StreamPreviewInfoExtractor extractor) throws ParsingException { | ||||
|         try { | ||||
|             StreamPreviewInfo resultItem = new StreamPreviewInfo(); | ||||
|             // importand information | ||||
|             resultItem.service_id = serviceId; | ||||
|             resultItem.webpage_url = extractor.getWebPageUrl(); | ||||
|             if (urlIdHandler == null) { | ||||
|                 throw new ParsingException("Error: UrlIdHandler not set"); | ||||
|             } else if(!resultItem.webpage_url.isEmpty()) { | ||||
|                 resultItem.id = NewPipe.getService(serviceId).getUrlIdHandlerInstance().getId(resultItem.webpage_url); | ||||
|             } | ||||
|             resultItem.title = extractor.getTitle(); | ||||
|             resultItem.stream_type = extractor.getStreamType(); | ||||
|  | ||||
|             // optional information | ||||
|             try { | ||||
|                 resultItem.duration = extractor.getDuration(); | ||||
|             } catch (Exception e) { | ||||
|                 addError(e); | ||||
|             } | ||||
|             try { | ||||
|                 resultItem.uploader = extractor.getUploader(); | ||||
|             } catch (Exception e) { | ||||
|                 addError(e); | ||||
|             } | ||||
|             try { | ||||
|                 resultItem.upload_date = extractor.getUploadDate(); | ||||
|             } catch (Exception e) { | ||||
|                 addError(e); | ||||
|             } | ||||
|             try { | ||||
|                 resultItem.view_count = extractor.getViewCount(); | ||||
|             } catch (Exception e) { | ||||
|                 addError(e); | ||||
|             } | ||||
|             try { | ||||
|                 resultItem.thumbnail_url = extractor.getThumbnailUrl(); | ||||
|             } catch (Exception e) { | ||||
|                 addError(e); | ||||
|             } | ||||
|             itemList.add(resultItem); | ||||
|         } catch(FoundAdException ae) { | ||||
|             System.out.println("AD_WARNING: " + ae.getMessage()); | ||||
|         } catch (Exception e) { | ||||
|             addError(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| 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 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); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public InfoItem.InfoType infoType() { | ||||
|         return InfoItem.InfoType.CHANNEL; | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,11 @@ | ||||
| package org.schabi.newpipe.info_list; | ||||
|  | ||||
| import android.app.Activity; | ||||
| 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; | ||||
| @@ -11,7 +13,9 @@ import com.nostra13.universalimageloader.core.ImageLoader; | ||||
| import org.schabi.newpipe.ImageErrorLoadingListener; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.AbstractStreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 26.09.16. | ||||
| @@ -35,8 +39,17 @@ import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo; | ||||
|  | ||||
| public class InfoItemBuilder { | ||||
|  | ||||
|     public interface OnItemSelectedListener { | ||||
|         void selected(String url); | ||||
|     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, int serviceId); | ||||
|     } | ||||
|  | ||||
|     private Activity activity = null; | ||||
| @@ -44,18 +57,75 @@ 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; | ||||
|         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 setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) { | ||||
|         this.onItemSelectedListener = onItemSelectedListener; | ||||
|     public void setOnStreamInfoItemSelectedListener( | ||||
|             OnInfoItemSelectedListener listener) { | ||||
|         this.onStreamInfoItemSelectedListener = listener; | ||||
|     } | ||||
|  | ||||
|     public void buildByHolder(InfoItemHolder holder, final StreamPreviewInfo info) { | ||||
|     public void setOnChannelInfoItemSelectedListener( | ||||
|             OnInfoItemSelectedListener listener) { | ||||
|         this.onChannelInfoItemSelectedListener = listener; | ||||
|     } | ||||
|  | ||||
|     public void buildByHolder(InfoItemHolder holder, final InfoItem i) { | ||||
|         if(i.infoType() != holder.infoType()) | ||||
|             return; | ||||
|         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"); | ||||
|         } | ||||
|         // fill holder with information | ||||
|         holder.itemVideoTitleView.setText(info.title); | ||||
|         if(info.uploader != null && !info.uploader.isEmpty()) { | ||||
| @@ -92,29 +162,55 @@ 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, info.service_id); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public View buildView(ViewGroup parent, final StreamPreviewInfo 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.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(), 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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
| @@ -2,12 +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.stream_info.StreamPreviewInfo; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Vector; | ||||
| @@ -33,47 +34,79 @@ 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<StreamPreviewInfo> 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 setOnStreamInfoItemSelectedListener | ||||
|             (InfoItemBuilder.OnInfoItemSelectedListener listener) { | ||||
|         infoItemBuilder.setOnStreamInfoItemSelectedListener(listener); | ||||
|     } | ||||
|  | ||||
|     public void addStreamItemList(List<StreamPreviewInfo> videos) { | ||||
|     public void setOnChannelInfoItemSelectedListener | ||||
|             (InfoItemBuilder.OnInfoItemSelectedListener listener) { | ||||
|         infoItemBuilder.setOnChannelInfoItemSelectedListener(listener); | ||||
|     } | ||||
|  | ||||
|     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(); | ||||
|     } | ||||
|  | ||||
|     // don't ask why we have to do that this way... it's android accept it -.- | ||||
|     @Override | ||||
|     public int getItemViewType(int position) { | ||||
|         switch(infoItemList.get(position).infoType()) { | ||||
|             case STREAM: | ||||
|                 return 0; | ||||
|             case CHANNEL: | ||||
|                 return 1; | ||||
|             case PLAYLIST: | ||||
|                 return 2; | ||||
|             default: | ||||
|                 Log.e(TAG, "Trollolo"); | ||||
|                 return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @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); | ||||
|     public InfoItemHolder onCreateViewHolder(ViewGroup parent, int type) { | ||||
|         switch(type) { | ||||
|             case 0: | ||||
|                 return new StreamInfoItemHolder(LayoutInflater.from(parent.getContext()) | ||||
|                         .inflate(R.layout.stream_item, parent, false)); | ||||
|             case 1: | ||||
|                 return new ChannelInfoItemHolder(LayoutInflater.from(parent.getContext()) | ||||
|                         .inflate(R.layout.channel_item, parent, false)); | ||||
|             case 2: | ||||
|                 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)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
| @@ -297,6 +297,11 @@ public class ErrorActivity extends ThemableActivity { | ||||
|         } | ||||
|  | ||||
|         errorView.setText(formErrorText(errorList)); | ||||
|  | ||||
|         //print stack trace once again for debugging: | ||||
|         for(String e : errorList) { | ||||
|             Log.e(TAG, e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -19,8 +19,10 @@ 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; | ||||
| import org.schabi.newpipe.extractor.search.SearchResult; | ||||
| import org.schabi.newpipe.info_list.InfoItemBuilder; | ||||
| import org.schabi.newpipe.report.ErrorActivity; | ||||
| @@ -29,6 +31,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; | ||||
|  | ||||
| @@ -56,6 +60,9 @@ public class SearchInfoItemFragment extends Fragment { | ||||
|  | ||||
|     private static final String TAG = SearchInfoItemFragment.class.toString(); | ||||
|  | ||||
|     private EnumSet<SearchEngine.Filter> filter = | ||||
|             EnumSet.of(SearchEngine.Filter.CHANNEL, SearchEngine.Filter.STREAM); | ||||
|  | ||||
|     /** | ||||
|      * Listener for search queries | ||||
|      */ | ||||
| @@ -166,7 +173,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,12 +220,19 @@ public class SearchInfoItemFragment extends Fragment { | ||||
|  | ||||
|         infoListAdapter = new InfoListAdapter(getActivity(), | ||||
|                 getActivity().findViewById(android.R.id.content)); | ||||
|         infoListAdapter.setOnItemSelectedListener(new InfoItemBuilder.OnItemSelectedListener() { | ||||
|         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() { | ||||
| @@ -250,6 +264,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(); | ||||
| @@ -275,6 +296,32 @@ public class SearchInfoItemFragment extends Fragment { | ||||
|         setupSearchView(searchView); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         switch(item.getItemId()) { | ||||
|             case R.id.menu_filter_all: | ||||
|                 changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM, SearchEngine.Filter.CHANNEL)); | ||||
|                 return true; | ||||
|             case R.id.menu_filter_video: | ||||
|                 changeFilter(item, EnumSet.of(SearchEngine.Filter.STREAM)); | ||||
|                 return true; | ||||
|             case R.id.menu_filter_channel: | ||||
|                 changeFilter(item, EnumSet.of(SearchEngine.Filter.CHANNEL)); | ||||
|                 return true; | ||||
|             default: | ||||
|                 return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void changeFilter(MenuItem item, EnumSet<SearchEngine.Filter> filter) { | ||||
|         this.filter = filter; | ||||
|         item.setChecked(true); | ||||
|         if(searchQuery != null && !searchQuery.isEmpty()) { | ||||
|             Log.d(TAG, "Fuck+ " + searchQuery); | ||||
|             search(searchQuery); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void setupSearchView(SearchView searchView) { | ||||
|         suggestionListAdapter = new SuggestionListAdapter(getActivity()); | ||||
|         searchView.setSuggestionsAdapter(suggestionListAdapter); | ||||
| @@ -298,7 +345,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(), | ||||
|                 filter); | ||||
|     } | ||||
|  | ||||
|     private void setDoneLoading() { | ||||
|   | ||||
| @@ -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,13 +110,14 @@ 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)); | ||||
|                 } | ||||
|  | ||||
|                 // 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:"); | ||||
| @@ -117,11 +126,17 @@ public class SearchWorker { | ||||
|                         Log.e(TAG, "------"); | ||||
|                     } | ||||
|  | ||||
|                     View rootView = a.findViewById(android.R.id.content); | ||||
|                     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) { | ||||
| @@ -180,11 +195,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(); | ||||
|     } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import android.widget.Toast; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.search.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.SuggestionExtractor; | ||||
| import org.schabi.newpipe.report.ErrorActivity; | ||||
| import org.schabi.newpipe.R; | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_headset_black_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 349 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_screen_rotation_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 858 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_share_black_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 398 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_screen_rotation_white_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 639 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_share_black_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 262 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-nodpi/buddy_channel_item.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-nodpi/dummi_thumbnail_playlist.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 869 B | 
| Before Width: | Height: | Size: 209 B | 
| Before Width: | Height: | Size: 786 B | 
| Before Width: | Height: | Size: 1.2 KiB | 
| Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 446 B | 
| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 483 B | 
| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 675 B | 
| After Width: | Height: | Size: 2.5 KiB | 
| Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B | 
							
								
								
									
										91
									
								
								app/src/main/res/layout/channel_item.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| <?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" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <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/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> | ||||
|  | ||||
|  | ||||
|             </LinearLayout> | ||||
|  | ||||
|         </RelativeLayout> | ||||
|     </LinearLayout> | ||||
|  | ||||
|     <Button | ||||
|         android:id="@+id/item_button" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:background="?attr/selectableItemBackground"/> | ||||
| </FrameLayout> | ||||
| @@ -14,7 +14,7 @@ | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         app:layoutManager="LinearLayoutManager" | ||||
|         tools:listitem="@layout/video_item" | ||||
|         tools:listitem="@layout/stream_item" | ||||
|         android:scrollbars="vertical"/> | ||||
|  | ||||
|     <ProgressBar android:id="@+id/progressBar" | ||||
|   | ||||
| @@ -69,7 +69,7 @@ | ||||
|                             android:layout_height="match_parent" | ||||
|                             android:layout_below="@id/detail_stream_thumbnail_window_layout" | ||||
|                             android:background="?android:windowBackground" | ||||
|                             android:visibility="gone"> | ||||
|                             android:visibility="visible"> | ||||
|  | ||||
|                 <LinearLayout | ||||
|                     android:layout_width="match_parent" | ||||
| @@ -226,6 +226,9 @@ | ||||
|                             android:background="#000" | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="1px" | ||||
|  | ||||
|                             android:layout_toRightOf="@+id/detail_uploader_thumbnail_view" | ||||
|                             android:layout_toEndOf="@+id/detail_uploader_thumbnail_view" | ||||
|                             android:layout_below="@id/detail_uploader_thumbnail_view"/> | ||||
|                     </RelativeLayout> | ||||
|                     <Button | ||||
|   | ||||
							
								
								
									
										71
									
								
								app/src/main/res/layout/play_list_item.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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> | ||||
| @@ -1,11 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <item android:id="@+id/action_settings" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/settings"/> | ||||
|  | ||||
|     <item android:id="@+id/action_show_downloads" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/downloads" /> | ||||
|  | ||||
|     <item android:id="@+id/action_settings" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/settings"/> | ||||
|  | ||||
| </menu> | ||||
| @@ -6,4 +6,15 @@ | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:title="@string/search" | ||||
|         app:actionViewClass="android.support.v7.widget.SearchView" /> | ||||
|  | ||||
|     <group android:id="@+id/search_filter_group" | ||||
|         android:checkableBehavior="single"> | ||||
|         <item android:id="@+id/menu_filter_all" | ||||
|             android:title = "@string/all" | ||||
|             android:checked = "true"/> | ||||
|         <item android:id="@+id/menu_filter_video" | ||||
|             android:title = "@string/video"/> | ||||
|         <item android:id="@+id/menu_filter_channel" | ||||
|             android:title = "@string/channel"/> | ||||
|     </group> | ||||
| </menu> | ||||
| @@ -5,5 +5,5 @@ | ||||
|     <item android:id="@+id/menu_item_screen_rotation" | ||||
|         android:title="@string/screen_rotation" | ||||
|         app:showAsAction="always" | ||||
|         android:icon="@drawable/ic_screen_rotation_white"/> | ||||
|         android:icon="@drawable/ic_screen_rotation_white_24dp"/> | ||||
| </menu> | ||||
| @@ -15,12 +15,12 @@ | ||||
|     <item android:id="@+id/menu_item_share" | ||||
|         android:title="@string/share" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:icon="@drawable/ic_share_black"/> | ||||
|         android:icon="?attr/share"/> | ||||
|  | ||||
|     <item android:id="@+id/action_play_with_kodi" | ||||
|         android:title="@string/play_with_kodi_title" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:icon="@drawable/ic_cast_black"/> | ||||
|         android:icon="?attr/cast"/> | ||||
|  | ||||
|     <item android:id="@+id/menu_item_openInBrowser" | ||||
|         app:showAsAction="never" | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <item android:id="@+id/action_search" | ||||
|         android:icon="@android:drawable/ic_menu_search" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:title="@string/search" | ||||
|         app:actionViewClass="android.support.v7.widget.SearchView" /> | ||||
|  | ||||
|     <item android:id="@+id/action_settings" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/settings"/> | ||||
|  | ||||
|     <item android:id="@+id/action_show_downloads" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/downloads" /> | ||||
|  | ||||
|     <item android:id="@+id/action_report_error" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/report_error" /> | ||||
| </menu> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|         <item name="thumbs_down">@drawable/ic_thumb_down_black_24dp</item> | ||||
|         <item name="audio">@drawable/ic_headset_black_24dp</item> | ||||
|         <item name="download">@drawable/ic_file_download_black_24dp</item> | ||||
|         <item name="share">@drawable/ic_share_black</item> | ||||
|         <item name="share">@drawable/ic_share_black_24dp</item> | ||||
|         <item name="cast">@drawable/ic_cast_black_24dp</item> | ||||
|         <item name="rss">@drawable/ic_rss_feed_white_24dp</item> | ||||
|     </style> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -84,6 +84,8 @@ | ||||
|     <string name="downloads_title">Downloads</string> | ||||
|     <string name="settings_title">Settings</string> | ||||
|     <string name="error_report_title">Error report</string> | ||||
|     <string name="all">All</string> | ||||
|     <string name="channel">Channel</string> | ||||
|  | ||||
|     <!-- error strings --> | ||||
|     <string name="general_error">Error</string> | ||||
| @@ -147,6 +149,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> | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
|         <item name="thumbs_down">@drawable/ic_thumb_down_black_24dp</item> | ||||
|         <item name="audio">@drawable/ic_headset_black_24dp</item> | ||||
|         <item name="download">@drawable/ic_file_download_black_24dp</item> | ||||
|         <item name="share">@drawable/ic_share_black</item> | ||||
|         <item name="share">@drawable/ic_share_black_24dp</item> | ||||
|         <item name="cast">@drawable/ic_cast_black_24dp</item> | ||||
|         <item name="rss">@drawable/ic_rss_feed_white_24dp</item> | ||||
|     </style> | ||||
|   | ||||
| @@ -1,16 +1,21 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| 
 | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelExtractor; | ||||
| 
 | ||||
| import static junit.framework.Assert.assertEquals; | ||||
| import static junit.framework.Assert.assertNotNull; | ||||
| import static junit.framework.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 12.09.16. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeSearchEngineTest.java is part of NewPipe. | ||||
|  * YoutubeSearchEngineStreamTest.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 | ||||
| @@ -26,56 +31,70 @@ import org.schabi.newpipe.extractor.channel.ChannelExtractor; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeChannelExtractorTest extends AndroidTestCase  { | ||||
| /** | ||||
|  * Test for {@link ChannelExtractor} | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeChannelExtractorTest  { | ||||
| 
 | ||||
|     ChannelExtractor extractor; | ||||
|     @Override | ||||
| 
 | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw", 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetDownloader()  throws Exception { | ||||
|         assertNotNull(NewPipe.getDownloader()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetChannelName() throws Exception { | ||||
|         assertEquals(extractor.getChannelName(), "Gronkh"); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetAvatarUrl() throws Exception { | ||||
|         assertTrue(extractor.getAvatarUrl(), extractor.getAvatarUrl().contains("yt3")); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetBannerurl() throws Exception { | ||||
|         assertTrue(extractor.getBannerUrl(), extractor.getBannerUrl().contains("yt3")); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetFeedUrl() throws Exception { | ||||
|         assertTrue(extractor.getFeedUrl(), extractor.getFeedUrl().contains("feed")); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetStreams() throws Exception { | ||||
|         assertTrue("no streams are received", !extractor.getStreams().getItemList().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetStreamsErrors() throws Exception { | ||||
|         assertTrue("errors during stream list extraction", extractor.getStreams().getErrors().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testHasNextPage() throws Exception { | ||||
|         // this particular example (link) has a next page !!! | ||||
|         assertTrue("no next page link found", extractor.hasNextPage()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetNextPage() throws Exception { | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw", 1); | ||||
|         assertTrue("next page didn't have content", !extractor.getStreams().getItemList().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetNextNextPageUrl() throws Exception { | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getChannelExtractorInstance("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw", 2); | ||||
| @@ -1,19 +1,25 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | ||||
| import org.schabi.newpipe.extractor.search.SearchResult; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.EnumSet; | ||||
| 
 | ||||
| import static junit.framework.Assert.assertEquals; | ||||
| import static junit.framework.Assert.assertFalse; | ||||
| import static junit.framework.Assert.assertTrue; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 29.12.15. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeSearchEngineTest.java is part of NewPipe. | ||||
|  * YoutubeSearchEngineStreamTest.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 | ||||
| @@ -29,26 +35,33 @@ import java.util.List; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeSearchEngineTest extends AndroidTestCase { | ||||
| /** | ||||
|  * Test for {@link SearchEngine} | ||||
|  */ | ||||
| public class YoutubeSearchEngineAllTest { | ||||
|     private SearchResult result; | ||||
| 
 | ||||
|     @Override | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); | ||||
| 
 | ||||
|         result = engine.search("this is something boring", 0, "de").getSearchResult(); | ||||
|         result = engine.search("asdf", 0, "de", | ||||
|                 EnumSet.of(SearchEngine.Filter.CHANNEL, | ||||
|                         SearchEngine.Filter.STREAM)).getSearchResult(); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testResultList() { | ||||
|         assertFalse(result.resultList.isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testResultErrors() { | ||||
|         assertTrue(result.errors == null || result.errors.isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testSuggestion() { | ||||
|         //todo write a real test | ||||
|         assertTrue(result.suggestion != null); | ||||
| @@ -0,0 +1,73 @@ | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
|  | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | ||||
| import org.schabi.newpipe.extractor.search.SearchResult; | ||||
|  | ||||
| import java.util.EnumSet; | ||||
|  | ||||
| import static junit.framework.Assert.assertEquals; | ||||
| import static junit.framework.Assert.assertFalse; | ||||
| import static junit.framework.Assert.assertTrue; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 29.12.15. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeSearchEngineStreamTest.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/>. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Test for {@link SearchEngine} | ||||
|  */ | ||||
| public class YoutubeSearchEngineChannelTest { | ||||
|     private SearchResult result; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); | ||||
|  | ||||
|         result = engine.search("gronkh", 0, "de", | ||||
|                 EnumSet.of(SearchEngine.Filter.CHANNEL)).getSearchResult(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testResultList() { | ||||
|         assertFalse(result.resultList.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testChannelItemType() { | ||||
|         assertEquals(result.resultList.get(0).infoType(), InfoItem.InfoType.CHANNEL); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testResultErrors() { | ||||
|         assertTrue(result.errors == null || result.errors.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSuggestion() { | ||||
|         //todo write a real test | ||||
|         assertTrue(result.suggestion != null); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,74 @@ | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
|  | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
|  | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.search.SearchEngine; | ||||
| import org.schabi.newpipe.extractor.search.SearchResult; | ||||
|  | ||||
| import java.util.EnumSet; | ||||
|  | ||||
| import static junit.framework.Assert.assertEquals; | ||||
| import static junit.framework.Assert.assertFalse; | ||||
| import static junit.framework.Assert.assertTrue; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 29.12.15. | ||||
|  * | ||||
|  * Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> | ||||
|  * YoutubeSearchEngineStreamTest.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/>. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Test for {@link SearchEngine} | ||||
|  */ | ||||
| public class YoutubeSearchEngineStreamTest { | ||||
|     private SearchResult result; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         SearchEngine engine = NewPipe.getService("Youtube").getSearchEngineInstance(); | ||||
|  | ||||
|         result = engine.search("this is something boring", 0, "de", | ||||
|                 EnumSet.of(SearchEngine.Filter.STREAM)).getSearchResult(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testResultList() { | ||||
|         assertFalse(result.resultList.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testChannelItemType() { | ||||
|         assertEquals(result.resultList.get(0).infoType(), InfoItem.InfoType.STREAM); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testResultErrors() { | ||||
|         assertTrue(result.errors == null || result.errors.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testSuggestion() { | ||||
|         //todo write a real test | ||||
|         assertTrue(result.suggestion != null); | ||||
|     } | ||||
| } | ||||
| @@ -1,15 +1,13 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.search.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.SuggestionExtractor; | ||||
| import org.schabi.newpipe.extractor.services.youtube.YoutubeSuggestionExtractor; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.List; | ||||
| import static junit.framework.Assert.assertFalse; | ||||
| 
 | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 18.11.16. | ||||
| @@ -31,18 +29,21 @@ import java.util.List; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeSearchResultTest extends AndroidTestCase { | ||||
| /** | ||||
|  * Test for {@link SuggestionExtractor} | ||||
|  */ | ||||
| public class YoutubeSearchResultTest { | ||||
|     List<String> suggestionReply; | ||||
| 
 | ||||
| 
 | ||||
|     @Override | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         SuggestionExtractor engine = new YoutubeSuggestionExtractor(0); | ||||
|         suggestionReply = engine.suggestionList("hello", "de"); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testIfSuggestions() { | ||||
|         assertFalse(suggestionReply.isEmpty()); | ||||
|     } | ||||
| @@ -1,7 +1,7 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.AbstractStreamInfo; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| @@ -12,6 +12,8 @@ import org.schabi.newpipe.extractor.stream_info.VideoStream; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import static junit.framework.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 30.12.15. | ||||
|  * | ||||
| @@ -32,22 +34,27 @@ import java.io.IOException; | ||||
|  * along with NewPipe.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| 
 | ||||
| public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase { | ||||
| /** | ||||
|  * Test for {@link StreamExtractor} | ||||
|  */ | ||||
| public class YoutubeStreamExtractorDefaultTest { | ||||
|     public static final String HTTPS = "https://"; | ||||
|     private StreamExtractor extractor; | ||||
| 
 | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getExtractorInstance("https://www.youtube.com/watch?v=YQHsXMglC9A"); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetInvalidTimeStamp() throws ParsingException { | ||||
|         assertTrue(Integer.toString(extractor.getTimeStamp()), | ||||
|                 extractor.getTimeStamp() <= 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetValidTimeStamp() throws ExtractionException, IOException { | ||||
|         StreamExtractor extractor = | ||||
|                 NewPipe.getService("Youtube") | ||||
| @@ -56,49 +63,60 @@ public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase { | ||||
|                 extractor.getTimeStamp() == 174); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetTitle() throws ParsingException { | ||||
|         assertTrue(!extractor.getTitle().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetDescription() throws ParsingException { | ||||
|         assertTrue(extractor.getDescription() != null); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploader() throws ParsingException { | ||||
|         assertTrue(!extractor.getUploader().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetLength() throws ParsingException { | ||||
|         assertTrue(extractor.getLength() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetViewCount() throws ParsingException { | ||||
|         assertTrue(Long.toString(extractor.getViewCount()), | ||||
|                 extractor.getViewCount() > /* specific to that video */ 1224000074); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploadDate() throws ParsingException { | ||||
|         assertTrue(extractor.getUploadDate().length() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetChannelUrl() throws ParsingException { | ||||
|         assertTrue(extractor.getChannelUrl().length() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetThumbnailUrl() throws ParsingException { | ||||
|         assertTrue(extractor.getThumbnailUrl(), | ||||
|                 extractor.getThumbnailUrl().contains(HTTPS)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploaderThumbnailUrl() throws ParsingException { | ||||
|         assertTrue(extractor.getUploaderThumbnailUrl(), | ||||
|                 extractor.getUploaderThumbnailUrl().contains(HTTPS)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetAudioStreams() throws ParsingException { | ||||
|         assertTrue(!extractor.getAudioStreams().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetVideoStreams() throws ParsingException { | ||||
|         for(VideoStream s : extractor.getVideoStreams()) { | ||||
|             assertTrue(s.url, | ||||
| @@ -109,10 +127,12 @@ public class YoutubeStreamExtractorDefaultTest extends AndroidTestCase { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testStreamType() throws ParsingException { | ||||
|         assertTrue(extractor.getStreamType() == AbstractStreamInfo.StreamType.VIDEO_STREAM); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetDashMpd() throws ParsingException { | ||||
|         assertTrue(extractor.getDashMpdUrl(), | ||||
|                 extractor.getDashMpdUrl() != null || !extractor.getDashMpdUrl().isEmpty()); | ||||
| @@ -1,14 +1,14 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import static junit.framework.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Created by Christian Schabesberger on 30.12.15. | ||||
|  * | ||||
| @@ -31,11 +31,15 @@ import java.io.IOException; | ||||
| 
 | ||||
| 
 | ||||
| // This class only works in Germany. | ||||
| public class YoutubeStreamExtractorGemaTest extends AndroidTestCase { | ||||
| /** | ||||
|  * Test for {@link YoutubeStreamExtractor} | ||||
|  */ | ||||
| public class YoutubeStreamExtractorGemaTest { | ||||
| 
 | ||||
|     // Deaktivate this Test Case bevore uploading it githup, otherwise CI will fail. | ||||
|     private static final boolean testActive = false; | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGemaError() throws IOException, ExtractionException { | ||||
|         if(testActive) { | ||||
|             try { | ||||
| @@ -1,32 +1,40 @@ | ||||
| package org.schabi.newpipe.extractor.youtube; | ||||
| 
 | ||||
| import android.test.AndroidTestCase; | ||||
| package org.schabi.newpipe.extractor.services.youtube.youtube; | ||||
| 
 | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.schabi.newpipe.Downloader; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.exceptions.ParsingException; | ||||
| import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler; | ||||
| import org.schabi.newpipe.extractor.stream_info.StreamExtractor; | ||||
| import org.schabi.newpipe.extractor.stream_info.VideoStream; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| public class YoutubeStreamExtractorRestrictedTest extends AndroidTestCase { | ||||
| import static junit.framework.Assert.assertTrue; | ||||
| 
 | ||||
| /** | ||||
|  * Test for {@link YoutubeStreamUrlIdHandler} | ||||
|  */ | ||||
| public class YoutubeStreamExtractorRestrictedTest { | ||||
|     public static final String HTTPS = "https://"; | ||||
|     private StreamExtractor extractor; | ||||
| 
 | ||||
|     @Before | ||||
|     public void setUp() throws Exception { | ||||
|         super.setUp(); | ||||
|         NewPipe.init(Downloader.getInstance()); | ||||
|         extractor = NewPipe.getService("Youtube") | ||||
|                 .getExtractorInstance("https://www.youtube.com/watch?v=i6JTvzrpBy0"); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetInvalidTimeStamp() throws ParsingException { | ||||
|         assertTrue(Integer.toString(extractor.getTimeStamp()), | ||||
|                 extractor.getTimeStamp() <= 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetValidTimeStamp() throws ExtractionException, IOException { | ||||
|         StreamExtractor extractor= NewPipe.getService("Youtube") | ||||
|                 .getExtractorInstance("https://youtu.be/FmG385_uUys?t=174"); | ||||
| @@ -34,49 +42,60 @@ public class YoutubeStreamExtractorRestrictedTest extends AndroidTestCase { | ||||
|                 extractor.getTimeStamp() == 174); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetAgeLimit() throws ParsingException { | ||||
|         assertTrue(extractor.getAgeLimit() == 18); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetTitle() throws ParsingException { | ||||
|         assertTrue(!extractor.getTitle().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetDescription() throws ParsingException { | ||||
|         assertTrue(extractor.getDescription() != null); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploader() throws ParsingException { | ||||
|         assertTrue(!extractor.getUploader().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetLength() throws ParsingException { | ||||
|         assertTrue(extractor.getLength() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetViews() throws ParsingException { | ||||
|         assertTrue(extractor.getLength() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploadDate() throws ParsingException { | ||||
|         assertTrue(extractor.getUploadDate().length() > 0); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetThumbnailUrl() throws ParsingException { | ||||
|         assertTrue(extractor.getThumbnailUrl(), | ||||
|                 extractor.getThumbnailUrl().contains(HTTPS)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUploaderThumbnailUrl() throws ParsingException { | ||||
|         assertTrue(extractor.getUploaderThumbnailUrl(), | ||||
|                 extractor.getUploaderThumbnailUrl().contains(HTTPS)); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetAudioStreams() throws ParsingException { | ||||
|         // audiostream not always necessary | ||||
|         //assertTrue(!extractor.getAudioStreams().isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetVideoStreams() throws ParsingException { | ||||
|         for(VideoStream s : extractor.getVideoStreams()) { | ||||
|             assertTrue(s.url, | ||||
							
								
								
									
										85
									
								
								assets/buddy_channel_item.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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> | ||||
| After Width: | Height: | Size: 2.7 KiB | 
| @@ -9,12 +9,12 @@ | ||||
|    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="426" | ||||
|    height="240" | ||||
|    width="454.39999" | ||||
|    height="256" | ||||
|    viewBox="0 0 426.00001 240" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    inkscape:version="0.92.0 r" | ||||
|    sodipodi:docname="dummi_thumbnail.svg"> | ||||
|   <defs | ||||
|      id="defs4" /> | ||||
| @@ -25,17 +25,17 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1.4" | ||||
|      inkscape:cx="375.9785" | ||||
|      inkscape:zoom="4.5554687" | ||||
|      inkscape:cx="305.73326" | ||||
|      inkscape:cy="69.804094" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      inkscape:window-width="1920" | ||||
|      inkscape:window-height="1016" | ||||
|      inkscape:window-height="1012" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-y="32" | ||||
|      inkscape:window-maximized="1" /> | ||||
|   <metadata | ||||
|      id="metadata7"> | ||||
| @@ -45,7 +45,7 @@ | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|         <dc:title /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   | ||||
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										83
									
								
								assets/dummi_thumbnail_backup.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,83 @@ | ||||
| <?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="426" | ||||
|    height="240" | ||||
|    viewBox="0 0 426.00001 240" | ||||
|    id="svg2" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="dummi_thumbnail.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.4" | ||||
|      inkscape:cx="375.9785" | ||||
|      inkscape:cy="69.804094" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      inkscape:window-width="1920" | ||||
|      inkscape:window-height="1016" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      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" /> | ||||
|     <path | ||||
|        sodipodi:type="star" | ||||
|        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" | ||||
|        id="path4164" | ||||
|        sodipodi:sides="3" | ||||
|        sodipodi:cx="41.58271" | ||||
|        sodipodi:cy="981.854" | ||||
|        sodipodi:r1="80.555222" | ||||
|        sodipodi:r2="40.877174" | ||||
|        sodipodi:arg1="-0.018810925" | ||||
|        sodipodi:arg2="0.93534698" | ||||
|        inkscape:flatsided="false" | ||||
|        inkscape:rounded="0" | ||||
|        inkscape:randomized="0" | ||||
|        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:transform-center-x="-18.464193" | ||||
|        transform="matrix(0.86438861,0.01393934,-0.01393934,0.86438861,190.62856,83.099197)" | ||||
|        inkscape:transform-center-y="-0.54163706" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										105
									
								
								assets/dummi_thumbnail_playlist.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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> | ||||
| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										72
									
								
								assets/dummi_thumbnail_playlist_background.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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> | ||||
| After Width: | Height: | Size: 2.2 KiB | 
 Weblate
					Weblate