Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0d41c6b16 | ||
|
|
f7a531e71b | ||
|
|
c28fddc4dd | ||
|
|
b6ea10fc73 | ||
|
|
320eb44061 | ||
|
|
7a6b5dd5b7 | ||
|
|
460653ed16 | ||
|
|
b6fda788c5 | ||
|
|
da4b9306fa | ||
|
|
d76c02cbf4 | ||
|
|
4d5466b5cd | ||
|
|
e9c20ac8b0 | ||
|
|
961820a250 | ||
|
|
4cc2976061 | ||
|
|
477f182b43 | ||
|
|
a5252bb765 | ||
|
|
3f0078f38a | ||
|
|
91434dd2ac | ||
|
|
f29bd0a6e7 | ||
|
|
7186f58374 | ||
|
|
5c8bcf15ba | ||
|
|
130757ee99 | ||
|
|
5020a4f9dc | ||
|
|
ef15902ec4 | ||
|
|
ed8d13f837 | ||
|
|
27f2c65e6d | ||
|
|
0b3428ede9 | ||
|
|
3e0102ad2a | ||
|
|
c42002ccc5 | ||
|
|
e8101d5d91 | ||
|
|
ecdf4ad502 | ||
|
|
af760f9cdc | ||
|
|
b3491da49f | ||
|
|
c322feeaff | ||
|
|
b26f46a063 | ||
|
|
9f0944dc5a | ||
|
|
e869098434 | ||
|
|
4baa23af5f | ||
|
|
78fc5bbbd5 | ||
|
|
a69784d168 | ||
|
|
d48a7f1a18 |
5
.github/CONTRIBUTING.md
vendored
@@ -13,6 +13,9 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
||||
* Check if this issue/feature is already fixed/implemented in the repository
|
||||
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
||||
|
||||
## Bugfixing
|
||||
* If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.
|
||||
|
||||
## Translation
|
||||
|
||||
* NewPipe can be translated on [weblate](https://hosted.weblate.org/projects/newpipe/strings/)
|
||||
@@ -31,6 +34,6 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
||||
|
||||
## Communication
|
||||
|
||||
* I hereby declare our Slack channel as dead!!! There are no plans on building a new chat, but if there is interest on creating one and keeping it alive, I'd be pleased to create one again.
|
||||
* WE DO NOW HAVE A MAILING LIST: [newpipe@list.schabi.org](https://list.schabi.org/cgi-bin/mailman/listinfo/newpipe).
|
||||
* If you want to get in contact with me or one of our other contributors you can send me an email at tnp(at)schabi.org
|
||||
* Feel free to post suggestions, changes, ideas etc!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@
|
||||
<activity
|
||||
android:name=".ChannelActivity"
|
||||
android:label="@string/title_activity_channel"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
|
||||
<activity
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ package org.schabi.newpipe.extractor;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**Common properties between StreamInfo and StreamPreviewInfo.*/
|
||||
/**Common properties between StreamInfo and StreamInfoItem.*/
|
||||
public abstract class AbstractStreamInfo {
|
||||
public static enum StreamType {
|
||||
public enum StreamType {
|
||||
NONE, // placeholder to check if stream type was checked or not
|
||||
VIDEO_STREAM,
|
||||
AUDIO_STREAM,
|
||||
|
||||
33
app/src/main/java/org/schabi/newpipe/extractor/InfoItem.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.schabi.newpipe.extractor;
|
||||
|
||||
/**
|
||||
* Created by the-scrabi on 11.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* InfoItem.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public interface InfoItem {
|
||||
public enum InfoType {
|
||||
STREAM,
|
||||
PLAYLIST,
|
||||
CHANNEL
|
||||
}
|
||||
|
||||
InfoType infoType();
|
||||
String getTitle();
|
||||
String getLink();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.schabi.newpipe.extractor;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* InfoItemCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class InfoItemCollector {
|
||||
private List<InfoItem> itemList = new Vector<>();
|
||||
private List<Throwable> errors = new Vector<>();
|
||||
private int serviceId = -1;
|
||||
|
||||
public InfoItemCollector(int serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public List<InfoItem> getItemList() {
|
||||
return itemList;
|
||||
}
|
||||
public List<Throwable> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
protected void addFromCollector(InfoItemCollector otherC) throws ExtractionException {
|
||||
if(serviceId != otherC.serviceId) {
|
||||
throw new ExtractionException("Service Id does not equal: "
|
||||
+ NewPipe.getNameOfService(serviceId)
|
||||
+ " and " + NewPipe.getNameOfService(otherC.serviceId));
|
||||
}
|
||||
errors.addAll(otherC.errors);
|
||||
itemList.addAll(otherC.itemList);
|
||||
}
|
||||
protected void addError(Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
protected void addItem(InfoItem item) {
|
||||
itemList.add(item);
|
||||
}
|
||||
protected int getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.schabi.newpipe.extractor.search;
|
||||
package org.schabi.newpipe.extractor;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.schabi.newpipe.extractor.channel;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -31,7 +31,7 @@ public abstract class ChannelExtractor {
|
||||
private int serviceId;
|
||||
private String url;
|
||||
private UrlIdHandler urlIdHandler;
|
||||
private StreamPreviewInfoCollector previewInfoCollector;
|
||||
private StreamInfoItemCollector previewInfoCollector;
|
||||
private int page = -1;
|
||||
|
||||
public ChannelExtractor(UrlIdHandler urlIdHandler, String url, int page, int serviceId)
|
||||
@@ -40,12 +40,12 @@ public abstract class ChannelExtractor {
|
||||
this.page = page;
|
||||
this.serviceId = serviceId;
|
||||
this.urlIdHandler = urlIdHandler;
|
||||
previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
|
||||
previewInfoCollector = new StreamInfoItemCollector(urlIdHandler, serviceId);
|
||||
}
|
||||
|
||||
public String getUrl() { return url; }
|
||||
public UrlIdHandler getUrlIdHandler() { return urlIdHandler; }
|
||||
public StreamPreviewInfoCollector getStreamPreviewInfoCollector() {
|
||||
public StreamInfoItemCollector getStreamPreviewInfoCollector() {
|
||||
return previewInfoCollector;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class ChannelExtractor {
|
||||
public abstract String getAvatarUrl() throws ParsingException;
|
||||
public abstract String getBannerUrl() throws ParsingException;
|
||||
public abstract String getFeedUrl() throws ParsingException;
|
||||
public abstract StreamPreviewInfoCollector getStreams() throws ParsingException;
|
||||
public abstract StreamInfoItemCollector getStreams() throws ParsingException;
|
||||
public abstract boolean hasNextPage() throws ParsingException;
|
||||
public int getServiceId() {
|
||||
return serviceId;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.schabi.newpipe.extractor.channel;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
@@ -59,7 +60,7 @@ public class ChannelInfo {
|
||||
info.errors.add(e);
|
||||
}
|
||||
try {
|
||||
StreamPreviewInfoCollector c = extractor.getStreams();
|
||||
StreamInfoItemCollector c = extractor.getStreams();
|
||||
info.related_streams = c.getItemList();
|
||||
info.errors.addAll(c.getErrors());
|
||||
} catch(Exception e) {
|
||||
@@ -74,7 +75,7 @@ public class ChannelInfo {
|
||||
public String avatar_url = "";
|
||||
public String banner_url = "";
|
||||
public String feed_url = "";
|
||||
public List<StreamPreviewInfo> related_streams = null;
|
||||
public List<InfoItem> related_streams = null;
|
||||
public boolean hasNextPage = false;
|
||||
|
||||
public List<Throwable> errors = new Vector<>();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.schabi.newpipe.extractor.channel;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 11.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* ChannelInfoItem.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class ChannelInfoItem implements InfoItem {
|
||||
|
||||
public int serviceId = -1;
|
||||
public String channelName = "";
|
||||
public String thumbnailUrl = "";
|
||||
public String webPageUrl = "";
|
||||
public String description = "";
|
||||
public long subscriberCount = -1;
|
||||
public int videoAmount = -1;
|
||||
|
||||
public InfoType infoType() {
|
||||
return InfoType.CHANNEL;
|
||||
}
|
||||
public String getTitle() {
|
||||
return channelName;
|
||||
}
|
||||
public String getLink() {
|
||||
return webPageUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.schabi.newpipe.extractor.channel;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.FoundAdException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* ChannelInfoItemCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class ChannelInfoItemCollector extends InfoItemCollector {
|
||||
public ChannelInfoItemCollector(int serviceId) {
|
||||
super(serviceId);
|
||||
}
|
||||
|
||||
public ChannelInfoItem extract(ChannelInfoItemExtractor extractor) throws ParsingException {
|
||||
ChannelInfoItem resultItem = new ChannelInfoItem();
|
||||
// importand information
|
||||
resultItem.channelName = extractor.getChannelName();
|
||||
|
||||
resultItem.serviceId = getServiceId();
|
||||
resultItem.webPageUrl = extractor.getWebPageUrl();
|
||||
|
||||
// optional information
|
||||
try {
|
||||
resultItem.subscriberCount = extractor.getSubscriberCount();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.videoAmount = extractor.getVideoAmount();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.thumbnailUrl = extractor.getThumbnailUrl();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.description = extractor.getDescription();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
return resultItem;
|
||||
}
|
||||
|
||||
public void commit(ChannelInfoItemExtractor extractor) throws ParsingException {
|
||||
try {
|
||||
addItem(extract(extractor));
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.schabi.newpipe.extractor.channel;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* ChannelInfoItemExtractor.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public interface ChannelInfoItemExtractor {
|
||||
String getThumbnailUrl() throws ParsingException;
|
||||
String getChannelName() throws ParsingException;
|
||||
String getWebPageUrl() throws ParsingException;
|
||||
String getDescription() throws ParsingException;
|
||||
long getSubscriberCount() throws ParsingException;
|
||||
int getVideoAmount() throws ParsingException;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.schabi.newpipe.extractor.search;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.FoundAdException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* InfoItemSearchCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class InfoItemSearchCollector extends InfoItemCollector {
|
||||
private String suggestion = "";
|
||||
private StreamInfoItemCollector streamCollector;
|
||||
private ChannelInfoItemCollector channelCollector;
|
||||
|
||||
SearchResult result = new SearchResult();
|
||||
|
||||
InfoItemSearchCollector(UrlIdHandler handler, int serviceId) {
|
||||
super(serviceId);
|
||||
streamCollector = new StreamInfoItemCollector(handler, serviceId);
|
||||
channelCollector = new ChannelInfoItemCollector(serviceId);
|
||||
}
|
||||
|
||||
public void setSuggestion(String suggestion) {
|
||||
this.suggestion = suggestion;
|
||||
}
|
||||
|
||||
public SearchResult getSearchResult() throws ExtractionException {
|
||||
|
||||
addFromCollector(channelCollector);
|
||||
addFromCollector(streamCollector);
|
||||
|
||||
result.suggestion = suggestion;
|
||||
result.errors = getErrors();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void commit(StreamInfoItemExtractor extractor) {
|
||||
try {
|
||||
result.resultList.add(streamCollector.extract(extractor));
|
||||
} catch(FoundAdException ae) {
|
||||
System.err.println("Found add");
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void commit(ChannelInfoItemExtractor extractor) {
|
||||
try {
|
||||
result.resultList.add(channelCollector.extract(extractor));
|
||||
} catch(FoundAdException ae) {
|
||||
System.err.println("Found add");
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package org.schabi.newpipe.extractor.search;
|
||||
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 10.08.15.
|
||||
@@ -26,24 +28,26 @@ import java.io.IOException;
|
||||
*/
|
||||
|
||||
public abstract class SearchEngine {
|
||||
public enum Filter {
|
||||
STREAM, CHANNEL, PLAY_LIST
|
||||
}
|
||||
|
||||
public static class NothingFoundException extends ExtractionException {
|
||||
public NothingFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private StreamPreviewInfoSearchCollector collector;
|
||||
private InfoItemSearchCollector collector;
|
||||
|
||||
public SearchEngine(UrlIdHandler urlIdHandler, int serviceId) {
|
||||
collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId);
|
||||
collector = new InfoItemSearchCollector(urlIdHandler, serviceId);
|
||||
}
|
||||
|
||||
protected StreamPreviewInfoSearchCollector getStreamPreviewInfoSearchCollector() {
|
||||
protected InfoItemSearchCollector getInfoItemSearchCollector() {
|
||||
return collector;
|
||||
}
|
||||
|
||||
//Result search(String query, int page);
|
||||
public abstract StreamPreviewInfoSearchCollector search(
|
||||
String query, int page, String contentCountry)
|
||||
public abstract InfoItemSearchCollector search(
|
||||
String query, int page, String contentCountry, EnumSet<Filter> filter)
|
||||
throws ExtractionException, IOException;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.schabi.newpipe.extractor.search;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
@@ -29,13 +31,17 @@ import java.util.Vector;
|
||||
|
||||
public class SearchResult {
|
||||
public static SearchResult getSearchResult(SearchEngine engine, String query,
|
||||
int page, String languageCode)
|
||||
int page, String languageCode, EnumSet<SearchEngine.Filter> filter)
|
||||
throws ExtractionException, IOException {
|
||||
|
||||
SearchResult result = engine.search(query, page, languageCode).getSearchResult();
|
||||
SearchResult result = engine
|
||||
.search(query, page, languageCode, filter)
|
||||
.getSearchResult();
|
||||
if(result.resultList.isEmpty()) {
|
||||
if(result.suggestion.isEmpty()) {
|
||||
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<>();
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package org.schabi.newpipe.extractor.search;
|
||||
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 11.05.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamPreviewInfoSearchCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class StreamPreviewInfoSearchCollector extends StreamPreviewInfoCollector {
|
||||
|
||||
private String suggestion = "";
|
||||
|
||||
public StreamPreviewInfoSearchCollector(UrlIdHandler handler, int serviceId) {
|
||||
super(handler, serviceId);
|
||||
}
|
||||
|
||||
public void setSuggestion(String suggestion) {
|
||||
this.suggestion = suggestion;
|
||||
}
|
||||
|
||||
public SearchResult getSearchResult() {
|
||||
SearchResult result = new SearchResult();
|
||||
result.suggestion = suggestion;
|
||||
result.errors = getErrors();
|
||||
result.resultList = getItemList();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -137,7 +137,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
Element el = doc.select("div[id=\"gh-banner\"]").first().select("style").first();
|
||||
String cssContent = el.html();
|
||||
String url = "https:" + Parser.matchGroup1("url\\(([^)]+)\\)", cssContent);
|
||||
if (url.contains("s.ytimg.com")) {
|
||||
|
||||
if (url.contains("s.ytimg.com") || url.contains("default_banner")) {
|
||||
bannerUrl = null;
|
||||
} else {
|
||||
bannerUrl = url;
|
||||
@@ -150,8 +151,8 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPreviewInfoCollector getStreams() throws ParsingException {
|
||||
StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector();
|
||||
public StreamInfoItemCollector getStreams() throws ParsingException {
|
||||
StreamInfoItemCollector collector = getStreamPreviewInfoCollector();
|
||||
Element ul = null;
|
||||
if(isAjaxPage) {
|
||||
ul = doc.select("body").first();
|
||||
@@ -161,7 +162,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
||||
|
||||
for(final Element li : ul.children()) {
|
||||
if (li.select("div[class=\"feed-item-dismissable\"]").first() != null) {
|
||||
collector.commit(new StreamPreviewInfoExtractor() {
|
||||
collector.commit(new StreamInfoItemExtractor() {
|
||||
@Override
|
||||
public AbstractStreamInfo.StreamType getStreamType() throws ParsingException {
|
||||
return AbstractStreamInfo.StreamType.VIDEO_STREAM;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import org.schabi.newpipe.extractor.Parser;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.jsoup.nodes.Element;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* YoutubeChannelInfoItemExtractor.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor {
|
||||
private Element el;
|
||||
|
||||
public YoutubeChannelInfoItemExtractor(Element el) {
|
||||
this.el = el;
|
||||
}
|
||||
|
||||
public String getThumbnailUrl() throws ParsingException {
|
||||
Element img = el.select("span[class*=\"yt-thumb-simple\"]").first()
|
||||
.select("img").first();
|
||||
|
||||
String url = img.attr("abs:src");
|
||||
|
||||
if(url.contains("gif")) {
|
||||
url = img.attr("abs:data-thumb");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getChannelName() throws ParsingException {
|
||||
return el.select("a[class*=\"yt-uix-tile-link\"]").first()
|
||||
.text();
|
||||
}
|
||||
|
||||
public String getWebPageUrl() throws ParsingException {
|
||||
return el.select("a[class*=\"yt-uix-tile-link\"]").first()
|
||||
.attr("abs:href");
|
||||
}
|
||||
|
||||
public long getSubscriberCount() throws ParsingException {
|
||||
Element subsEl = el.select("span[class*=\"yt-subscriber-count\"]").first();
|
||||
if(subsEl == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return Integer.parseInt(subsEl.text().replaceAll("\\D+",""));
|
||||
}
|
||||
}
|
||||
|
||||
public int getVideoAmount() throws ParsingException {
|
||||
Element metaEl = el.select("ul[class*=\"yt-lockup-meta-info\"]").first();
|
||||
if(metaEl == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return Integer.parseInt(metaEl.text().replaceAll("\\D+",""));
|
||||
}
|
||||
}
|
||||
|
||||
public String getDescription() throws ParsingException {
|
||||
Element desEl = el.select("div[class*=\"yt-lockup-description\"]").first();
|
||||
if(desEl == null) {
|
||||
return "";
|
||||
} else {
|
||||
return desEl.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import org.schabi.newpipe.extractor.Downloader;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.InfoItemSearchCollector;
|
||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||
import org.schabi.newpipe.extractor.search.StreamPreviewInfoSearchCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,16 +45,24 @@ public class YoutubeSearchEngine extends SearchEngine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPreviewInfoSearchCollector search(String query, int page, String languageCode)
|
||||
public InfoItemSearchCollector search(String query,
|
||||
int page,
|
||||
String languageCode,
|
||||
EnumSet<Filter> filter)
|
||||
throws IOException, ExtractionException {
|
||||
StreamPreviewInfoSearchCollector collector = getStreamPreviewInfoSearchCollector();
|
||||
InfoItemSearchCollector collector = getInfoItemSearchCollector();
|
||||
|
||||
|
||||
Downloader downloader = NewPipe.getDownloader();
|
||||
|
||||
String url = "https://www.youtube.com/results"
|
||||
+ "?search_query=" + URLEncoder.encode(query, CHARSET_UTF_8)
|
||||
+ "&page=" + Integer.toString(page + 1)
|
||||
+ "&filters=" + "video";
|
||||
+ "?q=" + URLEncoder.encode(query, CHARSET_UTF_8)
|
||||
+ "&page=" + Integer.toString(page + 1);
|
||||
if(filter.contains(Filter.STREAM) && !filter.contains(Filter.CHANNEL)) {
|
||||
url += "&sp=EgIQAQ%253D%253D";
|
||||
} else if(!filter.contains(Filter.STREAM) && filter.contains(Filter.CHANNEL)) {
|
||||
url += "&sp=EgIQAg%253D%253D";
|
||||
}
|
||||
|
||||
String site;
|
||||
//String url = builder.build().toString();
|
||||
@@ -92,22 +100,20 @@ public class YoutubeSearchEngine extends SearchEngine {
|
||||
}
|
||||
// search message item
|
||||
} else if ((el = item.select("div[class*=\"search-message\"]").first()) != null) {
|
||||
//result.errorMessage = el.text();
|
||||
throw new NothingFoundException(el.text());
|
||||
|
||||
// video item type
|
||||
} else if ((el = item.select("div[class*=\"yt-lockup-video\"").first()) != null) {
|
||||
collector.commit(extractPreviewInfo(el));
|
||||
} else if ((el = item.select("div[class*=\"yt-lockup-video\"]").first()) != null) {
|
||||
collector.commit(new YoutubeStreamInfoItemExtractor(el));
|
||||
} else if((el = item.select("div[class*=\"yt-lockup-channel\"]").first()) != null) {
|
||||
collector.commit(new YoutubeChannelInfoItemExtractor(el));
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
collector.addError(new Exception("unexpected element found:\"" + el + "\""));
|
||||
// noinspection ConstantConditions
|
||||
// simply ignore not known items
|
||||
// throw new ExtractionException("unexpected element found: \"" + item + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
return collector;
|
||||
}
|
||||
|
||||
private StreamPreviewInfoExtractor extractPreviewInfo(final Element item) {
|
||||
return new YoutubeStreamPreviewInfoExtractor(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.search.SearchEngine;
|
||||
import org.schabi.newpipe.extractor.search.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -657,7 +657,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPreviewInfoExtractor getNextVideo() throws ParsingException {
|
||||
public StreamInfoItemExtractor getNextVideo() throws ParsingException {
|
||||
try {
|
||||
return extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
|
||||
.select("li").first());
|
||||
@@ -667,9 +667,9 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamPreviewInfoCollector getRelatedVideos() throws ParsingException {
|
||||
public StreamInfoItemCollector getRelatedVideos() throws ParsingException {
|
||||
try {
|
||||
StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector();
|
||||
StreamInfoItemCollector collector = getStreamPreviewInfoCollector();
|
||||
Element ul = doc.select("ul[id=\"watch-related\"]").first();
|
||||
if(ul != null) {
|
||||
for (Element li : ul.children()) {
|
||||
@@ -707,10 +707,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
||||
}
|
||||
|
||||
/**Provides information about links to other videos on the video page, such as related videos.
|
||||
* This is encapsulated in a StreamPreviewInfo object,
|
||||
* This is encapsulated in a StreamInfoItem object,
|
||||
* which is a subset of the fields in a full StreamInfo.*/
|
||||
private StreamPreviewInfoExtractor extractVideoPreviewInfo(final Element li) {
|
||||
return new StreamPreviewInfoExtractor() {
|
||||
private StreamInfoItemExtractor extractVideoPreviewInfo(final Element li) {
|
||||
return new StreamInfoItemExtractor() {
|
||||
@Override
|
||||
public AbstractStreamInfo.StreamType getStreamType() throws ParsingException {
|
||||
return null;
|
||||
|
||||
@@ -4,11 +4,11 @@ import org.jsoup.nodes.Element;
|
||||
import org.schabi.newpipe.extractor.AbstractStreamInfo;
|
||||
import org.schabi.newpipe.extractor.Parser;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemExtractor;
|
||||
|
||||
/**
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* YoutubeStreamPreviewInfoExtractor.java is part of NewPipe.
|
||||
* YoutubeStreamInfoItemExtractor.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,11 +24,11 @@ import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfoExtractor;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtractor {
|
||||
public class YoutubeStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||
|
||||
private final Element item;
|
||||
|
||||
public YoutubeStreamPreviewInfoExtractor(Element item) {
|
||||
public YoutubeStreamInfoItemExtractor(Element item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@@ -83,9 +83,12 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra
|
||||
@Override
|
||||
public String getUploadDate() throws ParsingException {
|
||||
try {
|
||||
return item.select("div[class=\"yt-lockup-meta\"]").first()
|
||||
.select("li").first()
|
||||
.text();
|
||||
Element div = item.select("div[class=\"yt-lockup-meta\"]").first();
|
||||
if(div == null) {
|
||||
return null;
|
||||
} else {
|
||||
return div.select("li").first().text();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
throw new ParsingException("Could not get uplaod date", e);
|
||||
}
|
||||
@@ -96,9 +99,13 @@ public class YoutubeStreamPreviewInfoExtractor implements StreamPreviewInfoExtra
|
||||
String output;
|
||||
String input;
|
||||
try {
|
||||
input = item.select("div[class=\"yt-lockup-meta\"]").first()
|
||||
.select("li").get(1)
|
||||
.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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.schabi.newpipe.extractor.Downloader;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.search.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.SuggestionExtractor;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
@@ -15,7 +15,6 @@ import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class StreamExtractor {
|
||||
private int serviceId;
|
||||
private String url;
|
||||
private UrlIdHandler urlIdHandler;
|
||||
private StreamPreviewInfoCollector previewInfoCollector;
|
||||
private StreamInfoItemCollector previewInfoCollector;
|
||||
|
||||
public class ExtractorInitException extends ExtractionException {
|
||||
public ExtractorInitException(String message) {
|
||||
@@ -61,10 +61,10 @@ public abstract class StreamExtractor {
|
||||
public StreamExtractor(UrlIdHandler urlIdHandler, String url, int serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
this.urlIdHandler = urlIdHandler;
|
||||
previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
|
||||
previewInfoCollector = new StreamInfoItemCollector(urlIdHandler, serviceId);
|
||||
}
|
||||
|
||||
protected StreamPreviewInfoCollector getStreamPreviewInfoCollector() {
|
||||
protected StreamInfoItemCollector getStreamPreviewInfoCollector() {
|
||||
return previewInfoCollector;
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ public abstract class StreamExtractor {
|
||||
public abstract String getAverageRating() throws ParsingException;
|
||||
public abstract int getLikeCount() throws ParsingException;
|
||||
public abstract int getDislikeCount() throws ParsingException;
|
||||
public abstract StreamPreviewInfoExtractor getNextVideo() throws ParsingException;
|
||||
public abstract StreamPreviewInfoCollector getRelatedVideos() throws ParsingException;
|
||||
public abstract StreamInfoItemExtractor getNextVideo() throws ParsingException;
|
||||
public abstract StreamInfoItemCollector getRelatedVideos() throws ParsingException;
|
||||
public abstract String getPageUrl();
|
||||
public abstract StreamInfo.StreamType getStreamType() throws ParsingException;
|
||||
public int getServiceId() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.schabi.newpipe.extractor.stream_info;
|
||||
|
||||
import org.schabi.newpipe.extractor.AbstractStreamInfo;
|
||||
import org.schabi.newpipe.extractor.DashMpdParser;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
|
||||
@@ -55,14 +56,14 @@ public class StreamInfo extends AbstractStreamInfo {
|
||||
this.view_count = avi.view_count;
|
||||
|
||||
//todo: better than this
|
||||
if(avi instanceof StreamPreviewInfo) {
|
||||
if(avi instanceof StreamInfoItem) {
|
||||
//shitty String to convert code
|
||||
/*
|
||||
String dur = ((StreamPreviewInfo)avi).duration;
|
||||
String dur = ((StreamInfoItem)avi).duration;
|
||||
int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":")));
|
||||
int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length()));
|
||||
*/
|
||||
this.duration = ((StreamPreviewInfo)avi).duration;
|
||||
this.duration = ((StreamInfoItem)avi).duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,12 +242,12 @@ public class StreamInfo extends AbstractStreamInfo {
|
||||
// get next video
|
||||
if(streamInfo.next_video != null)
|
||||
{
|
||||
StreamPreviewInfoCollector c = new StreamPreviewInfoCollector(
|
||||
StreamInfoItemCollector c = new StreamInfoItemCollector(
|
||||
extractor.getUrlIdHandler(), extractor.getServiceId());
|
||||
StreamPreviewInfoExtractor nextVideo = extractor.getNextVideo();
|
||||
StreamInfoItemExtractor nextVideo = extractor.getNextVideo();
|
||||
c.commit(nextVideo);
|
||||
if(c.getItemList().size() != 0) {
|
||||
streamInfo.next_video = c.getItemList().get(0);
|
||||
streamInfo.next_video = (StreamInfoItem) c.getItemList().get(0);
|
||||
}
|
||||
streamInfo.errors.addAll(c.getErrors());
|
||||
}
|
||||
@@ -256,7 +257,7 @@ public class StreamInfo extends AbstractStreamInfo {
|
||||
}
|
||||
try {
|
||||
// get related videos
|
||||
StreamPreviewInfoCollector c = extractor.getRelatedVideos();
|
||||
StreamInfoItemCollector c = extractor.getRelatedVideos();
|
||||
streamInfo.related_streams = c.getItemList();
|
||||
streamInfo.errors.addAll(c.getErrors());
|
||||
} catch(Exception e) {
|
||||
@@ -284,8 +285,8 @@ public class StreamInfo extends AbstractStreamInfo {
|
||||
public int like_count = -1;
|
||||
public int dislike_count = -1;
|
||||
public String average_rating = "";
|
||||
public StreamPreviewInfo next_video = null;
|
||||
public List<StreamPreviewInfo> related_streams = null;
|
||||
public StreamInfoItem next_video = null;
|
||||
public List<InfoItem> related_streams = null;
|
||||
//in seconds. some metadata is not passed using a StreamInfo object!
|
||||
public int start_position = 0;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ package org.schabi.newpipe.extractor.stream_info;
|
||||
* Created by Christian Schabesberger on 26.08.15.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamPreviewInfo.java is part of NewPipe.
|
||||
* StreamInfoItem.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,8 +21,21 @@ package org.schabi.newpipe.extractor.stream_info;
|
||||
*/
|
||||
|
||||
import org.schabi.newpipe.extractor.AbstractStreamInfo;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
/**Info object for previews of unopened videos, eg search results, related videos*/
|
||||
public class StreamPreviewInfo extends AbstractStreamInfo {
|
||||
public class StreamInfoItem extends AbstractStreamInfo implements InfoItem {
|
||||
public int duration;
|
||||
|
||||
public InfoType infoType() {
|
||||
return InfoType.STREAM;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return webpage_url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.schabi.newpipe.extractor.stream_info;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItemCollector;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.FoundAdException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 28.02.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamInfoItemCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class StreamInfoItemCollector extends InfoItemCollector {
|
||||
|
||||
private UrlIdHandler urlIdHandler;
|
||||
|
||||
public StreamInfoItemCollector(UrlIdHandler handler, int serviceId) {
|
||||
super(serviceId);
|
||||
urlIdHandler = handler;
|
||||
}
|
||||
|
||||
private UrlIdHandler getUrlIdHandler() {
|
||||
return urlIdHandler;
|
||||
}
|
||||
|
||||
public StreamInfoItem extract(StreamInfoItemExtractor extractor) throws Exception {
|
||||
|
||||
StreamInfoItem resultItem = new StreamInfoItem();
|
||||
// importand information
|
||||
resultItem.service_id = getServiceId();
|
||||
resultItem.webpage_url = extractor.getWebPageUrl();
|
||||
if (getUrlIdHandler() == null) {
|
||||
throw new ParsingException("Error: UrlIdHandler not set");
|
||||
} else if (!resultItem.webpage_url.isEmpty()) {
|
||||
resultItem.id = NewPipe.getService(getServiceId())
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
* Created by Christian Schabesberger on 28.02.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamPreviewInfoExtractor.java is part of NewPipe.
|
||||
* StreamInfoItemExtractor.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,7 +23,7 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public interface StreamPreviewInfoExtractor {
|
||||
public interface StreamInfoItemExtractor {
|
||||
AbstractStreamInfo.StreamType getStreamType() throws ParsingException;
|
||||
String getWebPageUrl() throws ParsingException;
|
||||
String getTitle() throws ParsingException;
|
||||
@@ -1,102 +0,0 @@
|
||||
package org.schabi.newpipe.extractor.stream_info;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.UrlIdHandler;
|
||||
import org.schabi.newpipe.extractor.exceptions.FoundAdException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 28.02.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamPreviewInfoCollector.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class StreamPreviewInfoCollector {
|
||||
private List<StreamPreviewInfo> itemList = new Vector<>();
|
||||
private List<Throwable> errors = new Vector<>();
|
||||
private UrlIdHandler urlIdHandler;
|
||||
private int serviceId = -1;
|
||||
|
||||
public StreamPreviewInfoCollector(UrlIdHandler handler, int serviceId) {
|
||||
urlIdHandler = handler;
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public List<StreamPreviewInfo> getItemList() {
|
||||
return itemList;
|
||||
}
|
||||
|
||||
public List<Throwable> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
public void addError(Exception e) {
|
||||
errors.add(e);
|
||||
}
|
||||
|
||||
public void commit(StreamPreviewInfoExtractor extractor) throws ParsingException {
|
||||
try {
|
||||
StreamPreviewInfo resultItem = new StreamPreviewInfo();
|
||||
// importand information
|
||||
resultItem.service_id = serviceId;
|
||||
resultItem.webpage_url = extractor.getWebPageUrl();
|
||||
if (urlIdHandler == null) {
|
||||
throw new ParsingException("Error: UrlIdHandler not set");
|
||||
} else if(!resultItem.webpage_url.isEmpty()) {
|
||||
resultItem.id = NewPipe.getService(serviceId).getUrlIdHandlerInstance().getId(resultItem.webpage_url);
|
||||
}
|
||||
resultItem.title = extractor.getTitle();
|
||||
resultItem.stream_type = extractor.getStreamType();
|
||||
|
||||
// optional information
|
||||
try {
|
||||
resultItem.duration = extractor.getDuration();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.uploader = extractor.getUploader();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.upload_date = extractor.getUploadDate();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.view_count = extractor.getViewCount();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
try {
|
||||
resultItem.thumbnail_url = extractor.getThumbnailUrl();
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
itemList.add(resultItem);
|
||||
} catch(FoundAdException ae) {
|
||||
System.out.println("AD_WARNING: " + ae.getMessage());
|
||||
} catch (Exception e) {
|
||||
addError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* ChannelInfoItemHolder .java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class ChannelInfoItemHolder extends InfoItemHolder {
|
||||
public final CircleImageView itemThumbnailView;
|
||||
public final TextView itemChannelTitleView;
|
||||
public final TextView itemSubscriberCountView;
|
||||
public final TextView itemVideoCountView;
|
||||
public final TextView itemChannelDescriptionView;
|
||||
public final Button itemButton;
|
||||
|
||||
ChannelInfoItemHolder(View v) {
|
||||
super(v);
|
||||
itemThumbnailView = (CircleImageView) v.findViewById(R.id.itemThumbnailView);
|
||||
itemChannelTitleView = (TextView) v.findViewById(R.id.itemChannelTitleView);
|
||||
itemSubscriberCountView = (TextView) v.findViewById(R.id.itemSubscriberCountView);
|
||||
itemVideoCountView = (TextView) v.findViewById(R.id.itemVideoCountView);
|
||||
itemChannelDescriptionView = (TextView) v.findViewById(R.id.itemChannelDescriptionView);
|
||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItem.InfoType infoType() {
|
||||
return InfoItem.InfoType.CHANNEL;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
@@ -11,7 +13,9 @@ import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import org.schabi.newpipe.ImageErrorLoadingListener;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.AbstractStreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamInfoItem;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 26.09.16.
|
||||
@@ -35,8 +39,17 @@ import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo;
|
||||
|
||||
public class InfoItemBuilder {
|
||||
|
||||
public interface OnItemSelectedListener {
|
||||
void selected(String url);
|
||||
final String viewsS;
|
||||
final String videosS;
|
||||
final String subsS;
|
||||
|
||||
final String thousand;
|
||||
final String million;
|
||||
final String billion;
|
||||
|
||||
private static final String TAG = InfoItemBuilder.class.toString();
|
||||
public interface OnInfoItemSelectedListener {
|
||||
void selected(String url, int serviceId);
|
||||
}
|
||||
|
||||
private Activity activity = null;
|
||||
@@ -44,18 +57,75 @@ public class InfoItemBuilder {
|
||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||
private DisplayImageOptions displayImageOptions =
|
||||
new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||
private OnItemSelectedListener onItemSelectedListener;
|
||||
private OnInfoItemSelectedListener onStreamInfoItemSelectedListener;
|
||||
private OnInfoItemSelectedListener onChannelInfoItemSelectedListener;
|
||||
|
||||
public InfoItemBuilder(Activity a, View rootView) {
|
||||
activity = a;
|
||||
this.rootView = rootView;
|
||||
viewsS = a.getString(R.string.views);
|
||||
videosS = a.getString(R.string.videos);
|
||||
subsS = a.getString(R.string.subscriber);
|
||||
thousand = a.getString(R.string.short_thousand);
|
||||
million = a.getString(R.string.short_million);
|
||||
billion = a.getString(R.string.short_billion);
|
||||
}
|
||||
|
||||
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
|
||||
this.onItemSelectedListener = onItemSelectedListener;
|
||||
public void setOnStreamInfoItemSelectedListener(
|
||||
OnInfoItemSelectedListener listener) {
|
||||
this.onStreamInfoItemSelectedListener = listener;
|
||||
}
|
||||
|
||||
public void buildByHolder(InfoItemHolder holder, final StreamPreviewInfo info) {
|
||||
public void setOnChannelInfoItemSelectedListener(
|
||||
OnInfoItemSelectedListener listener) {
|
||||
this.onChannelInfoItemSelectedListener = listener;
|
||||
}
|
||||
|
||||
public void buildByHolder(InfoItemHolder holder, final InfoItem i) {
|
||||
if(i.infoType() != holder.infoType())
|
||||
return;
|
||||
switch(i.infoType()) {
|
||||
case STREAM:
|
||||
buildStreamInfoItem((StreamInfoItemHolder) holder, (StreamInfoItem) i);
|
||||
break;
|
||||
case CHANNEL:
|
||||
buildChannelInfoItem((ChannelInfoItemHolder) holder, (ChannelInfoItem) i);
|
||||
break;
|
||||
case PLAYLIST:
|
||||
Log.e(TAG, "Not yet implemented");
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
}
|
||||
}
|
||||
|
||||
public View buildView(ViewGroup parent, final InfoItem info) {
|
||||
View itemView = null;
|
||||
InfoItemHolder holder = null;
|
||||
switch(info.infoType()) {
|
||||
case STREAM:
|
||||
itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.stream_item, parent, false);
|
||||
holder = new StreamInfoItemHolder(itemView);
|
||||
break;
|
||||
case CHANNEL:
|
||||
itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.channel_item, parent, false);
|
||||
holder = new ChannelInfoItemHolder(itemView);
|
||||
break;
|
||||
case PLAYLIST:
|
||||
Log.e(TAG, "Not yet implemented");
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
}
|
||||
buildByHolder(holder, info);
|
||||
return itemView;
|
||||
}
|
||||
|
||||
private void buildStreamInfoItem(StreamInfoItemHolder holder, final StreamInfoItem info) {
|
||||
if(info.infoType() != InfoItem.InfoType.STREAM) {
|
||||
Log.e("InfoItemBuilder", "Info type not yet supported");
|
||||
}
|
||||
// fill holder with information
|
||||
holder.itemVideoTitleView.setText(info.title);
|
||||
if(info.uploader != null && !info.uploader.isEmpty()) {
|
||||
@@ -92,29 +162,55 @@ public class InfoItemBuilder {
|
||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onItemSelectedListener.selected(info.webpage_url);
|
||||
onStreamInfoItemSelectedListener.selected(info.webpage_url, info.service_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public View buildView(ViewGroup parent, final StreamPreviewInfo info) {
|
||||
View streamPreviewView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.video_item, parent, false);
|
||||
InfoItemHolder holder = new InfoItemHolder(streamPreviewView);
|
||||
buildByHolder(holder, info);
|
||||
return streamPreviewView;
|
||||
private void buildChannelInfoItem(ChannelInfoItemHolder holder, final ChannelInfoItem info) {
|
||||
holder.itemChannelTitleView.setText(info.getTitle());
|
||||
holder.itemSubscriberCountView.setText(shortSubscriber(info.subscriberCount) + " • ");
|
||||
holder.itemVideoCountView.setText(info.videoAmount + " " + videosS);
|
||||
holder.itemChannelDescriptionView.setText(info.description);
|
||||
|
||||
holder.itemThumbnailView.setImageResource(R.drawable.buddy_channel_item);
|
||||
if(info.thumbnailUrl != null && !info.thumbnailUrl.isEmpty()) {
|
||||
imageLoader.displayImage(info.thumbnailUrl,
|
||||
holder.itemThumbnailView,
|
||||
displayImageOptions,
|
||||
new ImageErrorLoadingListener(activity, rootView, info.serviceId));
|
||||
}
|
||||
|
||||
holder.itemButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
onChannelInfoItemSelectedListener.selected(info.getLink(), info.serviceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static String shortViewCount(Long viewCount){
|
||||
public String shortViewCount(Long viewCount){
|
||||
if(viewCount >= 1000000000){
|
||||
return Long.toString(viewCount/1000000000)+"B views";
|
||||
return Long.toString(viewCount/1000000000)+ billion + " " + viewsS;
|
||||
}else if(viewCount>=1000000){
|
||||
return Long.toString(viewCount/1000000)+"M views";
|
||||
return Long.toString(viewCount/1000000)+ million + " " + viewsS;
|
||||
}else if(viewCount>=1000){
|
||||
return Long.toString(viewCount/1000)+"K views";
|
||||
return Long.toString(viewCount/1000)+ thousand + " " + viewsS;
|
||||
}else {
|
||||
return Long.toString(viewCount)+" views";
|
||||
return Long.toString(viewCount)+ " " + viewsS;
|
||||
}
|
||||
}
|
||||
|
||||
public String shortSubscriber(Long viewCount){
|
||||
if(viewCount >= 1000000000){
|
||||
return Long.toString(viewCount/1000000000)+ billion + " " + subsS;
|
||||
}else if(viewCount>=1000000){
|
||||
return Long.toString(viewCount/1000000)+ million + " " + subsS;
|
||||
}else if(viewCount>=1000){
|
||||
return Long.toString(viewCount/1000)+ thousand + " " + subsS;
|
||||
}else {
|
||||
return Long.toString(viewCount)+ " " + subsS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,11 @@ package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 01.08.16.
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* InfoItemHolder.java is part of NewPipe.
|
||||
@@ -28,25 +25,9 @@ import org.schabi.newpipe.R;
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class InfoItemHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemVideoTitleView,
|
||||
itemUploaderView,
|
||||
itemDurationView,
|
||||
itemUploadDateView,
|
||||
itemViewCountView;
|
||||
public final Button itemButton;
|
||||
|
||||
public abstract class InfoItemHolder extends RecyclerView.ViewHolder {
|
||||
public InfoItemHolder(View v) {
|
||||
super(v);
|
||||
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
|
||||
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
|
||||
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
|
||||
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
|
||||
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
|
||||
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
|
||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
||||
}
|
||||
|
||||
public abstract InfoItem.InfoType infoType();
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream_info.StreamPreviewInfo;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
@@ -33,47 +34,79 @@ import java.util.Vector;
|
||||
*/
|
||||
|
||||
public class InfoListAdapter extends RecyclerView.Adapter<InfoItemHolder> {
|
||||
private static final String TAG = InfoListAdapter.class.toString();
|
||||
|
||||
private final InfoItemBuilder infoItemBuilder;
|
||||
private final List<StreamPreviewInfo> streamList;
|
||||
private final List<InfoItem> infoItemList;
|
||||
|
||||
public InfoListAdapter(Activity a, View rootView) {
|
||||
infoItemBuilder = new InfoItemBuilder(a, rootView);
|
||||
streamList = new Vector<>();
|
||||
infoItemList = new Vector<>();
|
||||
}
|
||||
|
||||
public void setOnItemSelectedListener
|
||||
(InfoItemBuilder.OnItemSelectedListener onItemSelectedListener) {
|
||||
infoItemBuilder.setOnItemSelectedListener(onItemSelectedListener);
|
||||
public void setOnStreamInfoItemSelectedListener
|
||||
(InfoItemBuilder.OnInfoItemSelectedListener listener) {
|
||||
infoItemBuilder.setOnStreamInfoItemSelectedListener(listener);
|
||||
}
|
||||
|
||||
public void addStreamItemList(List<StreamPreviewInfo> videos) {
|
||||
public void setOnChannelInfoItemSelectedListener
|
||||
(InfoItemBuilder.OnInfoItemSelectedListener listener) {
|
||||
infoItemBuilder.setOnChannelInfoItemSelectedListener(listener);
|
||||
}
|
||||
|
||||
public void addInfoItemList(List<InfoItem> videos) {
|
||||
if(videos!= null) {
|
||||
streamList.addAll(videos);
|
||||
infoItemList.addAll(videos);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSteamItemList() {
|
||||
streamList.clear();
|
||||
infoItemList.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return streamList.size();
|
||||
return infoItemList.size();
|
||||
}
|
||||
|
||||
// don't ask why we have to do that this way... it's android accept it -.-
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
switch(infoItemList.get(position).infoType()) {
|
||||
case STREAM:
|
||||
return 0;
|
||||
case CHANNEL:
|
||||
return 1;
|
||||
case PLAYLIST:
|
||||
return 2;
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemHolder onCreateViewHolder(ViewGroup parent, int i) {
|
||||
View itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.video_item, parent, false);
|
||||
|
||||
return new InfoItemHolder(itemView);
|
||||
public InfoItemHolder onCreateViewHolder(ViewGroup parent, int type) {
|
||||
switch(type) {
|
||||
case 0:
|
||||
return new StreamInfoItemHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.stream_item, parent, false));
|
||||
case 1:
|
||||
return new ChannelInfoItemHolder(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.channel_item, parent, false));
|
||||
case 2:
|
||||
Log.e(TAG, "Playlist is not yet implemented");
|
||||
return null;
|
||||
default:
|
||||
Log.e(TAG, "Trollolo");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InfoItemHolder holder, int i) {
|
||||
infoItemBuilder.buildByHolder(holder, streamList.get(i));
|
||||
infoItemBuilder.buildByHolder(holder, infoItemList.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.icu.text.IDNA;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 01.08.16.
|
||||
*
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* StreamInfoItemHolder.java is part of NewPipe.
|
||||
*
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class StreamInfoItemHolder extends InfoItemHolder {
|
||||
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemVideoTitleView,
|
||||
itemUploaderView,
|
||||
itemDurationView,
|
||||
itemUploadDateView,
|
||||
itemViewCountView;
|
||||
public final Button itemButton;
|
||||
|
||||
public StreamInfoItemHolder(View v) {
|
||||
super(v);
|
||||
itemThumbnailView = (ImageView) v.findViewById(R.id.itemThumbnailView);
|
||||
itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView);
|
||||
itemUploaderView = (TextView) v.findViewById(R.id.itemUploaderView);
|
||||
itemDurationView = (TextView) v.findViewById(R.id.itemDurationView);
|
||||
itemUploadDateView = (TextView) v.findViewById(R.id.itemUploadDateView);
|
||||
itemViewCountView = (TextView) v.findViewById(R.id.itemViewCountView);
|
||||
itemButton = (Button) v.findViewById(R.id.item_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItem.InfoType infoType() {
|
||||
return InfoItem.InfoType.STREAM;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
151
app/src/main/java/org/schabi/newpipe/util/NavStack.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_headset_black_24dp.png
Normal file
|
After Width: | Height: | Size: 349 B |
BIN
app/src/main/res/drawable-hdpi/ic_screen_rotation_white_24dp.png
Normal file
|
After Width: | Height: | Size: 858 B |
BIN
app/src/main/res/drawable-hdpi/ic_share_black_24dp.png
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
app/src/main/res/drawable-mdpi/ic_screen_rotation_white_24dp.png
Normal file
|
After Width: | Height: | Size: 639 B |
BIN
app/src/main/res/drawable-mdpi/ic_share_black_24dp.png
Normal file
|
After Width: | Height: | Size: 262 B |
BIN
app/src/main/res/drawable-nodpi/buddy_channel_item.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/drawable-nodpi/dummi_thumbnail_playlist.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 869 B |
|
Before Width: | Height: | Size: 209 B |
|
Before Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 446 B |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_share_black_24dp.png
Normal file
|
After Width: | Height: | Size: 483 B |
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_share_black_24dp.png
Normal file
|
After Width: | Height: | Size: 675 B |
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 888 B After Width: | Height: | Size: 888 B |
91
app/src/main/res/layout/channel_item.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout android:id="@+id/item_main_layout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/itemThumbnailView"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
|
||||
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:src="@drawable/buddy_channel_item"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
|
||||
android:layout_toRightOf="@id/itemThumbnailViewContainer"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<TextView android:id="@+id/itemChannelTitleView"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/channel_item_detail_title_text_size"/>
|
||||
|
||||
<TextView android:id="@+id/itemChannelDescriptionView"
|
||||
android:layout_weight="2"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:id="@+id/itemSubscriberCountView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
android:text="1000 subs"/>
|
||||
|
||||
<TextView android:id="@+id/itemVideoCountView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
android:text="1000 vids"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/item_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackground"/>
|
||||
</FrameLayout>
|
||||
@@ -14,7 +14,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:listitem="@layout/video_item"
|
||||
tools:listitem="@layout/stream_item"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
<ProgressBar android:id="@+id/progressBar"
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/detail_stream_thumbnail_window_layout"
|
||||
android:background="?android:windowBackground"
|
||||
android:visibility="gone">
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -226,6 +226,9 @@
|
||||
android:background="#000"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
|
||||
android:layout_toRightOf="@+id/detail_uploader_thumbnail_view"
|
||||
android:layout_toEndOf="@+id/detail_uploader_thumbnail_view"
|
||||
android:layout_below="@id/detail_uploader_thumbnail_view"/>
|
||||
</RelativeLayout>
|
||||
<Button
|
||||
|
||||
71
app/src/main/res/layout/play_list_item.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/item_main_layout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<RelativeLayout android:id="@+id/itemThumbnailViewContainer"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:layout_width="@dimen/video_item_search_thumbnail_image_width"
|
||||
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:src="@drawable/dummi_thumbnail_playlist"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
|
||||
android:layout_toRightOf="@id/itemThumbnailViewContainer"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<TextView android:id="@+id/itemTitleView"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/item_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackground"/>
|
||||
</FrameLayout>
|
||||
@@ -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>
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_settings"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/settings"/>
|
||||
|
||||
<item android:id="@+id/action_show_downloads"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/downloads" />
|
||||
|
||||
<item android:id="@+id/action_settings"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/settings"/>
|
||||
|
||||
</menu>
|
||||
@@ -6,4 +6,15 @@
|
||||
app:showAsAction="ifRoom"
|
||||
android:title="@string/search"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||
|
||||
<group android:id="@+id/search_filter_group"
|
||||
android:checkableBehavior="single">
|
||||
<item android:id="@+id/menu_filter_all"
|
||||
android:title = "@string/all"
|
||||
android:checked = "true"/>
|
||||
<item android:id="@+id/menu_filter_video"
|
||||
android:title = "@string/video"/>
|
||||
<item android:id="@+id/menu_filter_channel"
|
||||
android:title = "@string/channel"/>
|
||||
</group>
|
||||
</menu>
|
||||
@@ -5,5 +5,5 @@
|
||||
<item android:id="@+id/menu_item_screen_rotation"
|
||||
android:title="@string/screen_rotation"
|
||||
app:showAsAction="always"
|
||||
android:icon="@drawable/ic_screen_rotation_white"/>
|
||||
android:icon="@drawable/ic_screen_rotation_white_24dp"/>
|
||||
</menu>
|
||||
@@ -15,12 +15,12 @@
|
||||
<item android:id="@+id/menu_item_share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_share_black"/>
|
||||
android:icon="?attr/share"/>
|
||||
|
||||
<item android:id="@+id/action_play_with_kodi"
|
||||
android:title="@string/play_with_kodi_title"
|
||||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_cast_black"/>
|
||||
android:icon="?attr/cast"/>
|
||||
|
||||
<item android:id="@+id/menu_item_openInBrowser"
|
||||
app:showAsAction="never"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_search"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
app:showAsAction="ifRoom"
|
||||
android:title="@string/search"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||
|
||||
<item android:id="@+id/action_settings"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/settings"/>
|
||||
|
||||
<item android:id="@+id/action_show_downloads"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/downloads" />
|
||||
|
||||
<item android:id="@+id/action_report_error"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/report_error" />
|
||||
</menu>
|
||||
@@ -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ání 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 & 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 component’s 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 won’t 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
146
app/src/main/res/values-pt-rBR/strings.xml
Normal 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 & Á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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<!-- Video Item Search View Dimensions-->
|
||||
<!-- Text Size -->
|
||||
<dimen name="channel_item_detail_title_text_size">21sp</dimen>
|
||||
<dimen name="video_item_search_title_text_size">14sp</dimen>
|
||||
<dimen name="video_item_search_duration_text_size">11sp</dimen>
|
||||
<dimen name="video_item_search_uploader_text_size">12sp</dimen>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
<!-- Video Item Detail View Dimensions-->
|
||||
<!-- Text Size -->
|
||||
<dimen name="channel_item_detail_title_text_size">30sp</dimen>
|
||||
<dimen name="video_item_detail_title_text_size">20sp</dimen>
|
||||
<dimen name="video_item_detail_views_text_size">16sp</dimen>
|
||||
<dimen name="video_item_detail_likes_text_size">14sp</dimen>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<item name="thumbs_down">@drawable/ic_thumb_down_black_24dp</item>
|
||||
<item name="audio">@drawable/ic_headset_black_24dp</item>
|
||||
<item name="download">@drawable/ic_file_download_black_24dp</item>
|
||||
<item name="share">@drawable/ic_share_black</item>
|
||||
<item name="share">@drawable/ic_share_black_24dp</item>
|
||||
<item name="cast">@drawable/ic_cast_black_24dp</item>
|
||||
<item name="rss">@drawable/ic_rss_feed_white_24dp</item>
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<resources>
|
||||
<!-- Video Item Search View Dimensions-->
|
||||
<!-- Text Size -->
|
||||
<dimen name="channel_item_detail_title_text_size">21sp</dimen>
|
||||
<dimen name="video_item_search_title_text_size">14sp</dimen>
|
||||
<dimen name="video_item_search_duration_text_size">11sp</dimen>
|
||||
<dimen name="video_item_search_uploader_text_size">12sp</dimen>
|
||||
|
||||
@@ -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>
|
||||
@@ -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&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."
|
||||
|
||||
@@ -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>
|
||||
|
||||