mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	added autio streaming & jumped to version 0.4.0
This commit is contained in:
		
							
								
								
									
										2
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @@ -37,7 +37,7 @@ | ||||
|     <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"> | ||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" 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"> | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										89
									
								
								app/src/main/java/org/schabi/newpipe/DownloadDialog.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/src/main/java/org/schabi/newpipe/DownloadDialog.java
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <chris.schabesberger@mailbox.org> | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| 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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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 = ""; | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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<VideoInfo.VideoStream> 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<VideoInfo.AudioStream> 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; | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_headset_black.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_headset_black.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 786 B | 
| @@ -3,14 +3,24 @@ | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
|     <item android:id="@+id/menu_item_play" | ||||
|         android:title="@string/play" | ||||
|         app:showAsAction="always" | ||||
|         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" | ||||
|         android:icon="@drawable/ic_headset_black" /> | ||||
|  | ||||
|     <item android:id="@+id/menu_item_share" | ||||
|         android:title="@string/share" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:icon="@drawable/ic_share_black"/> | ||||
|  | ||||
|     <item android:id="@+id/action_play_with_kodi" | ||||
|         android:title="@string/playWithKodiTitle" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:icon="@drawable/ic_cast_black"/> | ||||
|  | ||||
|     <item android:id="@+id/menu_item_openInBrowser" | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/open_in_browser" /> | ||||
| @@ -23,8 +33,5 @@ | ||||
|         app:showAsAction="never" | ||||
|         android:title="@string/settings"/> | ||||
|  | ||||
|     <item android:id="@+id/action_play_with_kodi" | ||||
|         android:title="@string/playWithKodiTitle" | ||||
|         app:showAsAction="ifRoom" | ||||
|         android:icon="@drawable/ic_cast_black"/> | ||||
|  | ||||
| </menu> | ||||
| @@ -36,4 +36,13 @@ | ||||
|     <string name="showPlayWithKodiTitle">Zeige \"Mit Kodi abspielen\" Option</string> | ||||
|     <string name="showPlayWithKodiSummary">Zeigt eine Option an, über die man Videos mit dem Kodi Mediacenter abspielen kann.</string> | ||||
|     <string name="leftHandLayoutTitle">Linkshänder freundliches Layout.</string> | ||||
|     <string name="playAudio">Audio</string> | ||||
|     <string name="defaultAudioFormatTitle">Bevorzugtes Audio Format</string> | ||||
|     <string name="webMAudioDescription">WebM - freies Format</string> | ||||
|     <string name="m4aAudioDescription">m4a - bessere Qualität</string> | ||||
|     <string name="downloadDialogTitle">Herunterladen</string> | ||||
|     <string-array name="downloadOptions"> | ||||
|         <item>Video</item> | ||||
|         <item>Audio</item> | ||||
|     </string-array> | ||||
| </resources> | ||||
|   | ||||
| @@ -13,4 +13,14 @@ | ||||
|     <string name="defaultResolutionListItem">360p</string> | ||||
|     <string name="showPlayWidthKodiPreference">show_play_with_kodi_preference</string> | ||||
|     <string name="leftHandLayout">left_hand_layout</string> | ||||
|     <string name="defaultAudioFormatPreference">default_audio_format</string> | ||||
|     <string-array name="audioFormatDescriptionList"> | ||||
|         <item>@string/webMAudioDescription</item> | ||||
|         <item>@string/m4aAudioDescription</item> | ||||
|     </string-array> | ||||
|     <string-array name="audioFormatList"> | ||||
|         <item>webm</item> | ||||
|         <item>m4a</item> | ||||
|     </string-array> | ||||
|     <string name="defaultAudioFormat">m4a</string> | ||||
| </resources> | ||||
| @@ -36,4 +36,13 @@ | ||||
|     <string name="showPlayWithKodiTitle">Show \"Play with Kodi\" option</string> | ||||
|     <string name="showPlayWithKodiSummary">Displays an option to play a video via Kodi media center.</string> | ||||
|     <string name="leftHandLayoutTitle">Left hand friendly layout.</string> | ||||
|     <string name="playAudio">Audio</string> | ||||
|     <string name="defaultAudioFormatTitle">Default audio format</string> | ||||
|     <string name="webMAudioDescription">WebM - free format</string> | ||||
|     <string name="m4aAudioDescription">m4a - better quality</string> | ||||
|     <string name="downloadDialogTitle">Download</string> | ||||
|     <string-array name="downloadOptions"> | ||||
|         <item>Video</item> | ||||
|         <item>Audio</item> | ||||
|     </string-array> | ||||
| </resources> | ||||
| @@ -39,4 +39,11 @@ | ||||
|         android:title="@string/leftHandLayoutTitle" | ||||
|         android:defaultValue="false" /> | ||||
|  | ||||
|     <ListPreference | ||||
|         android:key="@string/defaultAudioFormatPreference" | ||||
|         android:title="@string/defaultAudioFormatTitle" | ||||
|         android:entries="@array/audioFormatDescriptionList" | ||||
|         android:entryValues="@array/audioFormatList" | ||||
|         android:defaultValue="@string/defaultAudioFormat"/> | ||||
|  | ||||
| </PreferenceScreen> | ||||
		Reference in New Issue
	
	Block a user
	 Christian Schabesberger
					Christian Schabesberger