Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2015-11-13 03:22:52 +01:00
commit d686c744d0
41 changed files with 487 additions and 706 deletions

6
.gitignore vendored
View File

@ -1,10 +1,8 @@
.gitignore
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.idea/gradle.xml
.idea/misc.xml
/app/app.iml
/.idea

View File

@ -1 +0,0 @@
NewPipe

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -1,3 +0,0 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View File

@ -1,3 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="the-scrabi" />
</component>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.4" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/NewPipe.iml" filepath="$PROJECT_DIR$/NewPipe.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="NewPipe" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -22,6 +22,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Download audio only (working but, but it could be better)
* Open a video in Kodi
* Show Next/Related videos
* Search Youtube in a specific language
## Coming Features

2
app/.gitignore vendored
View File

@ -1,3 +1,3 @@
.gitignore
/build
app/app.iml
app.iml

View File

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="NewPipe" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateDebugAndroidTestSources</task>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="recyclerview-v7-23.1.0" level="project" />
<orderEntry type="library" exported="" name="jsoup-1.8.3" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.1.0" level="project" />
<orderEntry type="library" exported="" name="rhino-1.7.7" level="project" />
<orderEntry type="library" exported="" name="design-23.1.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.1.0" level="project" />
<orderEntry type="library" exported="" name="support-annotations-23.1.0" level="project" />
</component>
</module>

View File

@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 23
versionCode 5
versionName "0.5.0"
versionCode 6
versionName "0.6.0"
}
buildTypes {
release {

View File

@ -77,7 +77,7 @@ public class ActionBarHandler {
int defaultResolutionPos = 0;
for(int i = 0; i < videoStreams.length; i++) {
itemArray[i] = VideoInfo.getNameById(videoStreams[i].format) + " " + videoStreams[i].resolution;
itemArray[i] = MediaFormat.getNameById(videoStreams[i].format) + " " + videoStreams[i].resolution;
if(defaultResolution.equals(videoStreams[i].resolution)) {
defaultResolutionPos = i;
}
@ -98,17 +98,15 @@ public class ActionBarHandler {
.getString(activity.getString(R.string.defaultAudioFormatPreference), "webm");
if(preferedFormat.equals("webm")) {
for(VideoInfo.AudioStream s : audioStreams) {
if(s.format == VideoInfo.I_WEBMA) {
if(s.format == MediaFormat.WEBMA.id) {
audioStream = s;
}
}
} else if(preferedFormat.equals("m4a")){
for(VideoInfo.AudioStream s : audioStreams) {
Log.d(TAG, VideoInfo.getMimeById(s.format) + " : " + Integer.toString(s.bandwidth));
if(s.format == VideoInfo.I_M4A &&
if(s.format == MediaFormat.M4A.id &&
(audioStream == null || audioStream.bandwidth > s.bandwidth)) {
audioStream = s;
Log.d(TAG, "last choosen");
}
}
}
@ -125,15 +123,8 @@ public class ActionBarHandler {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
inflater.inflate(R.menu.videoitem_detail, menu);
MenuItem playItem = menu.findItem(R.id.menu_item_play);
MenuItem shareItem = menu.findItem(R.id.menu_item_share);
MenuItem castItem = menu.findItem(R.id.action_play_with_kodi);
MenuItemCompat.setShowAsAction(playItem, MenuItemCompat.SHOW_AS_ACTION_ALWAYS
| MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
MenuItemCompat.setShowAsAction(shareItem, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM
| MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
castItem.setVisible(defaultPreferences
.getBoolean(activity.getString(R.string.showPlayWidthKodiPreference), false));
@ -143,9 +134,6 @@ public class ActionBarHandler {
public boolean onItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id) {
case R.id.menu_item_play:
playVideo();
return true;
case R.id.menu_item_share:
if(!videoTitle.isEmpty()) {
Intent intent = new Intent();
@ -196,7 +184,7 @@ public class ActionBarHandler {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(videoStreams[selectedStream].url),
VideoInfo.getMimeById(videoStreams[selectedStream].format));
MediaFormat.getMimeById(videoStreams[selectedStream].format));
intent.putExtra(Intent.EXTRA_TITLE, videoTitle);
intent.putExtra("title", videoTitle);
@ -235,10 +223,9 @@ public class ActionBarHandler {
}
public void downloadVideo() {
Log.d(TAG, "bla");
if(!videoTitle.isEmpty()) {
String videoSuffix = "." + VideoInfo.getSuffixById(videoStreams[selectedStream].format);
String audioSuffix = "." + VideoInfo.getSuffixById(audioStream.format);
String videoSuffix = "." + MediaFormat.getSuffixById(videoStreams[selectedStream].format);
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
Bundle args = new Bundle();
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
@ -297,7 +284,7 @@ public class ActionBarHandler {
try {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(audioStream.url),
VideoInfo.getMimeById(audioStream.format));
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(Intent.EXTRA_TITLE, videoTitle);
intent.putExtra("title", videoTitle);
activity.startActivity(intent); // HERE !!!
@ -321,7 +308,7 @@ public class ActionBarHandler {
}
});
builder.create().show();
Log.d(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
e.printStackTrace();
}
}

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
@ -29,12 +30,25 @@ import java.net.UnknownHostException;
public class Downloader {
private static final String USER_AGENT = "Mozilla/5.0";
public static String download(String siteUrl) {
StringBuffer response = new StringBuffer();
public static String download(String siteUrl, String language) {
String ret = "";
try {
URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Accept-Language", language);
ret = dl(con);
}
catch(Exception e) {
e.printStackTrace();
}
return ret;
}
private static String dl(HttpURLConnection con) {
StringBuffer response = new StringBuffer();
try {
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", USER_AGENT);
@ -57,4 +71,20 @@ public class Downloader {
}
return response.toString();
}
public static String download(String siteUrl) {
String ret = "";
try {
URL url = new URL(siteUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
ret = dl(con);
}
catch(Exception e) {
e.printStackTrace();
}
return ret;
}
}

View File

@ -0,0 +1,64 @@
package org.schabi.newpipe;
/**
* Created by Adam Howard on 08/11/15.
*
* Copyright (c) Christian Schabesberger <chris.schabesberger@mailbox.org>
* and Adam Howard <achdisposable1@gmail.com> 2015
*
* VideoListAdapter.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 enum MediaFormat {
// id name suffix mime type
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
v3GPP (0x1, "3GPP", "3gp", "video/3gpp"),
WEBM (0x2, "WebM", "webm", "video/webm"),
M4A (0x3, "m4a", "m4a", "audio/mp4"),
WEBMA (0x4, "WebM", "webm", "audio/webm");
public final int id;
public final String name;
public final String suffix;
public final String mimeType;
MediaFormat(int id, String name, String suffix, String mimeType) {
this.id = id;
this.name = name;
this.suffix = suffix;
this.mimeType = mimeType;
}
public static String getNameById(int ident) {
for (MediaFormat vf : MediaFormat.values()) {
if(vf.id == ident) return vf.name;
}
return "";
}
public static String getSuffixById(int ident) {
for (MediaFormat vf : MediaFormat.values()) {
if(vf.id == ident) return vf.suffix;
}
return "";
}
public static String getMimeById(int ident) {
for (MediaFormat vf : MediaFormat.values()) {
if(vf.id == ident) return vf.mimeType;
}
return "";
}
}

View File

@ -253,10 +253,9 @@ public class PlayVideoActivity extends AppCompatActivity {
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
private void adjustMediaControlMetrics() {

View File

@ -1,5 +1,11 @@
package org.schabi.newpipe;
import android.graphics.Bitmap;
import android.util.Log;
import java.util.Date;
import java.util.Vector;
/**
* Created by Christian Schabesberger on 26.08.15.
*
@ -20,82 +26,36 @@ package org.schabi.newpipe;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
import android.graphics.Bitmap;
import android.util.Log;
import java.util.Vector;
public class VideoInfo {
public String id = "";
public String title = "";
public String uploader = "";
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String webpage_url = "";
public String upload_date = "";
public long view_count = 0;
public String uploader_thumbnail_url = "";
public Bitmap uploader_thumbnail = null;
public String description = "";
public int duration = -1;
public int age_limit = 0;
public int like_count = 0;
public int dislike_count = 0;
public String average_rating = "";
public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null;
public VideoInfoItem nextVideo = null;
public VideoInfoItem[] relatedVideos = null;
public int videoAvailableStatus = VIDEO_AVAILABLE;
private static final String TAG = VideoInfo.class.toString();
// format identifier
public static final int I_MPEG_4 = 0x0;
public static final int I_3GPP = 0x1;
public static final int I_WEBM = 0x2;
public static final int I_M4A = 0x3;
public static final int I_WEBMA = 0x4;
// format name
public static final String F_MPEG_4 = "MPEG-4";
public static final String F_3GPP = "3GPP";
public static final String F_WEBM = "WebM";
public static final String F_M4A = "m4a";
public static final String F_WEBMA = "WebM";
// file suffix
public static final String C_MPEG_4 = "mp4";
public static final String C_3GPP = "3gp";
public static final String C_WEBM = "webm";
public static final String C_M4A = "m4a";
public static final String C_WEBMA = "webm";
// mimeType
public static final String M_MPEG_4 = "video/mp4";
public static final String M_3GPP = "video/3gpp";
public static final String M_WEBM = "video/webm";
public static final String M_M4A = "audio/mp4";
public static final String M_WEBMA = "audio/webm";
public static final int VIDEO_AVAILABLE = 0x00;
public static final int VIDEO_UNAVAILABLE = 0x01;
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation; sound pretty draconian
public static String getNameById(int id) {
switch(id) {
case I_MPEG_4: return F_MPEG_4;
case I_3GPP: return F_3GPP;
case I_WEBM: return F_WEBM;
case I_M4A: return F_M4A;
case I_WEBMA: return F_WEBMA;
default: formatNotKnown(id);
}
return "";
}
public static String getSuffixById(int id) {
switch(id) {
case I_MPEG_4: return C_MPEG_4;
case I_3GPP: return C_3GPP;
case I_WEBM: return C_WEBM;
case I_M4A: return C_M4A;
case I_WEBMA: return C_WEBMA;
default: formatNotKnown(id);
}
return "";
}
public static String getMimeById(int id) {
switch(id) {
case I_MPEG_4: return M_MPEG_4;
case I_3GPP: return M_3GPP;
case I_WEBM: return M_WEBM;
case I_M4A: return M_M4A;
case I_WEBMA: return M_WEBMA;
default: formatNotKnown(id);
}
return "";
}
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
public static class VideoStream {
public VideoStream(String url, int format, String res) {
@ -106,11 +66,6 @@ public class VideoInfo {
public String resolution = "";
}
protected static void formatNotKnown(int id) {
Log.e(TAG, "format not known: \"" +
Integer.toString(id) + "\". Call the programmers, they messed it up!");
}
public static class AudioStream {
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
this.url = url; this.format = format;
@ -120,28 +75,5 @@ public class VideoInfo {
public int format = -1;
public int bandwidth = -1;
public int samplingRate = -1;
}
public String id = "";
public String uploader = "";
public String upload_date = "";
public String uploader_thumbnail_url = "";
public Bitmap uploader_thumbnail = null;
public String title = "";
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String description = "";
public int duration = -1;
public int age_limit = 0;
public String webpage_url = "";
public String view_count = "";
public String like_count = "";
public String dislike_count = "";
public String average_rating = "";
public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null;
public VideoInfoItem nextVideo = null;
public VideoInfoItem[] relatedVideos = null;
public int videoAvailableStatus = VIDEO_AVAILABLE;
}

View File

@ -28,13 +28,14 @@ public class VideoInfoItem implements Parcelable {
public String id = "";
public String title = "";
public String uploader = "";
public String duration = "";
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String webpage_url = "";
public String upload_date = "";
public String view_count = "";
public String duration = "";
protected VideoInfoItem(Parcel in) {
id = in.readString();
title = in.readString();

View File

@ -51,7 +51,7 @@ public class VideoInfoItemViewCreator {
}
if(info.thumbnail == null) {
holder.itemThumbnailView.setImageResource(R.drawable.dummi_thumbnail);
holder.itemThumbnailView.setImageResource(R.drawable.dummy_thumbnail);
} else {
holder.itemThumbnailView.setImageBitmap(info.thumbnail);
}

View File

@ -5,6 +5,7 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -60,6 +61,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
// this means the video was called though another app
if (getIntent().getData() != null) {
videoUrl = getIntent().getData().toString();
Log.i(TAG, "video URL passed:\"" + videoUrl + "\"");
StreamingService[] serviceList = ServiceList.getServices();
Extractor extractor = null;
for (int i = 0; i < serviceList.length; i++) {
@ -67,8 +69,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
try {
currentStreamingService = i;
extractor = ServiceList.getService(i)
.getExtractorInstance();
extractor = ServiceList.getService(i).getExtractorInstance();
} catch (Exception e) {
e.printStackTrace();
}

View File

@ -2,6 +2,7 @@ package org.schabi.newpipe;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
@ -29,6 +30,11 @@ import android.widget.TextView;
import android.view.MenuItem;
import java.net.URL;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Vector;
@ -216,12 +222,31 @@ public class VideoItemDetailFragment extends Fragment {
case VideoInfo.VIDEO_AVAILABLE: {
videoTitleView.setText(info.title);
uploaderView.setText(info.uploader);
viewCountView.setText(info.view_count
Locale locale = getPreferredLocale();
NumberFormat nf = NumberFormat.getInstance(locale);
String localisedViewCount = nf.format(info.view_count);
viewCountView.setText(localisedViewCount
+ " " + activity.getString(R.string.viewSufix));
thumbsUpView.setText(info.like_count);
thumbsDownView.setText(info.dislike_count);
thumbsUpView.setText(nf.format(info.like_count));
thumbsDownView.setText(nf.format(info.dislike_count));
//this is horribly convoluted
//TODO: find a better way to convert YYYY-MM-DD to a locale-specific date
//suggestions welcome
int year = Integer.parseInt(info.upload_date.substring(0, 4));
int month = Integer.parseInt(info.upload_date.substring(5, 7));
int date = Integer.parseInt(info.upload_date.substring(8, 10));
Calendar cal = Calendar.getInstance();
cal.set(year, month, date);
Date datum = cal.getTime();
DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String localisedDate = df.format(datum);
uploadDateView.setText(
activity.getString(R.string.uploadDatePrefix) + " " + info.upload_date);
activity.getString(R.string.uploadDatePrefix) + " " + localisedDate);
descriptionView.setText(Html.fromHtml(info.description));
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
@ -369,6 +394,23 @@ public class VideoItemDetailFragment extends Fragment {
}
}
public Locale getPreferredLocale() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
String languageKey = getContext().getString(R.string.searchLanguage);
String languageCode = "en";//i know the following lines defaults languageCode to "en", but java is picky about uninitialised values
languageCode = sp.getString(languageKey, "en");
if(languageCode.length() == 2) {
return new Locale(languageCode);
}
else if(languageCode.contains("_")) {
String country = languageCode
.substring(languageCode.indexOf("_"), languageCode.length());
return new Locale(languageCode.substring(0, 2), country);
}
return Locale.getDefault();
}
public boolean checkIfLandscape() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

View File

@ -103,7 +103,7 @@ public class VideoItemListActivity extends AppCompatActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_videoitem_list);
//-------- remove this line when multiservice support is implemented ----------
//------ todo: remove this line when multiservice support is implemented ------
currentStreamingServiceId = ServiceList.getIdOfService("Youtube");
//-----------------------------------------------------------------------------

View File

@ -93,10 +93,10 @@ public class VideoItemListFragment extends ListFragment {
public void run() {
try {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
String contentCountryKey = getContext().getString(R.string.contentCountry);
String contentCountry = sp.getString(contentCountryKey, "");
SearchEngine.Result result = engine.search(query, page, contentCountry);
Log.i(TAG, "countryCode passed:\""+contentCountry+"\"");
String searchLanguageKey = getContext().getString(R.string.searchLanguage);
String searchLanguage = sp.getString(searchLanguageKey, "en");
SearchEngine.Result result = engine.search(query, page, searchLanguage);
Log.i(TAG, "language code passed:\""+searchLanguage+"\"");
if(run) {
h.post(new ResultRunnable(result, requestId));
}

View File

@ -3,7 +3,6 @@ package org.schabi.newpipe.youtube;
import android.util.Log;
import android.util.Xml;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@ -14,12 +13,14 @@ import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.Extractor;
import org.schabi.newpipe.MediaFormat;
import org.schabi.newpipe.VideoInfo;
import org.schabi.newpipe.VideoInfoItem;
import org.xmlpull.v1.XmlPullParser;
import java.io.StringReader;
import java.net.URI;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
@ -57,16 +58,16 @@ public class YoutubeExtractor implements Extractor {
public static int resolveFormat(int itag) {
switch(itag) {
// video
case 17: return VideoInfo.I_3GPP;
case 18: return VideoInfo.I_MPEG_4;
case 22: return VideoInfo.I_MPEG_4;
case 36: return VideoInfo.I_3GPP;
case 37: return VideoInfo.I_MPEG_4;
case 38: return VideoInfo.I_MPEG_4;
case 43: return VideoInfo.I_WEBM;
case 44: return VideoInfo.I_WEBM;
case 45: return VideoInfo.I_WEBM;
case 46: return VideoInfo.I_WEBM;
case 17: return MediaFormat.v3GPP.id;
case 18: return MediaFormat.MPEG_4.id;
case 22: return MediaFormat.MPEG_4.id;
case 36: return MediaFormat.v3GPP.id;
case 37: return MediaFormat.MPEG_4.id;
case 38: return MediaFormat.MPEG_4.id;
case 43: return MediaFormat.WEBM.id;
case 44: return MediaFormat.WEBM.id;
case 45: return MediaFormat.WEBM.id;
case 46: return MediaFormat.WEBM.id;
default:
//Log.i(TAG, "Itag " + Integer.toString(itag) + " not known or not supported.");
return -1;
@ -113,7 +114,7 @@ public class YoutubeExtractor implements Extractor {
JSONObject jsonObj = new JSONObject(jsonString);
//----------------------------------
// load an parse description code
// load and parse description code
//----------------------------------
if (decryptionCode.isEmpty()) {
JSONObject ytAssets = jsonObj.getJSONObject("assets");
@ -132,38 +133,27 @@ public class YoutubeExtractor implements Extractor {
@Override
public String getVideoId(String videoUrl) {
try {
URI uri = new URI(videoUrl);
if(uri.getHost().contains("youtube")) {
String query = uri.getFragment();
if(query == null) {
query = uri.getQuery();
} else {
query = query.replace("/watch?", "");
}
String queryElements[] = query.split("&");
Map<String, String> queryArguments = new HashMap<>();
for (String e : queryElements) {
String[] s = e.split("=");
queryArguments.put(s[0], s[1]);
}
return queryArguments.get("v");
} else if(uri.getHost().contains("youtu.be")) {
// uri.getRawPath() does somehow not return the last character.
// so we do a workaround instead.
//return uri.getRawPath();
String url[] = videoUrl.split("/");
return url[url.length-1];
} else {
Log.e(TAG, "Error could not parse url: " + videoUrl);
String id = "";
Pattern pat;
}
} catch(Exception e) {
if(videoUrl.contains("youtube")) {
pat = Pattern.compile("youtube\\.com/watch\\?v=([\\-a-zA-Z0-9_]{11})");
}
else if(videoUrl.contains("youtu.be")) {
pat = Pattern.compile("youtu\\.be/([a-zA-Z0-9_-]{11})");
}
else {
Log.e(TAG, "Error could not parse url: " + videoUrl);
e.printStackTrace();
return "";
}
return null;
Matcher mat = pat.matcher(videoUrl);
boolean foundMatch = mat.find();
if(foundMatch){
id = mat.group(1);
Log.i(TAG, "string \""+videoUrl+"\" matches!");
}
Log.i(TAG, "string \""+videoUrl+"\" does not match.");
return id;
}
@Override
@ -178,12 +168,11 @@ public class YoutubeExtractor implements Extractor {
Document doc = Jsoup.parse(site, siteUrl);
videoInfo.id = matchGroup1("v=([0-9a-zA-Z]*)", siteUrl);
videoInfo.id = matchGroup1("v=([0-9a-zA-Z_-]{11})", siteUrl);
videoInfo.age_limit = 0;
videoInfo.webpage_url = siteUrl;
initService(site);
//-------------------------------------
@ -224,7 +213,7 @@ public class YoutubeExtractor implements Extractor {
videoInfo.uploader = playerArgs.getString("author");
videoInfo.title = playerArgs.getString("title");
//first attempt gating a small image version
//first attempt getting a small image version
//in the html extracting part we try to get a thumbnail with a higher resolution
videoInfo.thumbnail_url = playerArgs.getString("thumbnail_url");
videoInfo.duration = playerArgs.getInt("length_seconds");
@ -243,7 +232,7 @@ public class YoutubeExtractor implements Extractor {
}
int itag = Integer.parseInt(tags.get("itag"));
String streamUrl = terrible_unescape_workaround_fuck(tags.get("url"));
String streamUrl = URLDecoder.decode(tags.get("url"), "UTF-8");
// if video has a signature: decrypt it and add it to the url
if(tags.get("s") != null) {
@ -281,35 +270,37 @@ public class YoutubeExtractor implements Extractor {
videoInfo.thumbnail_url = doc.select("link[itemprop=\"thumbnailUrl\"]").first()
.attr("abs:href");
} catch(Exception e) {
Log.i(TAG, "Could not find high res Thumbnail. Use low res instead");
Log.i(TAG, "Could not find high res Thumbnail. Using low res instead");
}
// upload date
videoInfo.upload_date = doc.select("strong[class=\"watch-time-text\"").first()
.text();
videoInfo.upload_date = doc.select("meta[itemprop=datePublished]").attr("content");
//TODO: Format date locale-specifically
// Try to only use date not the text around it
videoInfo.upload_date = matchGroup1("([0-9.]*$)", videoInfo.upload_date);
// description
videoInfo.description = doc.select("p[id=\"eow-description\"]").first()
.html();
videoInfo.description = doc.select("p[id=\"eow-description\"]").first().html();
String likesString = "";
String dislikesString = "";
try {
// likes
videoInfo.like_count = doc.select("span[class=\"like-button-renderer \"]").first()
.getAllElements().select("button")
.select("span").get(0).text();
likesString = doc.select("button.like-button-renderer-like-button").first()
.select("span.yt-uix-button-content").first().text();
videoInfo.like_count = Integer.parseInt(likesString.replaceAll("[^\\d]", ""));
// dislikes
videoInfo.dislike_count = doc.select("span[class=\"like-button-renderer \"]").first()
.getAllElements().select("button")
.select("span").get(2).text();
dislikesString = doc.select("button.like-button-renderer-dislike-button").first()
.select("span.yt-uix-button-content").first().text();
videoInfo.dislike_count = Integer.parseInt(dislikesString.replaceAll("[^\\d]", ""));
} catch(NumberFormatException nfe) {
Log.e(TAG, "failed to parse likesString \""+likesString+"\" and dislikesString \""+
dislikesString+"\" as integers");
} catch(Exception e) {
// if it fails we know that the video does not offer dislikes.
videoInfo.like_count = "0";
videoInfo.dislike_count = "0";
e.printStackTrace();
videoInfo.like_count = 0;
videoInfo.dislike_count = 0;
}
// uploader thumbnail
@ -317,21 +308,20 @@ public class YoutubeExtractor implements Extractor {
.select("img").first()
.attr("abs:data-thumb");
// view count
videoInfo.view_count = doc.select("div[class=\"watch-view-count\"]").first().text();
// view count TODO: locale-specific formatting
String viewCountString = doc.select("meta[itemprop=interactionCount]").attr("content");
videoInfo.view_count = Integer.parseInt(viewCountString);
// next video
videoInfo.nextVideo = extractVideoInfoItem(doc.select("div[class=\"watch-sidebar-section\"]").first()
.select("li").first());
int i = 0;
// related videos
Vector<VideoInfoItem> relatedVideos = new Vector<>();
for(Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
// first check if we have a playlist. If so leave them out
if(li.select("a[class*=\"content-link\"]").first() != null) {
relatedVideos.add(extractVideoInfoItem(li));
i++;
}
}
videoInfo.relatedVideos = relatedVideos.toArray(new VideoInfoItem[relatedVideos.size()]);
@ -377,13 +367,13 @@ public class YoutubeExtractor implements Extractor {
if(currentTagIsBaseUrl &&
(currentMimeType.contains("audio"))) {
int format = -1;
if(currentMimeType.equals(VideoInfo.M_WEBMA)) {
format = VideoInfo.I_WEBMA;
} else if(currentMimeType.equals(VideoInfo.M_M4A)) {
format = VideoInfo.I_M4A;
if(currentMimeType.equals(MediaFormat.WEBMA.mimeType)) {
format = MediaFormat.WEBMA.id;
} else if(currentMimeType.equals(MediaFormat.M4A.mimeType)) {
format = MediaFormat.M4A.id;
}
audioStreams.add(new VideoInfo.AudioStream(parser.getText(),
format, currentBandwidth, currentSamplingRate));
format, currentBandwidth, currentSamplingRate));
}
case XmlPullParser.END_TAG:
if(tagName.equals("AdaptationSet")) {
@ -412,6 +402,7 @@ public class YoutubeExtractor implements Extractor {
e.printStackTrace();
}
//todo: check NullPointerException causing
info.title = li.select("span[class=\"title\"]").first().text();
info.view_count = li.select("span[class*=\"view-count\"]").first().text();
info.uploader = li.select("span[class=\"g-hovercard\"]").first().text();
@ -431,19 +422,6 @@ public class YoutubeExtractor implements Extractor {
return info;
}
private String terrible_unescape_workaround_fuck(String shit) {
String[] splitAtEscape = shit.split("%");
String retval = "";
retval += splitAtEscape[0];
for(int i = 1; i < splitAtEscape.length; i++) {
String escNum = splitAtEscape[i].substring(0, 2);
char c = (char) Integer.parseInt(escNum,16);
retval += c;
retval += splitAtEscape[i].substring(2);
}
return retval;
}
private String loadDecryptionCode(String playerUrl) {
String playerCode = Downloader.download(playerUrl);
String decryptionFuncName = "";
@ -456,12 +434,13 @@ public class YoutubeExtractor implements Extractor {
try {
decryptionFuncName = matchGroup1("\\.sig\\|\\|([a-zA-Z0-9$]+)\\(", playerCode);
String functionPattern = "(function " + decryptionFuncName.replace("$", "\\$") + "\\([a-zA-Z0-9_]*\\)\\{.+?\\})";
String functionPattern = "(var "+ decryptionFuncName.replace("$", "\\$") +"=function\\([a-zA-Z0-9_]*\\)\\{.+?\\})";
decryptionFunc = matchGroup1(functionPattern, playerCode);
decryptionFunc += ";";
helperObjectName = matchGroup1(";([A-Za-z0-9_\\$]{2})\\...\\(", decryptionFunc);
String helperPattern = "(var " + helperObjectName.replace("$", "\\$") + "=\\{.+?\\}\\};)function";
String helperPattern = "(var " + helperObjectName.replace("$", "\\$") + "=\\{.+?\\}\\};)";
helperObject = matchGroup1(helperPattern, playerCode);
} catch (Exception e) {
@ -474,13 +453,13 @@ public class YoutubeExtractor implements Extractor {
return decryptionCode;
}
private String decryptSignature(String encryptedSig, String decryptoinCode) {
private String decryptSignature(String encryptedSig, String decryptionCode) {
Context context = Context.enter();
context.setOptimizationLevel(-1);
Object result = null;
try {
ScriptableObject scope = context.initStandardObjects();
context.evaluateString(scope, decryptoinCode, "decryptionCode", 1, null);
context.evaluateString(scope, decryptionCode, "decryptionCode", 1, null);
Function decryptionFunc = (Function) scope.get("decrypt", scope);
result = decryptionFunc.call(context, scope, scope, new Object[]{encryptedSig});
} catch (Exception e) {
@ -498,7 +477,7 @@ public class YoutubeExtractor implements Extractor {
return mat.group(1);
}
else {
Log.e(TAG, "failed to find pattern \""+pattern+"\"");
Log.e(TAG, "failed to find pattern \""+pattern+"\" inside of \""+input+"\"");
new Exception("failed to find pattern \""+pattern+"\"").printStackTrace();
return "";
}

View File

@ -49,7 +49,7 @@ public class YoutubeSearchEngine implements SearchEngine {
private static final String TAG = YoutubeSearchEngine.class.toString();
@Override
public Result search(String query, int page, String countryCode) {
public Result search(String query, int page, String languageCode) {
//String contentCountry = PreferenceManager.getDefaultSharedPreferences(this).getString(getString(R.string., "");
Uri.Builder builder = new Uri.Builder();
builder.scheme("https")
@ -59,20 +59,18 @@ public class YoutubeSearchEngine implements SearchEngine {
.appendQueryParameter("page", Integer.toString(page))
.appendQueryParameter("filters", "video");
//if we've been passed a valid, non-empty country code, append it to the URL
if(countryCode.length() > 0) {
if(countryCode.length() == 2) {
builder.appendQueryParameter("gl", countryCode);
builder.appendQueryParameter("persist_gl", "1");
Log.i(TAG, "URI: \""+builder+"\"");
}
else {
Log.e(TAG, "invalid country code passed to search(): \""+countryCode+"\"");
}
}
String site;
String url = builder.build().toString();
//if we've been passed a valid language code, append it to the URL
if(languageCode.length() > 0) {
//assert Pattern.matches("[a-z]{2}(-([A-Z]{2}|[0-9]{1,3}))?", languageCode);
site = Downloader.download(url, languageCode);
}
else {
site = Downloader.download(url);
}
String site = Downloader.download(url);
Document doc = Jsoup.parse(site, url);
Result result = new Result();
Element list = doc.select("ol[class=\"item-section\"]").first();

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

View File

@ -33,7 +33,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:src="@drawable/dummi_thumbnail"/>
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/detailVideoTitleView"
android:layout_width="wrap_content"
@ -53,7 +53,7 @@
android:paddingRight="5dp"
android:layout_below="@id/detailVideoTitleView"
android:layout_alignParentLeft="true"
android:src="@drawable/budy" />
android:src="@drawable/buddy" />
<TextView android:id="@+id/detailUploaderView"
android:layout_width="wrap_content"

View File

@ -33,7 +33,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:src="@drawable/dummi_thumbnail"/>
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/detailVideoTitleView"
android:layout_width="wrap_content"
@ -53,7 +53,7 @@
android:paddingRight="5dp"
android:layout_below="@id/detailVideoTitleView"
android:layout_alignParentLeft="true"
android:src="@drawable/budy" />
android:src="@drawable/buddy" />
<TextView android:id="@+id/detailUploaderView"
android:layout_width="wrap_content"

View File

@ -33,7 +33,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:src="@drawable/dummi_thumbnail"/>
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/detailVideoTitleView"
android:layout_width="wrap_content"
@ -42,19 +42,21 @@
android:layout_alignParentLeft="true"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingBottom="20dp"
android:paddingBottom="0dp"
android:paddingTop="3dp"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Bla blabla !!!"/>
android:text="Video title placeholder"/>
<ImageView android:id="@+id/detailUploaderThumbnailView"
android:layout_width="85dp"
android:layout_height="100dp"
android:paddingTop="25dp"
android:paddingTop="0dp"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:layout_below="@id/detailVideoTitleView"
android:layout_alignParentLeft="true"
android:src="@drawable/budy" />
android:src="@drawable/buddy" />
<TextView android:id="@+id/detailUploaderView"
android:layout_width="wrap_content"
@ -63,19 +65,22 @@
android:layout_alignParentLeft="true"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Herr von Gurken" />
android:textStyle="bold"
android:textSize="@dimen/text_video_uploader_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="username" />
<TextView android:id="@+id/detailViewCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="25dp"
android:paddingTop="6dp"
android:layout_below="@id/detailVideoTitleView"
android:layout_alignParentRight="true"
android:paddingRight="16dp"
android:paddingLeft="16dp"
android:textSize="@dimen/text_video_visits_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="drölf views" />
android:text="81,754 views" />
<TextView android:id="@+id/detailThumbsDownCountView"
android:layout_width="wrap_content"
@ -84,8 +89,9 @@
android:layout_alignParentRight="true"
android:paddingRight="16dp"
android:paddingLeft="16dp"
android:textSize="@dimen/text_video_like_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="-5.000" />
android:text="100" />
<ImageView android:id="@+id/detailThumbsDownImgView"
android:layout_width="40dp"
@ -99,8 +105,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/detailViewCountView"
android:layout_toLeftOf="@id/detailThumbsDownImgView"
android:textSize="@dimen/text_video_like_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="" />
android:text="20" />
<ImageView android:id="@+id/detailThumbsUpImgView"
android:layout_width="40dp"
@ -116,17 +123,20 @@
android:layout_alignParentLeft="true"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="20dp"
android:paddingTop="0dp"
android:textSize="@dimen/text_video_uploadtime_size"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Uploaded at: 45.64.1285" />
android:text="Published on Jan 01 1975" />
<TextView android:id="@+id/detailDescriptionView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/detailUploadDateView"
android:layout_alignParentLeft="true"
android:paddingTop="3dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textSize="@dimen/text_video_description_size"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmodtempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. "
/>
@ -209,5 +219,5 @@
android:layout_height="wrap_content"
app:backgroundTint="@color/primaryColorYoutube"
android:src="@drawable/ic_play_arrow_black"
android:layout_margin="16dip"/>
android:layout_margin="20dp"/>
</RelativeLayout>

View File

@ -12,7 +12,7 @@
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="@drawable/dummi_thumbnail"/>
android:src="@drawable/dummy_thumbnail"/>
<TextView android:id="@+id/itemVideoTitleView"
android:layout_width="wrap_content"

View File

@ -1,16 +1,17 @@
<?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/menu_item_play"
android:title="@string/play"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_play_arrow_black"/>
<item android:id="@+id/menu_item_play_audio"
android:title="@string/playAudio"
app:showAsAction="always"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_headset_black" />
<item android:id="@+id/menu_item_download"
app:showAsAction="ifRoom"
android:title="@string/download"
android:icon="@drawable/ic_file_download_black"/>
<item android:id="@+id/menu_item_share"
android:title="@string/share"
app:showAsAction="ifRoom"
@ -25,10 +26,6 @@
app:showAsAction="never"
android:title="@string/open_in_browser" />
<item android:id="@+id/menu_item_download"
app:showAsAction="never"
android:title="@string/download"/>
<item android:id="@+id/action_settings"
app:showAsAction="never"
android:title="@string/settings"/>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primaryColorYoutube">#dd0000</color>
<color name="primaryColorDarkYoutube">#bb0000</color>
<color name="primaryColorYoutube">#cd322e</color>
<color name="primaryColorDarkYoutube">#bc211d</color>
<color name="accentColorYoutube">#000000</color>
<color name="black_overlay">#66000000</color>
</resources>

View File

@ -4,4 +4,10 @@
<dimen name="text_search_duration_size">11sp</dimen>
<dimen name="text_search_uploader_size">12sp</dimen>
<dimen name="text_search_uploadtime_size">12sp</dimen>
<dimen name="text_video_title_size">14sp</dimen>
<dimen name="text_video_visits_size">14sp</dimen>
<dimen name="text_video_like_size">12sp</dimen>
<dimen name="text_video_uploader_size">14sp</dimen>
<dimen name="text_video_uploadtime_size">14sp</dimen>
<dimen name="text_video_description_size">14sp</dimen>
</resources>

View File

@ -24,183 +24,163 @@
</string-array>
<string name="defaultAudioFormat">m4a</string>
<string name="showNextVideo">show_next_video</string>
<string name="contentCountry">content_country</string>
<string name="searchLanguage">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="countryCodes">
<item></item><!--NONE-->
<item>US</item>
<item>DZ</item>
<item>AR</item>
<item>AU</item>
<item>AT</item>
<item>AZ</item>
<item>BH</item>
<item>BY</item>
<item>BE</item>
<item>BA</item>
<item>BR</item>
<item>BG</item>
<item>CA</item>
<item>CL</item>
<item>CO</item>
<item>HR</item>
<item>CZ</item>
<item>DK</item>
<item>EG</item>
<item>EE</item>
<item>FI</item>
<item>FR</item>
<item>GE</item>
<item>DE</item>
<item>GH</item>
<item>GR</item>
<item>HK</item>
<item>HU</item>
<item>IS</item>
<item>IN</item>
<item>ID</item>
<item>IE</item>
<item>IL</item>
<item>IT</item>
<item>JP</item>
<item>JO</item>
<item>KZ</item>
<item>KE</item>
<item>KW</item>
<item>LV</item>
<item>LB</item>
<item>LY</item>
<item>LT</item>
<item>LU</item>
<item>MK</item>
<item>MY</item>
<item>MX</item>
<item>ME</item>
<item>MA</item>
<item>NL</item>
<item>NZ</item>
<item>NG</item>
<item>NO</item>
<item>OM</item>
<item>PE</item>
<item>PH</item>
<item>PL</item>
<item>PT</item>
<item>PR</item>
<item>QA</item>
<item>RO</item>
<item>RU</item>
<item>SA</item>
<item>SN</item>
<item>RS</item>
<item>SG</item>
<item>SK</item>
<item>SI</item>
<item>ZA</item>
<item>KR</item>
<item>ES</item>
<item>SE</item>
<item>CH</item>
<item>TW</item>
<item>TZ</item>
<item>TH</item>
<item>TN</item>
<item>TR</item>
<item>UG</item>
<item>UA</item>
<item>AE</item>
<item>GB</item>
<item>VN</item>
<item>YE</item>
<item>ZW</item>
<string-array name='languageCodes'>
<item>af</item>
<item>az</item>
<item>id</item>
<item>ms</item>
<item>ca</item>
<item>cs</item>
<item>da</item>
<item>de</item>
<item>et</item>
<item>en-GB</item>
<item>en</item>
<item>es</item>
<item>es-419</item>
<item>eu</item>
<item>fil</item>
<item>fr</item>
<item>fr-CA</item>
<item>gl</item>
<item>hr</item>
<item>zu</item>
<item>is</item>
<item>it</item>
<item>sw</item>
<item>lv</item>
<item>lt</item>
<item>hu</item>
<item>nl</item>
<item>no</item>
<item>uz</item>
<item>pl</item>
<item>pt-PT</item>
<item>pt</item>
<item>ro</item>
<item>sq</item>
<item>sk</item>
<item>sl</item>
<item>fi</item>
<item>sv</item>
<item>vi</item>
<item>tr</item>
<item>bg</item>
<item>ky</item>
<item>kk</item>
<item>mk</item>
<item>mn</item>
<item>ru</item>
<item>sr</item>
<item>uk</item>
<item>el</item>
<item>hy</item>
<item>iw</item>
<item>ur</item>
<item>ar</item>
<item>fa</item>
<item>ne</item>
<item>mr</item>
<item>hi</item>
<item>bn</item>
<item>pa</item>
<item>gu</item>
<item>ta</item>
<item>te</item>
<item>kn</item>
<item>ml</item>
<item>si</item>
<item>th</item>
<item>lo</item>
<item>my</item>
<item>ka</item>
<item>am</item>
<item>km</item>
<item>zh-CN</item>
<item>zh-TW</item>
<item>zh-HK</item>
<item>ja</item>
<item>ko</item>
</string-array>
<string-array name="countryNames">
<item>(None)</item>
<item>Worldwide (USA)</item>
<item>Algeria</item>
<item>Argentina</item>
<item>Australia</item>
<item>Austria</item>
<item>Azerbaijan</item>
<item>Bahrain</item>
<item>Belarus</item>
<item>Belgium</item>
<item>Bosnia and Herzegovina</item>
<item>Brazil</item>
<item>Bulgaria</item>
<item>Canada</item>
<item>Chile</item>
<item>Colombia</item>
<item>Croatia</item>
<item>Czech Republic</item>
<item>Denmark</item>
<item>Egypt</item>
<item>Estonia</item>
<item>Finland</item>
<item>France</item>
<item>Georgia</item>
<item>Germany</item>
<item>Ghana</item>
<item>Greece</item>
<item>Hong Kong</item>
<item>Hungary</item>
<item>Iceland</item>
<item>India</item>
<item>Indonesia</item>
<item>Ireland</item>
<item>Israel</item>
<item>Italy</item>
<item>Japan</item>
<item>Jordan</item>
<item>Kazakhstan</item>
<item>Kenya</item>
<item>Kuwait</item>
<item>Latvia</item>
<item>Lebanon</item>
<item>Libya</item>
<item>Lithuania</item>
<item>Luxembourg</item>
<item>Macedonia</item>
<item>Malaysia</item>
<item>Mexico</item>
<item>Montenegro</item>
<item>Morocco</item>
<item>Netherlands</item>
<item>New Zealand</item>
<item>Nigeria</item>
<item>Norway</item>
<item>Oman</item>
<item>Peru</item>
<item>Philippines</item>
<item>Poland</item>
<item>Portugal</item>
<item>Puerto Rico</item>
<item>Qatar</item>
<item>Romania</item>
<item>Russia</item>
<item>Saudi Arabia</item>
<item>Senegal</item>
<item>Serbia</item>
<item>Singapore</item>
<item>Slovakia</item>
<item>Slovenia</item>
<item>South Africa</item>
<item>South Korea</item>
<item>Spain</item>
<item>Sweden</item>
<item>Switzerland</item>
<item>Taiwan</item>
<item>Tanzania</item>
<item>Thailand</item>
<item>Tunisia</item>
<item>Turkey</item>
<item>Uganda</item>
<item>Ukraine</item>
<item>United Arab Emirates</item>
<item>United Kingdom</item>
<item>Vietnam</item>
<item>Yemen</item>
<item>Zimbabwe</item>
<string-array name='languageNames'>
<item>Afrikaans</item>
<item>Azərbaycan</item>
<item>Bahasa Indonesia</item>
<item>Bahasa Malaysia</item>
<item>Català</item>
<item>Čeština</item>
<item>Dansk</item>
<item>Deutsch</item>
<item>Eesti</item>
<item>English (UK)</item>
<item>English (US)</item>
<item>Español (España)</item>
<item>Español (Latinoamérica)</item>
<item>Euskara</item>
<item>Filipino</item>
<item>Français</item>
<item>Français (Canada)</item>
<item>Galego</item>
<item>Hrvatski</item>
<item>IsiZulu</item>
<item>Íslenska</item>
<item>Italiano</item>
<item>Kiswahili</item>
<item>Latviešu valoda</item>
<item>Lietuvių</item>
<item>Magyar</item>
<item>Nederlands</item>
<item>Norsk</item>
<item>Ozbek</item>
<item>Polski</item>
<item>Português</item>
<item>Português (Brasil)</item>
<item>Română</item>
<item>Shqip</item>
<item>Slovenčina</item>
<item>Slovenščina</item>
<item>Suomi</item>
<item>Svenska</item>
<item>Tiếng Việt</item>
<item>Türkçe</item>
<item>Български</item>
<item>Кыргызча</item>
<item>Қазақ Тілі</item>
<item>Македонски</item>
<item>Монгол</item>
<item>Русский</item>
<item>Српски</item>
<item>Українська</item>
<item>Ελληνικά</item>
<item>Հայերեն</item>
<item>עברית</item>
<item>اردو</item>
<item>العربية</item>
<item>فارسی</item>
<item>नेपाली</item>
<item>मराठी</item>
<item>हिन्दी</item>
<item>বাংলা</item>
<item>ਪੰਜਾਬੀ</item>
<item>ગુજરાતી</item>
<item>தமிழ்</item>
<item>తెలుగు</item>
<item>ಕನ್ನಡ</item>
<item>മലയാളം</item>
<item>සිංහල</item>
<item>ภาษาไทย</item>
<item>ລາວ</item>
<item>ဗမာ</item>
<item>ქართული</item>
<item>አማርኛ</item>
<item>ខ្មែរ</item>
<item>中文 (简体)</item>
<item>中文 (繁體)</item>
<item>中文 (香港)</item>
<item>日本語</item>
<item>한국어</item>
</string-array>
</resources>

View File

@ -4,7 +4,7 @@
<string name="title_videoitem_detail">NewPipe</string>
<string name="nothingFound">Nothing found</string>
<string name="viewSufix">views</string>
<string name="uploadDatePrefix">Uploaded at: </string>
<string name="uploadDatePrefix">Published on </string>
<string name="noPlayerFound">No StreamPlayer found. You may want to install one.</string>
<string name="installStreamPlayer">Install one</string>
<string name="cancel">Cancel</string>
@ -45,9 +45,9 @@
<item>Video</item>
<item>Audio</item>
</string-array>
<string name="nextVideoTitle">Next Video</string>
<string name="showNextAndSimilarTitle">Show next and similar Videos.</string>
<string name="urlNotSupportedText">Url not Supported.</string>
<string name="showSimilarVideosButtonText">Similar Videos</string>
<string name="contentCountryTitle">Video Content Country</string>
<string name="nextVideoTitle">Next video</string>
<string name="showNextAndSimilarTitle">Show next and similar videos</string>
<string name="urlNotSupportedText">URL not supported.</string>
<string name="showSimilarVideosButtonText">Similar videos</string>
<string name="searchLanguageTitle">Preferable content language</string>
</resources>

View File

@ -51,12 +51,11 @@
android:title="@string/showNextAndSimilarTitle"
android:defaultValue="true" />
<!-- This function is not yet available
<ListPreference
android:key="@string/contentCountry"
android:title="@string/contentCountryTitle"
android:entries="@array/countryNames"
android:entryValues="@array/countryCodes"
android:defaultValue="" /> <!- default will include no country code in URL-->
-->
android:key="@string/searchLanguage"
android:title="@string/searchLanguageTitle"
android:entries="@array/languageNames"
android:entryValues="@array/languageCodes"
android:defaultValue="en" />
</PreferenceScreen>