1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-01-15 05:58:02 +00:00

Compare commits

..

41 Commits

Author SHA1 Message Date
Christian Schabesberger
d0d41c6b16 move on to v0.8.10 2017-02-19 16:09:39 +01:00
Christian Schabesberger
f7a531e71b Merge branch 'fix_nav' 2017-02-19 16:09:02 +01:00
Christian Schabesberger
c28fddc4dd did some finetuning 2017-02-19 16:07:45 +01:00
zmni
b6ea10fc73 Translated using Weblate (Indonesian)
Currently translated at 100.0% (149 of 149 strings)
2017-02-19 15:45:16 +01:00
Gian Maria Viglianti
320eb44061 Translated using Weblate (Italian)
Currently translated at 100.0% (149 of 149 strings)
2017-02-19 00:54:12 +01:00
Christian Schabesberger
7a6b5dd5b7 add initial support for NavStack 2017-02-18 21:59:48 +01:00
naofum
460653ed16 Translated using Weblate (Japanese)
Currently translated at 100.0% (149 of 149 strings)
2017-02-17 16:18:49 +01:00
Mladen Pejaković
b6fda788c5 Translated using Weblate (Serbian)
Currently translated at 100.0% (149 of 149 strings)
2017-02-16 22:12:17 +01:00
Matej U
da4b9306fa Translated using Weblate (Slovenian)
Currently translated at 100.0% (149 of 149 strings)
2017-02-16 22:00:25 +01:00
Weblate
d76c02cbf4 Merge remote-tracking branch 'origin/master' 2017-02-16 21:44:49 +01:00
nonamebg
4d5466b5cd Translated using Weblate (English)
Currently translated at 100.0% (141 of 141 strings)
2017-02-16 21:44:40 +01:00
Christian Schabesberger
e9c20ac8b0 add search channels to available features 2017-02-16 13:57:48 +01:00
Christian Schabesberger
961820a250 Merge branch 'search_channel' 2017-02-16 13:33:46 +01:00
Christian Schabesberger
4cc2976061 add unit test for new channel search 2017-02-16 00:46:15 +01:00
Christian Schabesberger
477f182b43 convert android tests to junit tests 2017-02-16 00:17:43 +01:00
Christian Schabesberger
a5252bb765 add search filter menu 2017-02-15 15:21:36 +01:00
Christian Schabesberger
3f0078f38a git channel item running 2017-02-15 12:59:36 +01:00
Christian Schabesberger
91434dd2ac setup core for search channel support 2017-02-13 00:55:11 +01:00
Christian Schabesberger
f29bd0a6e7 fix actionbar icon theming 2017-02-12 16:45:01 +01:00
Christian Schabesberger
7186f58374 remove StreamInfoItemSearchCollector 2017-02-12 14:22:39 +01:00
Christian Schabesberger
5c8bcf15ba make colector hirarchicall
remove broken commit()
2017-02-12 13:39:39 +01:00
Christian Schabesberger
130757ee99 Update CONTRIBUTING.md 2017-02-12 00:16:41 +01:00
Christian Schabesberger
5020a4f9dc add mailinglist to contributing 2017-02-11 22:26:00 +01:00
Christian Schabesberger
ef15902ec4 add InfoItem 2017-02-11 21:33:01 +01:00
zmni
ed8d13f837 Translated using Weblate (Indonesian)
Currently translated at 100.0% (141 of 141 strings)
2017-02-11 19:20:01 +01:00
aladar42
27f2c65e6d Translated using Weblate (Czech)
Currently translated at 100.0% (141 of 141 strings)
2017-02-10 18:44:38 +01:00
Weblate
0b3428ede9 Merge remote-tracking branch 'origin/master' 2017-02-08 17:13:21 +01:00
aladar42
3e0102ad2a Translated using Weblate (Czech)
Currently translated at 100.0% (141 of 141 strings)
2017-02-08 17:13:19 +01:00
Christian Schabesberger
c42002ccc5 removed download notice again 2017-02-06 00:04:20 +01:00
Christian Schabesberger
e8101d5d91 change download link 2017-02-05 23:56:26 +01:00
Christian Schabesberger
ecdf4ad502 add np download to readme 2017-02-05 21:09:36 +01:00
Matej U
af760f9cdc Translated using Weblate (Slovenian)
Currently translated at 100.0% (141 of 141 strings)
2017-02-05 20:16:33 +01:00
Weblate
b3491da49f Merge remote-tracking branch 'origin/master' 2017-02-05 12:46:26 +01:00
Snob Malkowitch
c322feeaff Translated using Weblate (Russian)
Currently translated at 94.3% (134 of 142 strings)
2017-02-05 12:46:18 +01:00
Christian Schabesberger
b26f46a063 Merge pull request #439 from marcobiscaro2112/master
Adding brazilian portuguese translation
2017-02-03 12:37:22 +01:00
Christian Schabesberger
9f0944dc5a Merge pull request #441 from k3b/master
fix NullPointerExcpeption when opening "Downloads" with android-api < 23
2017-02-03 12:35:52 +01:00
k3b
e869098434 fix NullPointerExcpeption when opening "Downloads" with android-api < 23 2017-02-02 01:58:47 +01:00
Weblate
4baa23af5f Merge remote-tracking branch 'origin/master' 2017-02-01 20:58:21 +01:00
aladar42
78fc5bbbd5 Translated using Weblate (Czech)
Currently translated at 97.1% (138 of 142 strings)
2017-02-01 20:58:20 +01:00
Wiredframe
a69784d168 Translated using Weblate (German)
Currently translated at 100.0% (142 of 142 strings)
2017-02-01 20:58:11 +01:00
Marco Biscaro
d48a7f1a18 Adding brazilian portuguese translation
(Also set settings_keys as untranslatable)
2017-02-01 12:32:06 -02:00
113 changed files with 2713 additions and 598 deletions

View File

@@ -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!

View File

@@ -37,12 +37,12 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Listen to YouTube videos (experimental)
* Select the streaming player to watch the video with
* Download videos
* Download audio only
* Open a video in Kodi
* Download audio only * Open a video in Kodi
* Show Next/Related videos
* Search YouTube in a specific language
* Watch age restricted material
* Display general information about channels
* Search channels
### Coming Features
@@ -50,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

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 25
versionCode 23
versionName "0.8.9"
versionCode 24
versionName "0.8.10"
}
buildTypes {
release {
@@ -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'
}

View File

@@ -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);
}
}

View File

@@ -156,6 +156,7 @@
<activity
android:name=".ChannelActivity"
android:label="@string/title_activity_channel"
android:launchMode="singleTask"
android:theme="@style/AppTheme.NoActionBar" />
<activity

View File

@@ -1,18 +1,24 @@
package org.schabi.newpipe;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
@@ -30,10 +36,14 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
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.
@@ -53,15 +63,9 @@ import java.util.Objects;
*/
public class ChannelActivity extends AppCompatActivity {
private static final String TAG = ChannelActivity.class.toString();
private View rootView = null;
// intent const
public static final String CHANNEL_URL = "channel_url";
public static final String SERVICE_ID = "service_id";
private int serviceId = -1;
private String channelUrl = "";
private int pageNumber = 0;
@@ -73,33 +77,43 @@ public class ChannelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//since we set themeing we have to set translucent statusBar by hand
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_NoActionBar);
}
super.onCreate(savedInstanceState);
setTranslucentStatusBar(getWindow());
setContentView(R.layout.activity_channel);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
rootView = findViewById(R.id.rootView);
setSupportActionBar(toolbar);
Intent i = getIntent();
channelUrl = i.getStringExtra(CHANNEL_URL);
serviceId = i.getIntExtra(SERVICE_ID, -1);
if(savedInstanceState == null) {
Intent i = getIntent();
channelUrl = i.getStringExtra(NavStack.URL);
serviceId = i.getIntExtra(NavStack.SERVICE_ID, -1);
} else {
channelUrl = savedInstanceState.getString(NavStack.URL);
serviceId = savedInstanceState.getInt(NavStack.SERVICE_ID);
NavStack.getInstance()
.restoreSavedInstanceState(savedInstanceState);
}
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) {
Intent detailIntent = new Intent(ChannelActivity.this, VideoItemDetailActivity.class);
detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, url);
detailIntent.putExtra(
VideoItemDetailFragment.STREAMING_SERVICE, serviceId);
startActivity(detailIntent);
public void selected(String url, int serviceId) {
NavStack.getInstance()
.openDetailActivity(ChannelActivity.this, url, serviceId);
}
});
@@ -129,7 +143,14 @@ public class ChannelActivity extends AppCompatActivity {
requestData(false);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(NavStack.URL, channelUrl);
outState.putInt(NavStack.SERVICE_ID, serviceId);
NavStack.getInstance()
.onSaveInstanceState(outState);
}
private void updateUi(final ChannelInfo info) {
CollapsingToolbarLayout ctl = (CollapsingToolbarLayout) findViewById(R.id.channel_toolbar_layout);
@@ -172,7 +193,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 +265,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 +297,38 @@ 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);
}
@Override
public void onBackPressed() {
try {
NavStack.getInstance()
.navBack(this);
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}

View File

@@ -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;
}
@@ -82,4 +86,9 @@ public class MainActivity extends ThemableActivity {
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
//ignore back
}
}

View File

@@ -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);
}
}
}

View File

@@ -1,5 +1,6 @@
package org.schabi.newpipe.detail;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
@@ -16,10 +17,14 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.ThemableActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavStack;
import java.util.Collection;
import java.util.HashSet;
import static android.os.Build.VERSION.SDK_INT;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
@@ -81,8 +86,10 @@ public class VideoItemDetailActivity extends ThemableActivity {
if (savedInstanceState == null) {
handleIntent(getIntent());
} else {
videoUrl = savedInstanceState.getString(VideoItemDetailFragment.VIDEO_URL);
currentStreamingService = savedInstanceState.getInt(VideoItemDetailFragment.STREAMING_SERVICE);
videoUrl = savedInstanceState.getString(NavStack.URL);
currentStreamingService = savedInstanceState.getInt(NavStack.SERVICE_ID);
NavStack.getInstance()
.restoreSavedInstanceState(savedInstanceState);
addFragment(savedInstanceState);
}
}
@@ -114,12 +121,12 @@ public class VideoItemDetailActivity extends ThemableActivity {
currentStreamingService = getServiceIdByUrl(videoUrl);
} else {
//this is if the video was called through another NewPipe activity
videoUrl = intent.getStringExtra(VideoItemDetailFragment.VIDEO_URL);
currentStreamingService = intent.getIntExtra(VideoItemDetailFragment.STREAMING_SERVICE, -1);
videoUrl = intent.getStringExtra(NavStack.URL);
currentStreamingService = intent.getIntExtra(NavStack.SERVICE_ID, -1);
}
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, autoplay);
arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
arguments.putString(NavStack.URL, videoUrl);
arguments.putInt(NavStack.SERVICE_ID, currentStreamingService);
addFragment(arguments);
}
@@ -142,9 +149,11 @@ public class VideoItemDetailActivity extends ThemableActivity {
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
outState.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
outState.putString(NavStack.URL, videoUrl);
outState.putInt(NavStack.SERVICE_ID, currentStreamingService);
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
NavStack.getInstance()
.onSaveInstanceState(outState);
}
@Override
@@ -159,15 +168,24 @@ public class VideoItemDetailActivity extends ThemableActivity {
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
NavStack.getInstance()
.openMainActivity(this);
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
try {
NavStack.getInstance()
.navBack(this);
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
/**
* Retrieves all Strings which look remotely like URLs from a text.
* Used if NewPipe was called through share menu.
@@ -224,7 +242,7 @@ public class VideoItemDetailActivity extends ThemableActivity {
StreamingService[] serviceList = NewPipe.getServices();
int service = -1;
for (int i = 0; i < serviceList.length; i++) {
if (serviceList[i].getUrlIdHandlerInstance().acceptUrl(videoUrl)) {
if (serviceList[i].getStreamUrlIdHandlerInstance().acceptUrl(videoUrl)) {
service = i;
//videoExtractor = ServiceList.getService(i).getExtractorInstance();
break;

View File

@@ -40,17 +40,16 @@ import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.ChannelActivity;
import org.schabi.newpipe.ImageErrorLoadingListener;
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.VideoStream;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.BackgroundPlayer;
@@ -58,6 +57,8 @@ import org.schabi.newpipe.player.ExoPlayerActivity;
import org.schabi.newpipe.player.PlayVideoActivity;
import org.schabi.newpipe.report.ErrorActivity;
import java.util.Vector;
import org.schabi.newpipe.util.NavStack;
import org.schabi.newpipe.util.PermissionHelper;
import static android.app.Activity.RESULT_OK;
@@ -91,8 +92,6 @@ public class VideoItemDetailFragment extends Fragment {
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String VIDEO_URL = "video_url";
public static final String STREAMING_SERVICE = "streaming_service";
public static final String AUTO_PLAY = "auto_play";
private AppCompatActivity activity;
@@ -288,10 +287,8 @@ public class VideoItemDetailFragment extends Fragment {
channelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(activity, ChannelActivity.class);
i.putExtra(ChannelActivity.CHANNEL_URL, info.channel_url);
i.putExtra(ChannelActivity.SERVICE_ID, info.service_id);
startActivity(i);
NavStack.getInstance()
.openChannelActivity(getActivity(), info.channel_url, info.service_id);
}
});
} else {
@@ -536,13 +533,15 @@ 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) {
openStreamUrl(url);
public void selected(String url, int serviceId) {
NavStack.getInstance()
.openDetailActivity(getContext(), url, serviceId);
}
});
}
@@ -677,8 +676,8 @@ public class VideoItemDetailFragment extends Fragment {
// then we must not try to access objects of this fragment.
// Otherwise the applications would crash.
if(backgroundButton != null) {
streamingServiceId = getArguments().getInt(STREAMING_SERVICE);
String videoUrl = getArguments().getString(VIDEO_URL);
streamingServiceId = getArguments().getInt(NavStack.SERVICE_ID);
String videoUrl = getArguments().getString(NavStack.URL);
StreamInfoWorker siw = StreamInfoWorker.getInstance();
siw.search(streamingServiceId, videoUrl, getActivity());
@@ -811,21 +810,13 @@ public class VideoItemDetailFragment extends Fragment {
stringResource, Toast.LENGTH_LONG).show();
}
private void openStreamUrl(String url) {
Intent detailIntent = new Intent(activity, VideoItemDetailActivity.class);
detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, url);
detailIntent.putExtra(
VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
activity.startActivity(detailIntent);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RECAPTCHA_REQUEST:
if (resultCode == RESULT_OK) {
String videoUrl = getArguments().getString(VIDEO_URL);
String videoUrl = getArguments().getString(NavStack.URL);
StreamInfoWorker siw = StreamInfoWorker.getInstance();
siw.search(streamingServiceId, videoUrl, getActivity());
} else {

View File

@@ -242,9 +242,7 @@ public class DownloadActivity extends ThemableActivity implements AdapterView.On
switch (id) {
case android.R.id.home: {
Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
onBackPressed();
return true;
}
case R.id.action_settings: {
@@ -252,14 +250,6 @@ public class DownloadActivity extends ThemableActivity implements AdapterView.On
startActivity(intent);
return true;
}
case R.id.action_report_error: {
ErrorActivity.reportError(DownloadActivity.this, new Vector<Throwable>(),
null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT,
null,
"user_report", R.string.user_report));
return true;
}
default:
return super.onOptionsItemSelected(item);
}

View File

@@ -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,

View 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();
}

View File

@@ -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;
}
}

View File

@@ -34,25 +34,22 @@ public class NewPipe {
private static final String TAG = NewPipe.class.toString();
private static final StreamingService[] serviceList = {
new YoutubeService(0)
};
private static Downloader downloader = null;
public static StreamingService[] getServices() {
return serviceList;
return ServiceList.serviceList;
}
public static StreamingService getService(int serviceId)throws ExtractionException {
for(StreamingService s : serviceList) {
for(StreamingService s : ServiceList.serviceList) {
if(s.getServiceId() == serviceId) {
return s;
}
}
throw new ExtractionException("Service not known: " + Integer.toString(serviceId));
return null;
}
public static StreamingService getService(String serviceName) throws ExtractionException {
return serviceList[getIdOfService(serviceName)];
return ServiceList.serviceList[getIdOfService(serviceName)];
}
public static String getNameOfService(int id) {
try {
@@ -63,13 +60,13 @@ public class NewPipe {
return "";
}
}
public static int getIdOfService(String serviceName) throws ExtractionException {
for(int i = 0; i < serviceList.length; i++) {
if(serviceList[i].getServiceInfo().name.equals(serviceName)) {
public static int getIdOfService(String serviceName) {
for(int i = 0; i < ServiceList.serviceList.length; i++) {
if(ServiceList.serviceList[i].getServiceInfo().name.equals(serviceName)) {
return i;
}
}
throw new ExtractionException("Error: Service " + serviceName + " not known.");
return -1;
}
public static void init(Downloader d) {
@@ -79,4 +76,13 @@ public class NewPipe {
public static Downloader getDownloader() {
return downloader;
}
public static StreamingService getServiceByUrl(String url) {
for(StreamingService s : ServiceList.serviceList) {
if(s.getLinkTypeByUrl(url) != StreamingService.LinkType.NONE) {
return s;
}
}
return null;
}
}

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.services.youtube.YoutubeService;
/**
* Created by the-scrabi on 18.02.17.
*/
class ServiceList {
public static final StreamingService[] serviceList = {
new YoutubeService(0)
};
}

View File

@@ -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;
@@ -33,6 +32,13 @@ public abstract class StreamingService {
public String name = "";
}
public enum LinkType {
NONE,
STREAM,
CHANNEL,
PLAYLIST
}
private int serviceId;
public StreamingService(int id) {
@@ -44,7 +50,7 @@ public abstract class StreamingService {
public abstract StreamExtractor getExtractorInstance(String url)
throws IOException, ExtractionException;
public abstract SearchEngine getSearchEngineInstance();
public abstract UrlIdHandler getUrlIdHandlerInstance();
public abstract UrlIdHandler getStreamUrlIdHandlerInstance();
public abstract UrlIdHandler getChannelUrlIdHandlerInstance();
public abstract ChannelExtractor getChannelExtractorInstance(String url, int page)
throws ExtractionException, IOException;
@@ -53,4 +59,20 @@ public abstract class StreamingService {
public final int getServiceId() {
return serviceId;
}
/**
* figure out where the link is pointing to (a channel, video, playlist, etc.)
*/
public final LinkType getLinkTypeByUrl(String url) {
UrlIdHandler sH = getStreamUrlIdHandlerInstance();
UrlIdHandler cH = getChannelUrlIdHandlerInstance();
if(sH.acceptUrl(url)) {
return LinkType.STREAM;
} else if(cH.acceptUrl(url)) {
return LinkType.CHANNEL;
} else {
return LinkType.NONE;
}
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.extractor.search;
package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;

View File

@@ -23,6 +23,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
*/
public interface UrlIdHandler {
String getUrl(String videoId);
String getId(String siteUrl) throws ParsingException;
String cleanUrl(String siteUrl) throws ParsingException;

View File

@@ -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;

View File

@@ -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<>();

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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()) {
throw new ExtractionException("Empty result despite no error");
if(result.errors.isEmpty()) {
throw new ExtractionException("Empty result despite no error");
}
} else {
// This is used as a fallback. Do not relay on it !!!
throw new SearchEngine.NothingFoundException(result.suggestion);
@@ -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<>();
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
@@ -56,11 +56,11 @@ public class YoutubeService extends StreamingService {
}
@Override
public SearchEngine getSearchEngineInstance() {
return new YoutubeSearchEngine(getUrlIdHandlerInstance(), getServiceId());
return new YoutubeSearchEngine(getStreamUrlIdHandlerInstance(), getServiceId());
}
@Override
public UrlIdHandler getUrlIdHandlerInstance() {
public UrlIdHandler getStreamUrlIdHandlerInstance() {
return YoutubeStreamUrlIdHandler.getInstance();
}

View File

@@ -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;

View File

@@ -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)
.text();
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

View File

@@ -150,7 +150,17 @@ public class YoutubeStreamUrlIdHandler implements UrlIdHandler {
@Override
public boolean acceptUrl(String videoUrl) {
videoUrl = videoUrl.toLowerCase();
return videoUrl.contains("youtube") ||
videoUrl.contains("youtu.be");
if(videoUrl.contains("youtube") ||
videoUrl.contains("youtu.be")) {
// bad programming I know
try {
getId(videoUrl);
return true;
} catch (Exception e) {
return false;
}
} else {
return false;
}
}
}

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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())
.getStreamUrlIdHandlerInstance()
.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);
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -27,6 +27,7 @@ import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.util.NavStack;
import java.io.IOException;
import java.util.Arrays;
@@ -355,8 +356,8 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
//build intent to return to video, on tapping notification
Intent openDetailViewIntent = new Intent(getApplicationContext(),
VideoItemDetailActivity.class);
openDetailViewIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, serviceId);
openDetailViewIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, webUrl);
openDetailViewIntent.putExtra(NavStack.SERVICE_ID, serviceId);
openDetailViewIntent.putExtra(NavStack.URL, webUrl);
openDetailViewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent openDetailView = PendingIntent.getActivity(owner, noteID,
openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT);

View File

@@ -16,6 +16,7 @@ import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -161,6 +162,10 @@ public class ErrorActivity extends ThemableActivity {
private TextView infoView;
private TextView errorMessageView;
public static void reportUiError(final AppCompatActivity activity, final Throwable el) {
reportError(activity, el, activity.getClass(), null, ErrorInfo.make(UI_ERROR, "none", "", R.string.app_ui_crash));
}
public static void reportError(final Context context, final List<Throwable> el,
final Class returnAcitivty, View rootView, final ErrorInfo errorInfo) {
@@ -297,6 +302,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

View File

@@ -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;
@@ -28,6 +30,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.util.NavStack;
import java.util.EnumSet;
import static android.app.Activity.RESULT_OK;
import static org.schabi.newpipe.ReCaptchaActivity.RECAPTCHA_REQUEST;
@@ -56,6 +61,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 +174,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,10 +221,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) {
startDetailActivity(url);
public void selected(String url, int serviceId) {
NavStack.getInstance()
.openDetailActivity(getContext(), url, serviceId);
}
});
infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() {
@Override
public void selected(String url, int serviceId) {
NavStack.getInstance()
.openChannelActivity(getContext(), url, serviceId);
}
});
recyclerView.setAdapter(infoListAdapter);
@@ -243,13 +260,6 @@ public class SearchInfoItemFragment extends Fragment {
return view;
}
private void startDetailActivity(String url) {
Intent i = new Intent(getActivity(), VideoItemDetailActivity.class);
i.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
i.putExtra(VideoItemDetailFragment.VIDEO_URL, url);
getActivity().startActivity(i);
}
@Override
public void onStart() {
super.onStart();
@@ -275,6 +285,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 +334,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() {

View File

@@ -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);
ErrorActivity.reportError(h, a, result.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.light_parsing_error));
if(result.resultList.isEmpty()&& !result.errors.isEmpty()) {
// if it compleatly failes dont show snackbar, instead show error directlry
ErrorActivity.reportError(h, a, result.errors, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.parsing_error));
} else {
// if it partly show snackbar
ErrorActivity.reportError(h, a, result.errors, null, rootView,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
serviceName, query, R.string.light_parsing_error));
}
}
// hard errors:
} catch (ReCaptchaException e) {
@@ -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();
}

View File

@@ -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;

View File

@@ -0,0 +1,151 @@
package org.schabi.newpipe.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.schabi.newpipe.ChannelActivity;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.detail.VideoItemDetailActivity;
import org.schabi.newpipe.detail.VideoItemDetailFragment;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import java.util.ArrayList;
import java.util.Stack;
/**
* Created by Christian Schabesberger on 16.02.17.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* NavStack.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/>.
*/
/**
* class helps to navigate within the app
* IMPORTAND: the top of the stack is the current activity !!!
*/
public class NavStack {
private static final String TAG = NavStack.class.toString();
public static final String SERVICE_ID = "service_id";
public static final String URL = "url";
private static final String NAV_STACK="nav_stack";
private enum ActivityId {
CHANNEL,
DETAIL
}
private class NavEntry {
public NavEntry(String url, int serviceId) {
this.url = url;
this.serviceId = serviceId;
}
public String url;
public int serviceId;
}
private static NavStack instance = new NavStack();
private Stack<NavEntry> stack = new Stack<NavEntry>();
private NavStack() {
}
public static NavStack getInstance() {
return instance;
}
private void addEntry(String url, Class ac, int serviceId) {
stack.push(new NavEntry(url, serviceId));
}
public void navBack(Activity activity) throws Exception {
if(stack.size() == 0) { // if stack is already empty here, activity was probably called
// from another app
activity.finish();
return;
}
stack.pop(); // remove curent activty, since we dont want to return to itself
if (stack.size() == 0) {
openMainActivity(activity); // if no more page is on the stack this means we are home
return;
}
NavEntry entry = stack.pop(); // this element will reapear, since by calling the old page
// this element will be pushed on top again
try {
StreamingService service = NewPipe.getService(entry.serviceId);
switch (service.getLinkTypeByUrl(entry.url)) {
case STREAM:
openDetailActivity(activity, entry.url, entry.serviceId);
break;
case CHANNEL:
openChannelActivity(activity, entry.url, entry.serviceId);
break;
case NONE:
throw new Exception("Url not known to service. service="
+ Integer.toString(entry.serviceId) + " url=" + entry.url);
default:
openMainActivity(activity);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void openChannelActivity(Context context, String url, int serviceId) {
openActivity(context, url, serviceId, ChannelActivity.class);
}
public void openDetailActivity(Context context, String url, int serviceId) {
openActivity(context, url, serviceId, VideoItemDetailActivity.class);
}
private void openActivity(Context context, String url, int serviceId, Class acitivtyClass) {
stack.push(new NavEntry(url, serviceId));
Intent i = new Intent(context, acitivtyClass);
i.putExtra(SERVICE_ID, serviceId);
i.putExtra(URL, url);
context.startActivity(i);
}
public void openMainActivity(Activity a) {
stack.clear();
Intent i = new Intent(a, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(a, i);
}
public void onSaveInstanceState(Bundle state) {
ArrayList<String> sa = new ArrayList<>();
for(NavEntry entry : stack) {
sa.add(entry.url);
}
state.putStringArrayList(NAV_STACK, sa);
}
public void restoreSavedInstanceState(Bundle state) {
ArrayList<String> sa = state.getStringArrayList(NAV_STACK);
for(String url : sa) {
stack.push(new NavEntry(url, NewPipe.getServiceByUrl(url).getServiceId()));
}
}
}

View File

@@ -84,9 +84,23 @@ public abstract class MissionsFragment extends Fragment
return v;
}
@Override
/** Added in API level 23. */
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
// Bug: in api< 23 this is never called
// so mActivity=null
// so app crashes with nullpointer exception
mActivity = activity;
}
/** deprecated in API level 23,
* but must remain to allow compatibility with api<23 */
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 446 B

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 888 B

After

Width:  |  Height:  |  Size: 888 B

View 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>

View File

@@ -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"

View File

@@ -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

View 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>

View File

@@ -2,12 +2,7 @@
<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_report_error"
app:showAsAction="never"
android:title="@string/report_error" />
</menu>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="upload_date_text">Publikováno %1$s</string>
<string name="no_player_found">Žádný přehrávač nenalezen. Nainstalovat VLC?</string>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><string name="upload_date_text">Publikováno %1$s</string>
<string name="no_player_found">Nenalezen žádný přehrávač. Nainstalovat VLC?</string>
<string name="install">Instalovat</string>
<string name="cancel">Zrušit</string>
<string name="open_in_browser">Otevřít v prohlížeči</string>
@@ -18,45 +18,45 @@
<string name="use_external_video_player_title">Použít externí video přehrávač</string>
<string name="use_external_audio_player_title">Použít externí audio přehrávač</string>
<string name="download_path_audio_summary">Cesta, kde se uloží audio po stažení.</string>
<string name="download_path_audio_dialog_title">Zadejte umístění pro uložení audio souborů.</string>
<string name="download_path_audio_summary">Cesta, kam se uloží stažené audio</string>
<string name="download_path_audio_dialog_title">Zadejte umístění pro stažené audio soubory.</string>
<string name="download_path_audio_title">Umístění pro stažené audio</string>
<string name="autoplay_through_intent_title">Automatické přehrávání skrze Intent</string>
<string name="autoplay_through_intent_summary">Automaticky přehrávat video, jestliže je volané z jiné aplikace.</string>
<string name="default_resolution_title">Výchozí rozlišení</string>
<string name="play_with_kodi_title">Přehrát s Kodi</string>
<string name="play_with_kodi_title">Přehrát pomocí Kodi</string>
<string name="kore_not_found">Aplikace Kore nenalezena. Nainstalovat Kore?</string>
<string name="view_count_text">%1$s zhlédnutí</string>
<string name="background_player_name">NewPipe Přehrávač na pozadí</string>
<string name="download_path_title">Umístění pro stažené video</string>
<string name="download_path_summary">Cesta, kde se uloží video po stažení.</string>
<string name="download_path_dialog_title">Zadejte umístění pro uložená videa</string>
<string name="download_path_summary">Cesta, kam se uloží stažené video.</string>
<string name="download_path_dialog_title">Zadejte umístění pro stažená videa</string>
<string name="show_play_with_kodi_title">Zobrazit možnost \"Přehrát s Kodi\"</string>
<string name="show_play_with_kodi_summary">Zobrazit možnost přehrát video s multimediálním centrem Kodi.</string>
<string name="show_play_with_kodi_title">Zobrazit možnost \"Přehrát pomocí Kodi\"</string>
<string name="show_play_with_kodi_summary">Zobrazit možnost přehrá videa pomocí multimediálního centra Kodi.</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Výchozí audio formát</string>
<string name="webm_description">WebM — svobodný formát</string>
<string name="m4a_description">m4a — lepší kvalita</string>
<string name="theme_title">Téma</string>
<string name="dark_theme_title">Tmavý</string>
<string name="light_theme_title">Světlý</string>
<string name="dark_theme_title">Tmavé</string>
<string name="light_theme_title">Světlé</string>
<string name="download_dialog_title">Stažení</string>
<string name="download_dialog_title">Stáhnout</string>
<string name="next_video_title">Následující video</string>
<string name="show_next_and_similar_title">Zobrazit následující a související videa</string>
<string name="show_next_and_similar_title">Zobrazit následující a podobná videa</string>
<string name="url_not_supported_toast">URL není podporováno</string>
<string name="similar_videos_btn_text">Související videa</string>
<string name="similar_videos_btn_text">Podobná videa</string>
<string name="search_language_title">Preferovaný jazyk obsahu</string>
<string name="settings_category_video_audio_title">Video &amp; audio</string>
<string name="settings_category_video_audio_title">Video a audio</string>
<string name="settings_category_appearance_title">Vzhled</string>
<string name="settings_category_other_title">Ostatní</string>
<string name="background_player_playing_toast">Přehrávám na pozadí</string>
<string name="play_btn_text">Přehrát</string>
<string name="general_error">Chyba</string>
<string name="network_error">Chyba sítě</string>
<string name="could_not_load_thumbnails">Nebylo možné stáhnout všechny náhledy</string>
<string name="could_not_load_thumbnails">Nebylo možné nahrát všechny náhledy</string>
<string name="youtube_signature_decryption_error">Nebylo možné dekódovat URL videa.</string>
<string name="parsing_error">Nebylo možné analyzovat webovou stránku.</string>
<string name="content_not_available">Obsah není k dispozici.</string>
@@ -64,11 +64,11 @@
<string name="list_thumbnail_view_description">Náhled videa</string>
<string name="detail_thumbnail_view_description">Náhled videa</string>
<string name="detail_uploader_thumbnail_view_description">Náhled uploadera</string>
<string name="detail_uploader_thumbnail_view_description">Náhled obrázku uploadera</string>
<string name="detail_likes_img_view_description">To se mi líbí</string>
<string name="detail_dislikes_img_view_description">To se mi nelíbí</string>
<string name="use_tor_title">Použít Tor</string>
<string name="use_tor_summary">Vynutit stahování skrz Tor pro zvýšené soukromí (streaming není zatím podporován)</string>
<string name="use_tor_summary">(Experimentální) Vynutit stahování skrz Tor pro zvýšené soukromí (streaming zatím není podporován).</string>
<string name="err_dir_create">Nebylo možné vytvořit složku pro stažené soubory \'%1$s\'</string>
<string name="info_dir_created">Vytvořena složka pro stažené soubory \'%1$s\'</string>
@@ -76,8 +76,229 @@
<string name="autoplay_by_calling_app_summary">Automaticky přehrát video, když je NewPipe otevřen z jiné aplikace.</string>
<string name="content">Obsah</string>
<string name="show_age_restricted_content_title">Zobrazovat věkově omezený obsah</string>
<string name="video_is_age_restricted">Toto video je věkově omezeno. Povolte věkově omezená video v nastavení.</string>
<string name="video_is_age_restricted">Toto video je věkově omezeno. Povolte věkově omezená videa v nastavení.</string>
<string name="duration_live">živě</string>
<string name="light_parsing_error">Nemůžu kompletně parsovat web.</string>
<string name="light_parsing_error">Nebylo možné kompletně analyzovat stránku.</string>
<string name="main_bg_subtitle">Pro začátek stiskni hledat</string>
<string name="msg_copied">Zkopírováno do schránky.</string>
<string name="msg_wait">Počkejte prosím…</string>
<string name="msg_running">NewPipe se stahuje</string>
<string name="msg_running_detail">Stiskněte pro detaily</string>
<string name="msg_server_unsupported">Server není podporován</string>
<string name="msg_exists">Soubor již existuje</string>
<string name="msg_url_malform">Špatné URL nebo připojení k Internetu není k disposici</string>
<string name="msg_error">Chyba</string>
<string name="msg_url">Stáhnout URL</string>
<string name="msg_name">Jméno souboru</string>
<string name="msg_threads">Vlákna</string>
<string name="pause">Zastavit</string>
<string name="delete">Smazat</string>
<string name="use_exoplayer_title">Použít ExoPlayer</string>
<string name="use_exoplayer_summary">Experimentální</string>
<string name="start">Začít</string>
<string name="error_drm_unknown">Neznámá chyba DRM</string>
<string name="retry">Zkusit znovu</string>
<string name="text">Text</string>
<string name="enable_background_audio">Hrát na pozadí</string>
<string name="video">Video</string>
<string name="audio">Audio</string>
<string name="report_error">Nahlásit chybu</string>
<string name="info_searched_lbl">Hledáno:</string>
<string name="error_details_headline">Detaily:</string>
<string name="info_requested_stream_lbl">Vyžádaný stream:</string>
<string name="what_happened_headline">Co se stalo:</string>
<string name="error_snackbar_action">NAHLÁSIT</string>
<string name="sorry_string">Omlouváme se, tohle se nemělo stát.</string>
<string name="error_report_button_text">Nahlásit chybu přes e-mail</string>
<string name="error_snackbar_message">Omlouváme se, staly se nějaké chyby.</string>
<string name="could_not_load_image">Nepodařilo se nahrát obrázek</string>
<string name="app_ui_crash">Aplikace/UI spadlo</string>
<string name="live_streams_not_supported">Tento stream je vysílán živě, funkce ještě není podporována.</string>
<string name="could_not_get_stream">Nepodařilo se dostat žádný stream.</string>
<string name="could_not_setup_download_menu">Nepodařilo se nastavit menu stahování.</string>
<string name="settings_title">Nastavení</string>
<string name="error_report_title">Nahlásit chybu</string>
<string name="downloads">Stažené soubory</string>
<string name="downloads_title">Stažené soubory</string>
<string name="what_device_headline">Info:</string>
<string name="your_comment">Vaše poznámky (Anglicky):</string>
<string name="logging">Logování</string>
<string name="logging_normal">Normální</string>
<string name="error_drm_not_supported">Chráněný obsah není podporován na API úrovni menší než 18</string>
<string name="error_drm_unsupported_scheme">Toto zařízení nepodporuje potřebné DRM schéma</string>
<string name="error_no_decoder">Toto zařízení nedodává dekodér pro <xliff:g id="mime_type">
%1$s</xliff:g></string>
<string name="error_no_secure_decoder">Toto zařízení nedodává bezpečný dekodér pro
<xliff:g id="mime_type">
%1$s</xliff:g></string>
<string name="storage_permission_denied">Oprávnění přístupu do Úložiště bylo zamítnuto</string>
<string name="view">Shlédnout</string>
<string name="add">Nová mise</string>
<string name="finish">Hotovo</string>
<string name="switch_mode">Přepnout mezi listem a mřížkou</string>
<string name="msg_fetch_filename">Získej jméno souboru</string>
<string name="action_settings">Nastavení</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Výzva reCAPTCHA</string>
<string name="recaptcha_request_toast">Požadována výzva reCAPTCHA</string>
<string name="black_theme_title">Černé</string>
<string name="off">[vypnuto]</string>
<string name="checksum">Checksum</string>
<string name="no_available_dir">Prosím vyberte dostupnou složku pro stažení souborů.</string>
<string name="title_activity_channel">Aktivita kanálu</string>
<string name="user_report">Hlášení uživatele</string>
<string name="large_text">"
Material is the metaphor.
A material metaphor is the unifying theory of a rationalized space and a system of motion.
The material is grounded in tactile reality, inspired by the study of paper and ink, yet
technologically advanced and open to imagination and magic.
Surfaces and edges of the material provide visual cues that are grounded in reality. The
use of familiar tactile attributes helps users quickly understand affordances. Yet the
flexibility of the material creates new affordances that supercede those in the physical
world, without breaking the rules of physics.
The fundamentals of light, surface, and movement are key to conveying how objects move,
interact, and exist in space and in relation to each other. Realistic lighting shows
seams, divides space, and indicates moving parts.
Bold, graphic, intentional.
The foundational elements of print based design typography, grids, space, scale, color,
and use of imagery guide visual treatments. These elements do far more than please the
eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge
imagery, large scale typography, and intentional white space create a bold and graphic
interface that immerse the user in the experience.
An emphasis on user actions makes core functionality immediately apparent and provides
waypoints for the user.
Motion provides meaning.
Motion respects and reinforces the user as the prime mover. Primary user actions are
inflection points that initiate motion, transforming the whole design.
All action takes place in a single environment. Objects are presented to the user without
breaking the continuity of experience even as they transform and reorganize.
Motion is meaningful and appropriate, serving to focus attention and maintain continuity.
Feedback is subtle yet clear. Transitions are efficient yet coherent.
3D world.
The material environment is a 3D space, which means all objects have x, y, and z
dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the
positive z-axis extending towards the viewer. Every sheet of material occupies a single
position along the z-axis and has a standard 1dp thickness.
On the web, the z-axis is used for layering and not for perspective. The 3D world is
emulated by manipulating the y-axis.
Light and shadow.
Within the material environment, virtual lights illuminate the scene. Key lights create
directional shadows, while ambient light creates soft shadows from all angles.
Shadows in the material environment are cast by these two light sources. In Android
development, shadows occur when light sources are blocked by sheets of material at
various positions along the z-axis. On the web, shadows are depicted by manipulating the
y-axis only. The following example shows the card with a height of 6dp.
Resting elevation.
All material objects, regardless of size, have a resting elevation, or default elevation
that does not change. If an object changes elevation, it should return to its resting
elevation as soon as possible.
Component elevations.
The resting elevation for a component type is consistent across apps (e.g., FAB elevation
does not vary from 6dp in one app to 16dp in another app).
Components may have different resting elevations across platforms, depending on the depth
of the environment (e.g., TV has a greater depth than mobile or desktop).
Responsive elevation and dynamic elevation offsets.
Some component types have responsive elevation, meaning they change elevation in response
to user input (e.g., normal, focused, and pressed) or system events. These elevation
changes are consistently implemented using dynamic elevation offsets.
Dynamic elevation offsets are the goal elevation that a component moves towards, relative
to the components resting state. They ensure that elevation changes are consistent
across actions and component types. For example, all components that lift on press have
the same elevation change relative to their resting elevation.
Once the input event is completed or cancelled, the component will return to its resting
elevation.
Avoiding elevation interference.
Components with responsive elevations may encounter other components as they move between
their resting elevations and dynamic elevation offsets. Because material cannot pass
through other material, components avoid interfering with one another any number of ways,
whether on a per component basis or using the entire app layout.
On a component level, components can move or be removed before they cause interference.
For example, a floating action button (FAB) can disappear or move off screen before a
user picks up a card, or it can move if a snackbar appears.
On the layout level, design your app layout to minimize opportunities for interference.
For example, position the FAB to one side of stream of a cards so the FAB wont interfere
when a user tries to pick up one of cards.
"</string>
<string name="logging_verbose">Podrobné</string>
<string name="error_querying_decoders">Nelze zjistit dekodéry zařízení</string>
<string name="error_instantiating_decoder">Nelze doložit dekodér <xliff:g id="decoder_name">
%1$s</xliff:g></string>
<string name="info_labels">Co:\\nŽádost:\\nJazyk obsahu:\\nSlužba:\\nČas GMT:\\nBalíček:\\nVerze:\\nVerze OS:\\nGlobální rozsah IP:</string>
</resources>

View File

@@ -138,6 +138,12 @@
<string name="switch_mode">Zwischen Liste und Gitter umschalten</string>
<string name="videos">Videos</string>
<string name="subscriber">Abonenten</string>
<string name="views">Aufrufe</string>
<string name="short_thousand">Tsd.</string>
<string name="short_million">Mio.</string>
<string name="short_billion">Mrd.</string>
<string name="msg_url">Download-URL</string>
<string name="msg_name">Dateiname</string>
@@ -301,4 +307,9 @@
"</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="black_theme_title">Schwarz</string>
<string name="reCaptcha_title">reCAPTCHA Herausforderung</string>
<string name="recaptcha_request_toast">reCAPTCHA Herausforderung angefordert</string>
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><string name="main_bg_subtitle">Klik pencarian untuk memulai</string>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><string name="main_bg_subtitle">Tekan pencarian untuk memulai</string>
<string name="view_count_text">%1$s tayang</string>
<string name="upload_date_text">Diterbitkan pada %1$s</string>
<string name="install">Pasang</string>
@@ -142,7 +142,7 @@
<string name="action_settings">Pengaturan</string>
<string name="background_player_name">Pemutar Latar Belakang NewPipe</string>
<string name="no_player_found">Tidak ditemukan pemutar stream. Pasang VLC?</string>
<string name="no_player_found">Tidak ditemukan pemutar stream. Apakah anda ingin memasang VLC?</string>
<string name="youtube_signature_decryption_error">Tidak bisa mendekrip tanda tangan url video.</string>
<string name="app_ui_crash">App/UI rusak</string>
<string name="could_not_get_stream">Tidak bisa mendapatkan stream apapun.</string>
@@ -293,4 +293,16 @@
<string name="recaptcha_request_toast">Meminta kode reCAPTCHA</string>
<string name="black_theme_title">Hitam</string>
<string name="all">Semua</string>
<string name="channel">Channel</string>
<string name="videos">video</string>
<string name="views">ditonton</string>
<string name="short_thousand">T</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="subscriber">subscriber</string>
</resources>

View File

@@ -154,7 +154,7 @@
<string name="msg_url_malform">URL errato od errore di rete</string>
<string name="msg_running">Download di NewPipe</string>
<string name="msg_running_detail">Fai tap per i dettagli</string>
<string name="msg_wait">Attendi...</string>
<string name="msg_wait">Attendi</string>
<string name="msg_copied">Copiato negli appunti.</string>
<string name="no_available_dir">Seleziona una cartella disponibile in cui salvare i download.</string>
@@ -301,7 +301,21 @@
"</string>
<string name="reCaptchaActivity">reCaptcha</string>
<string name="reCaptcha_title">Sfida reCaptcha</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Sfida reCAPTCHA</string>
<string name="black_theme_title">Nero</string>
<string name="all">Tutto</string>
<string name="channel">Canale</string>
<string name="videos">video</string>
<string name="subscriber">iscritto</string>
<string name="views">visualizzazioni</string>
<string name="short_thousand">K</string>
<string name="short_million">M</string>
<string name="short_billion">B</string>
<string name="recaptcha_request_toast">Richiesta sfida reCAPTCHA</string>
</resources>

View File

@@ -306,4 +306,14 @@
<string name="black_theme_title">ブラック</string>
<string name="all">すべて</string>
<string name="channel">チャンネル</string>
<string name="videos">ビデオ</string>
<string name="subscriber">購読者</string>
<string name="views">表示</string>
<string name="short_thousand"></string>
<string name="short_million">百万</string>
<string name="short_billion">十億</string>
</resources>

View File

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

View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="action_settings">Configurações</string>
<string name="audio">Áudio</string>
<string name="youtube_signature_decryption_error">Não foi possível decriptar a assinatura de URL do vídeo.</string>
<string name="your_comment">Seu comentário (em inglês):</string>
<string name="what_happened_headline">O que aconteceu:</string>
<string name="what_device_headline">Informações:</string>
<string name="webm_description">WebM — formato aberto</string>
<string name="view_count_text">%1$s visualizações</string>
<string name="view">Ver</string>
<string name="video_is_age_restricted">Vídeo com restrição de idade. Ative os vídeos com restrição de idade nas configurações.</string>
<string name="video">Vídeo</string>
<string name="autoplay_by_calling_app_summary">Reproduz o vídeo automaticamente quando o NewPipe for aberto a partir de outro app.</string>
<string name="autoplay_by_calling_app_title">Reproduzir automaticamente</string>
<string name="black_theme_title">Preto</string>
<string name="blocked_by_gema">Bloqueado pelo GEMA.</string>
<string name="cancel">Cancelar</string>
<string name="checksum">Checksum</string>
<string name="choose_browser">Escolher navegador</string>
<string name="content">Conteúdo</string>
<string name="content_not_available">Conteúdo não disponível.</string>
<string name="could_not_get_stream">Não foi possível carregar nenhum stream.</string>
<string name="could_not_load_image">Não foi possível carregar a imagem.</string>
<string name="could_not_load_thumbnails">Não foi possível carregar os thumbnails.</string>
<string name="dark_theme_title">Escuro</string>
<string name="default_audio_format_title">Formato de áudio padrão</string>
<string name="default_resolution_title">Resolução padrão</string>
<string name="delete">Excluir</string>
<string name="detail_dislikes_img_view_description">Não curtidas</string>
<string name="detail_likes_img_view_description">Curtidas</string>
<string name="download">Baixar</string>
<string name="download_dialog_title">Baixar</string>
<string name="error_details_headline">Detalhes:</string>
<string name="err_dir_create">Não foi possível criar pasta de download \'%1$s\'</string>
<string name="error_report_button_text">Reportar erro por e-mail</string>
<string name="error_report_title">Relatório de erro</string>
<string name="error_snackbar_action">RELATÓRIO</string>
<string name="error_snackbar_message">Desculpe, ocorreram alguns erros.</string>
<string name="finish">OK</string>
<string name="general_error">Erro</string>
<string name="info_dir_created">Pasta de download criada \'%1$s\'</string>
<string name="install">Instalar</string>
<string name="light_theme_title">Claro</string>
<string name="msg_error">Erro</string>
<string name="msg_copied">Copiado para a área de transferência.</string>
<string name="msg_name">Nome do arquivo</string>
<string name="msg_running_detail">Toque para detalhes</string>
<string name="msg_running">NewPipe baixando</string>
<string name="msg_server_unsupported">Servidor não suportado</string>
<string name="msg_wait">Aguarde…</string>
<string name="network_error">Erro de rede</string>
<string name="next_video_title">Próximo vídeo</string>
<string name="open_in_browser">Abrir no navegador</string>
<string name="pause">Pausar</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Desafio reCAPTCHA</string>
<string name="recaptcha_request_toast">Desafio reCAPTCHA solicitado</string>
<string name="report_error">Reportar um erro</string>
<string name="retry">Tentar de novo</string>
<string name="screen_rotation">Rotação</string>
<string name="search_language_title">Idioma preferido de conteúdo</string>
<string name="settings">Configurações</string>
<string name="settings_activity_title">Configurações</string>
<string name="settings_category_appearance_title">Aparência</string>
<string name="settings_category_other_title">Outros</string>
<string name="settings_category_video_audio_title">Vídeo &amp; Áudio</string>
<string name="settings_title">Configurações</string>
<string name="share">Compartilhar</string>
<string name="share_dialog_title">Compartilhar com</string>
<string name="show_age_restricted_content_title">Exibir conteúdo com restrição de idade</string>
<string name="show_next_and_similar_title">Exibir próximo vídeo e relacionados</string>
<string name="similar_videos_btn_text">Vídeos relacionados</string>
<string name="sorry_string">Desculpe, isto não deveria acontecer.</string>
<string name="start">Iniciar</string>
<string name="storage_permission_denied">Permissão para acessar o armazenamento negada</string>
<string name="switch_mode">Alternar entre lista e grade</string>
<string name="text">Texto</string>
<string name="theme_title">Tema</string>
<string name="upload_date_text">Publicado em %1$s</string>
<string name="url_not_supported_toast">URL não suportada</string>
<string name="default_language_value">pt</string>
<string name="duration_live">ao vivo</string>
<string name="downloads">Downloads</string>
<string name="downloads_title">Downloads</string>
<string name="did_you_mean">Você quis dizer: %1$s ?</string>
<string name="m4a_description">m4a — melhor qualidade</string>
<string name="add">Nova missão</string>
<string name="app_ui_crash">App/interface parou</string>
<string name="background_player_name">Player NewPipe em segundo plano</string>
<string name="background_player_playing_toast">Reproduzindo em segundo plano</string>
<string name="could_not_setup_download_menu">Não foi possível preparar o menu de download.</string>
<string name="detail_thumbnail_view_description">Thumbnail do vídeo</string>
<string name="detail_uploader_thumbnail_view_description">Thumbnail do usuário que enviou o vídeo</string>
<string name="download_path_audio_dialog_title">Informe o local para download de áudios.</string>
<string name="download_path_audio_summary">Local para salvar áudios baixados</string>
<string name="download_path_audio_title">Local de download de áudio</string>
<string name="download_path_dialog_title">Informe o local para download de vídeos</string>
<string name="download_path_summary">Local para salvar vídeos baixados</string>
<string name="download_path_title">Local de download de vídeo</string>
<string name="enable_background_audio">Reproduzir em segundo plano</string>
<string name="error_drm_not_supported">Conteúdo protegido não é suportado em versões de API anterior a 18</string>
<string name="error_drm_unknown">Ocorreu um erro DRM desconhecido</string>
<string name="error_drm_unsupported_scheme">Este dispositivo não suporta o método DRM necessário</string>
<string name="error_instantiating_decoder">Não foi possível inicializar o decodificador %1$s</string>
<string name="error_no_decoder">Este dispositivo não possui um decodificador para %1$s</string>
<string name="error_no_secure_decoder">Este dispositivo não possui um decodificador seguro para %1$s</string>
<string name="error_querying_decoders">Não foi possível consultar os decodificadores do dispositivo</string>
<string name="info_requested_stream_lbl">Stream requisitado:</string>
<string name="info_searched_lbl">Busca por:</string>
<string name="kore_not_found">App Kore não instalado. Instalar?</string>
<string name="light_parsing_error">Não foi possível interpretar completamente o site.</string>
<string name="list_thumbnail_view_description">Thumbnail do vídeo</string>
<string name="live_streams_not_supported">Transmissões ao vivo ainda não são suportadas.</string>
<string name="loading">Carregando</string>
<string name="logging">Nível de log</string>
<string name="logging_normal">Normal</string>
<string name="logging_verbose">Detalhado</string>
<string name="main_bg_subtitle">Toque em busca para começar</string>
<string name="msg_exists">Arquivo já existe</string>
<string name="msg_threads">Threads</string>
<string name="msg_url">URL de download</string>
<string name="msg_url_malform">URL inválida ou internet indisponível</string>
<string name="no_available_dir">Selecione um diretório de download.</string>
<string name="no_player_found">Nenhum player de stream encontrado. Instalar VLC?</string>
<string name="parsing_error">Não foi possível interpretar o site.</string>
<string name="play_audio">Áudio</string>
<string name="play_btn_text">Reproduzir</string>
<string name="play_with_kodi_title">Reproduzir com Kodi</string>
<string name="search">Buscar</string>
<string name="search_page">Buscar página:</string>
<string name="show_play_with_kodi_summary">Exibir opção para reprodizir víedeo pela central de mídia Kodi.</string>
<string name="use_exoplayer_summary">Experimental</string>
<string name="use_exoplayer_title">Usar ExoPlayer</string>
<string name="use_external_audio_player_title">Usar reprodutor de áudio externo</string>
<string name="use_external_video_player_title">Usar reprodutor de vídeo externo</string>
<string name="use_tor_summary">(Experimental) Forçar o download de conteúdo através do Tor para maior privacidade (streaming de vídeos ainda não suportado).</string>
<string name="use_tor_title">Usar Tor</string>
<string name="user_report">Relatório do usuário</string>
<string name="show_play_with_kodi_title">Exibir opção \"Reproduzir com Kodi\"</string>
<string name="title_activity_channel">Atividade do canal</string>
<string name="msg_fetch_filename">Buscar nome do arquivo</string>
<string name="info_labels">O que:\nRequisição:\nIdioma do conteúdo:\nServiço:\nHora GMT:\nPacote:\nVersão:\nVersão SO:\nFaixa de IP:</string>
<string name="off">[off]</string>
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="view_count_text">%1$s просмотров</string>
<string name="upload_date_text">Опубликовано %1$s</string>
<string name="no_player_found">Ни одного потокового проигрывателя не было найдено. Установить?</string>
@@ -69,7 +69,7 @@
<string name="download_path_audio_dialog_title">Введите путь к папке для загрузки аудио</string>
<string name="main_bg_subtitle">Нажмите поиск чтобы начать</string>
<string name="msg_wait">Пожалуйста подождите...</string>
<string name="msg_wait">Пожалуйста подождите</string>
<string name="msg_exists">Файл уже существует</string>
<string name="msg_threads">Потоки</string>
<string name="finish">Окей</string>
@@ -143,4 +143,18 @@
<string name="storage_permission_denied">В доступе к хранилищу было отказано</string>
<string name="use_exoplayer_title">Использовать ExoPlayer</string>
<string name="msg_fetch_filename">Получить название файла</string>
<string name="could_not_load_thumbnails">Невозможно загрузить все миниатюры</string>
<string name="youtube_signature_decryption_error">Невозможно расшифровать url у видео</string>
<string name="could_not_get_stream">Потоковое видео недоступно.</string>
<string name="off">[отключен]</string>
<string name="error_no_decoder">Это устройство не поддерживает декодер для <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_no_secure_decoder">Это устройство не поддерживает безопасный декодер для <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_querying_decoders">На этом устройстве недоступен декодер.</string>
<string name="error_instantiating_decoder">Невозможно инициировать декодер <xliff:g id="decoder_name"> 1%1$s</xliff:g> 2</string>
<string name="use_exoplayer_summary">Эксперементально</string>
<string name="view">Воспроизвести</string>
<string name="switch_mode">Переключатель между списком и сеткой</string>
</resources>

View File

@@ -165,4 +165,16 @@
<string name="recaptcha_request_toast">Zahteva izziva reCAPTCHA</string>
<string name="info_labels">Predmet:\\nZahteva:\\nJezik vsebine:\\nStoritev:\\nČas v GMT:\\nPaket:\\nRazličica:\\nRazličica OS:\\nObseg IP:</string>
<string name="black_theme_title">Črna</string>
<string name="all">Vse</string>
<string name="channel">Kanal</string>
<string name="videos">videi</string>
<string name="subscriber">naročnik</string>
<string name="views">pogledi</string>
<string name="short_thousand">k</string>
<string name="short_million">mio</string>
<string name="short_billion">mrd</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="view_count_text">%1$s приказа</string>
<string name="upload_date_text">Објављен %1$s</string>
<string name="no_player_found">Нема плејера токова. Инсталирати ВЛЦ?</string>
<string name="no_player_found">Нема плејера токова. Желите ли да инсталирате ВЛЦ?</string>
<string name="install">Инсталирај</string>
<string name="cancel">Одустани</string>
<string name="open_in_browser">Отвори у прегледачу</string>
@@ -308,4 +308,14 @@
<string name="black_theme_title">Црна</string>
<string name="all">Сви</string>
<string name="channel">Канал</string>
<string name="videos">видеи</string>
<string name="subscriber">претплатник</string>
<string name="views">прегледа</string>
<string name="short_thousand">х</string>
<string name="short_million">м</string>
<string name="short_billion">млрд</string>
</resources>

View File

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

View File

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

View File

@@ -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>

View File

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

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources translatable="false">
<!-- Categories -->
<string name="settings_category_video_audio">settings_category_video_audio</string>
<string name="settings_category_appearance">settings_category_appearance</string>
<string name="settings_content_options">settings_content_options</string>
<string name="settings_category_other">settings_category_other</string>
<string name="settings_category_video_audio" translatable="false">settings_category_video_audio</string>
<string name="settings_category_appearance" translatable="false">settings_category_appearance</string>
<string name="settings_content_options" translatable="false">settings_content_options</string>
<string name="settings_category_other" translatable="false">settings_category_other</string>
<!-- Key values -->
<string name="download_path_key">download_path</string>
<string name="download_path_audio_key">download_path_audio</string>
<string name="use_external_video_player_key">use_external_video_player</string>
<string name="use_external_audio_player_key">use_external_audio_player</string>
<string name="autoplay_through_intent_key">autoplay_through_intent</string>
<string name="use_exoplayer_key">use_exoplayer</string>
<string name="download_path_key" translatable="false">download_path</string>
<string name="download_path_audio_key" translatable="false">download_path_audio</string>
<string name="use_external_video_player_key" translatable="false">use_external_video_player</string>
<string name="use_external_audio_player_key" translatable="false">use_external_audio_player</string>
<string name="autoplay_through_intent_key" translatable="false">autoplay_through_intent</string>
<string name="use_exoplayer_key" translatable="false">use_exoplayer</string>
<string name="default_resolution_key">default_resolution_preference</string>
<string name="default_resolution_value">360p</string>
<string name="default_resolution_key" translatable="false">default_resolution_preference</string>
<string name="default_resolution_value" translatable="false">360p</string>
<string-array name="resolution_list">
<item>720p</item>
<item>360p</item>
@@ -22,10 +22,10 @@
<item>144p</item>
</string-array>
<string name="show_play_with_kodi_key">show_play_with_kodi</string>
<string name="show_play_with_kodi_key" translatable="false">show_play_with_kodi</string>
<string name="theme_key">theme</string>
<string name="default_theme_value">@string/light_theme_title</string>
<string name="theme_key" translatable="false">theme</string>
<string name="default_theme_value" translatable="false">@string/light_theme_title</string>
<string-array name="theme_description_list">
<item>@string/light_theme_title</item>
<item>@string/dark_theme_title</item>
@@ -35,8 +35,8 @@
<item>@string/dark_theme_title</item>
</string-array>
<string name="default_audio_format_key">default_audio_format</string>
<string name="default_audio_format_value">m4a</string>
<string name="default_audio_format_key" translatable="false">default_audio_format</string>
<string name="default_audio_format_value" translatable="false">m4a</string>
<string-array name="audio_format_description_list">
<item>@string/webm_description</item>
<item>@string/m4a_description</item>
@@ -46,9 +46,9 @@
<item>m4a</item>
</string-array>
<string name="show_next_video_key">show_next_video</string>
<string name="show_next_video_key" translatable="false">show_next_video</string>
<string name="default_language_value">en</string>
<string name="search_language_key">search_language</string>
<string name="search_language_key" translatable="false">search_language</string>
<!-- TODO: scrape these programmatically, then store in a local cache -->
<!-- alternatively, load these from some local android data store -->
<string-array name="language_codes">
@@ -209,6 +209,6 @@
<item>日本語</item>
<item>한국어</item>
</string-array>
<string name="show_age_restricted_content">show_age_restricted_content</string>
<string name="use_tor_key">use_tor</string>
<string name="show_age_restricted_content" translatable="false">show_age_restricted_content</string>
<string name="use_tor_key" translatable="false">use_tor</string>
</resources>

View File

@@ -6,7 +6,7 @@
<string name="title_videoitem_detail" translatable="false">NewPipe</string>
<string name="view_count_text">%1$s views</string>
<string name="upload_date_text">Published on %1$s</string>
<string name="no_player_found">No stream player found. Install VLC?</string>
<string name="no_player_found">No stream player found. Do you want to install VLC?</string>
<string name="install">Install</string>
<string name="cancel">Cancel</string>
<string name="fdroid_vlc_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string>
@@ -17,7 +17,7 @@
<string name="search">Search</string>
<string name="settings">Settings</string>
<string name="did_you_mean">Did you mean: %1$s ?</string>
<string name="search_page">Search page: </string>
<string name="search_page">"Search page: "</string>
<string name="share_dialog_title">Share with</string>
<string name="choose_browser">Choose browser</string>
<string name="screen_rotation">rotation</string>
@@ -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>
@@ -180,7 +188,7 @@
<string name="md5" translatable="false">MD5</string>
<string name="sha1" translatable="false">SHA1</string>
<string name="title_activity_channel">ChannelActivity</string>
<string name="large_text">
<string name="large_text" translatable="false">
"Material is the metaphor.\n\n"
"A material metaphor is the unifying theory of a rationalized space and a system of motion."

View File

@@ -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>

Some files were not shown because too many files have changed in this diff Show More