diff --git a/app/build.gradle b/app/build.gradle index 5662b2efc..2ce9fefff 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 4 + versionName "0.4.1" } 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..41594b1e8 100644 --- a/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/VideoItemDetailFragment.java @@ -12,6 +12,7 @@ import android.support.v4.app.Fragment; import android.support.v7.app.ActionBar; import android.text.Html; import android.text.method.LinkMovementMethod; +import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -205,7 +206,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: @@ -271,7 +272,7 @@ public class VideoItemDetailFragment extends Fragment { FloatingActionButton playVideoButton = (FloatingActionButton) getActivity().findViewById(R.id.playVideoButton); if(PreferenceManager.getDefaultSharedPreferences(getActivity()) - .getBoolean(getString(R.string.leftHandLayout), false)) { + .getBoolean(getString(R.string.leftHandLayout), false) && checkIfLandscape()) { RelativeLayout.LayoutParams oldLayout = (RelativeLayout.LayoutParams) playVideoButton.getLayoutParams(); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, @@ -289,4 +290,10 @@ public class VideoItemDetailFragment extends Fragment { } }); } + + public boolean checkIfLandscape() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + return displayMetrics.heightPixels < displayMetrics.widthPixels; + } } \ No newline at end of file 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/layout/fragment_videoitem_detail.xml b/app/src/main/res/layout/fragment_videoitem_detail.xml index f39e545c7..a1d6c612e 100644 --- a/app/src/main/res/layout/fragment_videoitem_detail.xml +++ b/app/src/main/res/layout/fragment_videoitem_detail.xml @@ -163,8 +163,8 @@ + + + + @@ -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