diff --git a/.idea/misc.xml b/.idea/misc.xml
index 5d1998103..fbb68289f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -37,7 +37,7 @@
-
+
diff --git a/app/build.gradle b/app/build.gradle
index 5662b2efc..511082f23 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 23
- versionCode 2
- versionName "0.3.5"
+ versionCode 3
+ versionName "0.4.0"
}
buildTypes {
release {
diff --git a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
index 201757f67..0be35c11f 100644
--- a/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/ActionBarHandler.java
@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
+import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.support.v4.view.MenuItemCompat;
@@ -50,6 +51,7 @@ public class ActionBarHandler {
private String webisteUrl = "";
private AppCompatActivity activity;
private VideoInfo.VideoStream[] videoStreams = null;
+ private VideoInfo.AudioStream audioStream = null;
private int selectedStream = -1;
private String videoTitle = "";
@@ -75,18 +77,18 @@ public class ActionBarHandler {
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
}
- public void setStreams(VideoInfo.VideoStream[] streams) {
- this.videoStreams = streams;
+ public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
+ this.videoStreams = videoStreams;
selectedStream = 0;
- String[] itemArray = new String[streams.length];
+ String[] itemArray = new String[videoStreams.length];
String defaultResolution = defaultPreferences
.getString(context.getString(R.string.defaultResolutionPreference),
context.getString(R.string.defaultResolutionListItem));
int defaultResolutionPos = 0;
- for(int i = 0; i < streams.length; i++) {
- itemArray[i] = streams[i].format + " " + streams[i].resolution;
- if(defaultResolution.equals(streams[i].resolution)) {
+ for(int i = 0; i < videoStreams.length; i++) {
+ itemArray[i] = VideoInfo.getNameById(videoStreams[i].format) + " " + videoStreams[i].resolution;
+ if(defaultResolution.equals(videoStreams[i].resolution)) {
defaultResolutionPos = i;
}
}
@@ -99,6 +101,27 @@ public class ActionBarHandler {
,new ForamatItemSelectListener());
ab.setSelectedNavigationItem(defaultResolutionPos);
}
+
+ // set audioStream
+ audioStream = null;
+ String preferedFormat = PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(context.getString(R.string.defaultAudioFormatPreference), "webm");
+ if(preferedFormat.equals("webm")) {
+ for(VideoInfo.AudioStream s : audioStreams) {
+ if(s.format == VideoInfo.I_WEBMA) {
+ 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 &&
+ (audioStream == null || audioStream.bandWidth > s.bandWidth)) {
+ audioStream = s;
+ Log.d(TAG, "last choosen");
+ }
+ }
+ }
}
private void selectFormatItem(int i) {
@@ -159,6 +182,9 @@ public class ActionBarHandler {
case R.id.action_play_with_kodi:
playWithKodi();
break;
+ case R.id.menu_item_play_audio:
+ playAudio();
+ break;
default:
Log.e(TAG, "Menu Item not known");
}
@@ -179,7 +205,7 @@ public class ActionBarHandler {
try {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(videoStreams[selectedStream].url),
- "video/" + videoStreams[selectedStream].format);
+ VideoInfo.getMimeById(videoStreams[selectedStream].format));
context.startActivity(intent); // HERE !!!
} catch (Exception e) {
e.printStackTrace();
@@ -216,29 +242,17 @@ public class ActionBarHandler {
public void downloadVideo() {
Log.d(TAG, "bla");
if(!videoTitle.isEmpty()) {
- String suffix = "";
- switch (videoStreams[selectedStream].format) {
- case VideoInfo.F_WEBM:
- suffix = ".webm";
- break;
- case VideoInfo.F_MPEG_4:
- suffix = ".mp4";
- break;
- case VideoInfo.F_3GPP:
- suffix = ".3gp";
- break;
- }
- DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
- DownloadManager.Request request = new DownloadManager.Request(
- Uri.parse(videoStreams[selectedStream].url));
- request.setDestinationUri(Uri.fromFile(new File(
- defaultPreferences.getString("download_path_preference", "/storage/emulated/0/NewPipe")
- + "/" + videoTitle + suffix)));
- try {
- dm.enqueue(request);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ String videoSuffix = "." + VideoInfo.getSuffixById(videoStreams[selectedStream].format);
+ String audioSuffix = "." + VideoInfo.getSuffixById(audioStream.format);
+ Bundle args = new Bundle();
+ args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
+ args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
+ args.putString(DownloadDialog.TITLE, videoTitle);
+ args.putString(DownloadDialog.VIDEO_URL, videoStreams[selectedStream].url);
+ args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
+ DownloadDialog downloadDialog = new DownloadDialog();
+ downloadDialog.setArguments(args);
+ downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
}
}
@@ -282,4 +296,34 @@ public class ActionBarHandler {
}
}
}
+
+ public void playAudio() {
+ Intent intent = new Intent();
+ try {
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(audioStream.url),
+ VideoInfo.getMimeById(audioStream.format));
+ context.startActivity(intent); // HERE !!!
+ } catch (Exception e) {
+ e.printStackTrace();
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setMessage(R.string.noPlayerFound)
+ .setPositiveButton(R.string.installStreamPlayer, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(context.getString(R.string.fdroidVLCurl)));
+ context.startActivity(intent);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.i(TAG, "You unlocked a secret unicorn.");
+ }
+ });
+ builder.create().show();
+ }
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/DownloadDialog.java
new file mode 100644
index 000000000..01479b642
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/DownloadDialog.java
@@ -0,0 +1,89 @@
+package org.schabi.newpipe;
+
+import android.app.Dialog;
+import android.app.DownloadManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Created by Christian Schabesberger on 21.09.15.
+ *
+ * Copyright (C) Christian Schabesberger 2015
+ * DownloadDialog.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 .
+ */
+
+public class DownloadDialog extends DialogFragment {
+ private static final String TAG = DialogFragment.class.getName();
+
+ public static final String TITLE = "name";
+ public static final String FILE_SUFFIX_AUDIO = "file_suffix_audio";
+ public static final String FILE_SUFFIX_VIDEO = "file_suffix_video";
+ public static final String AUDIO_URL = "audio_url";
+ public static final String VIDEO_URL = "video_url";
+ Bundle arguments;
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ arguments = getArguments();
+ super.onCreateDialog(savedInstanceState);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.downloadDialogTitle)
+ .setItems(R.array.downloadOptions, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Context context = getActivity();
+ SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ String suffix = "";
+ String title = arguments.getString(TITLE);
+ String url = "";
+ switch(which) {
+ case 0: // Video
+ suffix = arguments.getString(FILE_SUFFIX_VIDEO);
+ url = arguments.getString(VIDEO_URL);
+ break;
+ case 1:
+ suffix = arguments.getString(FILE_SUFFIX_AUDIO);
+ url = arguments.getString(AUDIO_URL);
+ break;
+ default:
+ Log.d(TAG, "lolz");
+ }
+ DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ DownloadManager.Request request = new DownloadManager.Request(
+ Uri.parse(url));
+ request.setDestinationUri(Uri.fromFile(new File(
+ defaultPreferences.getString("download_path_preference", "/storage/emulated/0/NewPipe")
+ + "/" + title + suffix)));
+ try {
+ dm.enqueue(request);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ return builder.create();
+ }
+
+}
diff --git a/app/src/main/java/org/schabi/newpipe/VideoInfo.java b/app/src/main/java/org/schabi/newpipe/VideoInfo.java
index 5dde64184..9b75be802 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoInfo.java
+++ b/app/src/main/java/org/schabi/newpipe/VideoInfo.java
@@ -21,35 +21,104 @@ package org.schabi.newpipe;
*/
import android.graphics.Bitmap;
+import android.util.Log;
import java.util.Vector;
public class VideoInfo {
+ 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;
+ 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: Log.e(TAG, "format not known: " +
+ Integer.toString(id) + "call the programmer he messed it up.");
+ }
+ 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: Log.e(TAG, "format not known: " +
+ Integer.toString(id) + "call the programmer he messed it up.");
+ }
+ 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: Log.e(TAG, "format not known: " +
+ Integer.toString(id) + "call the programmer he messed it up.");
+ }
+ return "";
+ }
+
public static class VideoStream {
- public VideoStream(String url, String format, String res) {
+ public VideoStream(String url, int format, String res) {
this.url = url; this.format = format; resolution = res;
}
public String url = ""; //url of the stream
- public String format = "";
+ public int format = -1;
public String resolution = "";
}
public static class AudioStream {
- public AudioStream(String url, String format) {
+ public AudioStream(String url, int format, int bandWidth, int samplingRate) {
this.url = url; this.format = format;
+ this.bandWidth = bandWidth; this.samplingRate = samplingRate;
}
public String url = "";
- public String format = "";
+ public int format = -1;
+ public int bandWidth = -1;
+ public int samplingRate = -1;
+
}
public String id = "";
diff --git a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
index 3da274d6b..6a3ebacfa 100644
--- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java
@@ -205,7 +205,7 @@ public class VideoItemDetailFragment extends Fragment {
for (int i = 0; i < streamList.length; i++) {
streamList[i] = streamsToUse.get(i);
}
- ActionBarHandler.getHandler().setStreams(streamList);
+ ActionBarHandler.getHandler().setStreams(streamList, info.audioStreams);
}
break;
case VideoInfo.VIDEO_UNAVAILABLE_GEMA:
diff --git a/app/src/main/java/org/schabi/newpipe/youtube/YoutubeExtractor.java b/app/src/main/java/org/schabi/newpipe/youtube/YoutubeExtractor.java
index acaf8aff9..f7188d8af 100644
--- a/app/src/main/java/org/schabi/newpipe/youtube/YoutubeExtractor.java
+++ b/app/src/main/java/org/schabi/newpipe/youtube/YoutubeExtractor.java
@@ -5,7 +5,9 @@ import org.schabi.newpipe.Extractor;
import org.schabi.newpipe.VideoInfo;
import android.util.Log;
+import android.util.Xml;
+import java.io.StringReader;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
@@ -22,6 +24,7 @@ import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.VideoInfoItem;
+import org.xmlpull.v1.XmlPullParser;
/**
* Created by Christian Schabesberger on 06.08.15.
@@ -53,22 +56,22 @@ public class YoutubeExtractor implements Extractor {
// How ever if you are heading for a list showing all itag formats lock at
// https://github.com/rg3/youtube-dl/issues/1687
- public static String resolveFormat(int itag) {
+ public static int resolveFormat(int itag) {
switch(itag) {
// video
- case 17: return VideoInfo.F_3GPP;
- case 18: return VideoInfo.F_MPEG_4;
- case 22: return VideoInfo.F_MPEG_4;
- case 36: return VideoInfo.F_3GPP;
- case 37: return VideoInfo.F_MPEG_4;
- case 38: return VideoInfo.F_MPEG_4;
- case 43: return VideoInfo.F_WEBM;
- case 44: return VideoInfo.F_WEBM;
- case 45: return VideoInfo.F_WEBM;
- case 46: return VideoInfo.F_WEBM;
+ 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;
default:
//Log.i(TAG, "Itag " + Integer.toString(itag) + " not known or not supported.");
- return null;
+ return -1;
}
}
@@ -153,6 +156,7 @@ public class YoutubeExtractor implements Extractor {
//-------------------------------------
JSONObject playerArgs = null;
JSONObject ytAssets = null;
+ String dashManifest = "";
{
Pattern p = Pattern.compile("ytplayer.config\\s*=\\s*(\\{.*?\\});");
Matcher m = p.matcher(site);
@@ -180,10 +184,20 @@ public class YoutubeExtractor implements Extractor {
videoInfo.duration = playerArgs.getInt("length_seconds");
videoInfo.average_rating = playerArgs.getString("avg_rating");
// View Count will be extracted from html
- //videoInfo.view_count = playerArgs.getString("view_count");
+ dashManifest = playerArgs.getString("dashmpd");
+ String playerUrl = ytAssets.getString("js");
+ if(playerUrl.startsWith("//")) {
+ playerUrl = "https:" + playerUrl;
+ }
+ if(decryptoinCode.isEmpty()) {
+ decryptoinCode = loadDecryptioinCode(playerUrl);
+ }
+
+ // extract audio
+ videoInfo.audioStreams = parseDashManifest(dashManifest, decryptoinCode);
//------------------------------------
- // extract stream url
+ // extract video stream url
//------------------------------------
String encoded_url_map = playerArgs.getString("url_encoded_fmt_stream_map");
Vector videoStreams = new Vector<>();
@@ -199,17 +213,13 @@ public class YoutubeExtractor implements Extractor {
// if video has a signature: decrypt it and add it to the url
if(tags.get("s") != null) {
- String playerUrl = ytAssets.getString("js");
- if(playerUrl.startsWith("//")) {
- playerUrl = "https:" + playerUrl;
- }
if(decryptoinCode.isEmpty()) {
decryptoinCode = loadDecryptioinCode(playerUrl);
}
- streamUrl = streamUrl + "&signature=" + decriptSignature(tags.get("s"), decryptoinCode);
+ streamUrl = streamUrl + "&signature=" + decryptSignature(tags.get("s"), decryptoinCode);
}
- if(resolveFormat(itag) != null) {
+ if(resolveFormat(itag) != -1) {
videoStreams.add(new VideoInfo.VideoStream(
streamUrl,
resolveFormat(itag),
@@ -309,6 +319,81 @@ public class YoutubeExtractor implements Extractor {
return videoInfo;
}
+ private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
+ if(!dashManifest.contains("/signature/")) {
+ String encryptedSig = "";
+ String decryptedSig = "";
+ try {
+ Pattern p = Pattern.compile("/s/([a-fA-F0-9\\.]+)");
+ Matcher m = p.matcher(dashManifest);
+ m.find();
+ encryptedSig = m.group(1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ decryptedSig = decryptSignature(encryptedSig, decryptoinCode);
+ dashManifest = dashManifest.replace("/s/" + encryptedSig, "/signature/" + decryptedSig);
+ }
+ String dashDoc = Downloader.download(dashManifest);
+ Vector audioStreams = new Vector<>();
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new StringReader(dashDoc));
+ int eventType = parser.getEventType();
+ String tagName = "";
+ String currentMimeType = "";
+ int currentBandwidth = -1;
+ int currentSamplingRate = -1;
+ boolean currentTagIsBaseUrl = false;
+ while(eventType != XmlPullParser.END_DOCUMENT) {
+ switch(eventType) {
+ case XmlPullParser.START_TAG:
+ tagName = parser.getName();
+ if(tagName.equals("AdaptationSet")) {
+ currentMimeType = parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "mimeType");
+ } else if(tagName.equals("Representation") && currentMimeType.contains("audio")) {
+ currentBandwidth = Integer.parseInt(
+ parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "bandwidth"));
+ currentSamplingRate = Integer.parseInt(
+ parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "audioSamplingRate"));
+ } else if(tagName.equals("BaseURL")) {
+ currentTagIsBaseUrl = true;
+ }
+
+ break;
+ case XmlPullParser.TEXT:
+ 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;
+ }
+ audioStreams.add(new VideoInfo.AudioStream(parser.getText(),
+ format, currentBandwidth, currentSamplingRate));
+ }
+ case XmlPullParser.END_TAG:
+ if(tagName.equals("AdaptationSet")) {
+ currentMimeType = "";
+ } else if(tagName.equals("BaseURL")) {
+ currentTagIsBaseUrl = false;
+ }
+ break;
+ default:
+ }
+ eventType = parser.next();
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ VideoInfo.AudioStream[] output = new VideoInfo.AudioStream[audioStreams.size()];
+ for(int i = 0; i < output.length; i++) {
+ output[i] = audioStreams.get(i);
+ }
+ return output;
+ }
+
private VideoInfoItem extractVideoInfoItem(Element li) {
VideoInfoItem info = new VideoInfoItem();
info.webpage_url = li.select("a[class*=\"content-link\"]").first()
@@ -395,7 +480,7 @@ public class YoutubeExtractor implements Extractor {
return decryptionCode;
}
- private String decriptSignature(String encryptedSig, String decryptoinCode) {
+ private String decryptSignature(String encryptedSig, String decryptoinCode) {
Context context = Context.enter();
context.setOptimizationLevel(-1);
Object result = null;
diff --git a/app/src/main/res/drawable/ic_headset_black.png b/app/src/main/res/drawable/ic_headset_black.png
new file mode 100644
index 000000000..974457ee1
Binary files /dev/null and b/app/src/main/res/drawable/ic_headset_black.png differ
diff --git a/app/src/main/res/menu/videoitem_detail.xml b/app/src/main/res/menu/videoitem_detail.xml
index 2625a801e..dda6fb03c 100644
--- a/app/src/main/res/menu/videoitem_detail.xml
+++ b/app/src/main/res/menu/videoitem_detail.xml
@@ -3,14 +3,24 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+
+
+
+
@@ -23,8 +33,5 @@
app:showAsAction="never"
android:title="@string/settings"/>
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 0b05a450d..4bed65a4e 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -36,4 +36,13 @@
Zeige \"Mit Kodi abspielen\" Option
Zeigt eine Option an, über die man Videos mit dem Kodi Mediacenter abspielen kann.
Linkshänder freundliches Layout.
+ Audio
+ Bevorzugtes Audio Format
+ WebM - freies Format
+ m4a - bessere Qualität
+ Herunterladen
+
+ - Video
+ - Audio
+
diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml
index 0b104a97c..d130ef4f3 100644
--- a/app/src/main/res/values/settings_keys.xml
+++ b/app/src/main/res/values/settings_keys.xml
@@ -13,4 +13,14 @@
360p
show_play_with_kodi_preference
left_hand_layout
+ default_audio_format
+
+ - @string/webMAudioDescription
+ - @string/m4aAudioDescription
+
+
+ - webm
+ - m4a
+
+ m4a
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 89b944448..93276b23f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -36,4 +36,13 @@
Show \"Play with Kodi\" option
Displays an option to play a video via Kodi media center.
Left hand friendly layout.
+ Audio
+ Default audio format
+ WebM - free format
+ m4a - better quality
+ Download
+
+ - Video
+ - Audio
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/settings_screen.xml b/app/src/main/res/xml/settings_screen.xml
index 82b50ef38..fc707bb15 100644
--- a/app/src/main/res/xml/settings_screen.xml
+++ b/app/src/main/res/xml/settings_screen.xml
@@ -39,4 +39,11 @@
android:title="@string/leftHandLayoutTitle"
android:defaultValue="false" />
+
+
\ No newline at end of file